@swype-org/react-sdk 0.2.316 → 0.2.323

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1418,6 +1418,53 @@ var DEFAULT_POLL_INTERVAL_MS = 1e3;
1418
1418
  var DEFAULT_COMMITMENT = "confirmed";
1419
1419
  var cachedAdapter = null;
1420
1420
  var cachedProviderKey = null;
1421
+ var solanaAccountChangeListeners = /* @__PURE__ */ new Set();
1422
+ var accountChangeBinding = null;
1423
+ function subscribeSolanaAccountChange(listener) {
1424
+ solanaAccountChangeListeners.add(listener);
1425
+ return () => {
1426
+ solanaAccountChangeListeners.delete(listener);
1427
+ };
1428
+ }
1429
+ function notifySolanaAccountChange(pubkey) {
1430
+ for (const listener of solanaAccountChangeListeners) {
1431
+ listener(pubkey);
1432
+ }
1433
+ }
1434
+ function bindAccountChangeListeners(adapter) {
1435
+ if (accountChangeBinding?.adapter === adapter) {
1436
+ return;
1437
+ }
1438
+ detachAccountChangeListeners();
1439
+ const binding = {
1440
+ adapter,
1441
+ lastNotifiedPubkey: adapter.publicKey?.toBase58() ?? null,
1442
+ onConnect: (publicKey) => {
1443
+ const next = publicKey?.toBase58();
1444
+ if (!next || next === binding.lastNotifiedPubkey) {
1445
+ return;
1446
+ }
1447
+ binding.lastNotifiedPubkey = next;
1448
+ console.info("[blink-sdk][solana-account-switch] adapter reported new account");
1449
+ notifySolanaAccountChange(next);
1450
+ },
1451
+ onDisconnect: () => {
1452
+ binding.lastNotifiedPubkey = null;
1453
+ }
1454
+ };
1455
+ adapter.on("connect", binding.onConnect);
1456
+ adapter.on("disconnect", binding.onDisconnect);
1457
+ accountChangeBinding = binding;
1458
+ }
1459
+ function detachAccountChangeListeners() {
1460
+ if (!accountChangeBinding) {
1461
+ return;
1462
+ }
1463
+ const { adapter, onConnect, onDisconnect } = accountChangeBinding;
1464
+ adapter.off("connect", onConnect);
1465
+ adapter.off("disconnect", onDisconnect);
1466
+ accountChangeBinding = null;
1467
+ }
1421
1468
  function providerKey(selection) {
1422
1469
  const providerName = selection.providerName?.trim();
1423
1470
  if (!providerName) {
@@ -1469,6 +1516,7 @@ async function connectSolanaWallet(selection, options) {
1469
1516
  await connectPromise;
1470
1517
  }
1471
1518
  }
1519
+ bindAccountChangeListeners(cachedAdapter);
1472
1520
  return {
1473
1521
  adapter: cachedAdapter,
1474
1522
  publicKey: readPublicKey(cachedAdapter)
@@ -1627,6 +1675,7 @@ async function disconnectSolanaWallet(windowImpl) {
1627
1675
  const adapter = cachedAdapter;
1628
1676
  cachedAdapter = null;
1629
1677
  cachedProviderKey = null;
1678
+ detachAccountChangeListeners();
1630
1679
  if (adapter?.connected) {
1631
1680
  try {
1632
1681
  console.info("[blink-sdk][solana-disconnect] disconnecting cached adapter");
@@ -2088,6 +2137,7 @@ __export(api_exports, {
2088
2137
  postTransferQuote: () => postTransferQuote,
2089
2138
  probeActionCompletion: () => probeActionCompletion,
2090
2139
  refreshManualTransferQuote: () => refreshManualTransferQuote,
2140
+ regenerateTransferSignPayload: () => regenerateTransferSignPayload,
2091
2141
  registerPasskey: () => registerPasskey,
2092
2142
  reportActionCompletion: () => reportActionCompletion,
2093
2143
  reportPasskeyActivity: () => reportPasskeyActivity,
@@ -2185,13 +2235,33 @@ async function fetchWithRetry(input, init, options) {
2185
2235
  throw lastError;
2186
2236
  }
2187
2237
 
2238
+ // src/apiError.ts
2239
+ var ApiError = class extends Error {
2240
+ status;
2241
+ code;
2242
+ constructor(status, code, message) {
2243
+ super(message);
2244
+ this.name = "ApiError";
2245
+ this.status = status;
2246
+ this.code = code;
2247
+ }
2248
+ };
2249
+ function isApiError(err) {
2250
+ if (err instanceof ApiError) return true;
2251
+ return typeof err === "object" && err !== null && "name" in err && err.name === "ApiError";
2252
+ }
2253
+ var SVM_SIGN_PAYLOAD_EXPIRED_CODE = "SVM_SIGN_PAYLOAD_EXPIRED";
2254
+ function isSvmSignExpiredError(err) {
2255
+ return isApiError(err) && err.code === SVM_SIGN_PAYLOAD_EXPIRED_CODE;
2256
+ }
2257
+
2188
2258
  // src/api.ts
2189
2259
  async function throwApiError(res) {
2190
2260
  const body = await res.json().catch(() => null);
2191
2261
  const detail = body?.error ?? body;
2192
2262
  const msg = detail?.message ?? res.statusText;
2193
2263
  const code = detail?.code ?? String(res.status);
2194
- throw new Error(`${res.status} \u2014 ${code}: ${msg}`);
2264
+ throw new ApiError(res.status, code, `${res.status} \u2014 ${code}: ${msg}`);
2195
2265
  }
2196
2266
  async function fetchProviders(apiBaseUrl, token) {
2197
2267
  const headers = {};
@@ -2380,6 +2450,21 @@ async function signTransfer(apiBaseUrl, token, transferId, signedTransfer, autho
2380
2450
  if (!res.ok) await throwApiError(res);
2381
2451
  return await res.json();
2382
2452
  }
2453
+ async function regenerateTransferSignPayload(apiBaseUrl, token, transferId, authorizationSessionToken) {
2454
+ if (!token && !authorizationSessionToken) {
2455
+ throw new Error("Missing auth credentials for sign payload regeneration.");
2456
+ }
2457
+ const res = await fetch(`${apiBaseUrl}/v1/transfers/${transferId}/sign-payload`, {
2458
+ method: "POST",
2459
+ headers: {
2460
+ "Content-Type": "application/json",
2461
+ ...token ? { Authorization: `Bearer ${token}` } : {},
2462
+ ...authorizationSessionToken ? { "x-authorization-session-token": authorizationSessionToken } : {}
2463
+ }
2464
+ });
2465
+ if (!res.ok) await throwApiError(res);
2466
+ return await res.json();
2467
+ }
2383
2468
  async function fetchAuthorizationSession(apiBaseUrl, sessionId) {
2384
2469
  const res = await fetchWithRetry(
2385
2470
  `${apiBaseUrl}/v1/authorization-sessions/${sessionId}`
@@ -6138,7 +6223,28 @@ function useTransferSigning(pollIntervalMs = 2e3, options) {
6138
6223
  },
6139
6224
  [apiBaseUrl, getAccessToken, pollIntervalMs, authorizationSessionToken]
6140
6225
  );
6141
- return { signing, signPayload, error, passkeyDismissed, signTransfer: signTransfer2 };
6226
+ const regenerateSignPayload = useCallback(
6227
+ async (transferId) => {
6228
+ if (!apiBaseUrl) {
6229
+ throw new Error("Missing apiBaseUrl. Provide useTransferSigning(_, { apiBaseUrl }) or wrap in <BlinkProvider>.");
6230
+ }
6231
+ if (!getAccessToken && !authorizationSessionToken) {
6232
+ throw new Error("Missing getAccessToken provider. Provide useTransferSigning(_, { getAccessToken }).");
6233
+ }
6234
+ const token = getAccessToken ? await getAccessToken() : null;
6235
+ if (!token && !authorizationSessionToken) {
6236
+ throw new Error("Could not get access token");
6237
+ }
6238
+ return regenerateTransferSignPayload(
6239
+ apiBaseUrl,
6240
+ token ?? "",
6241
+ transferId,
6242
+ authorizationSessionToken
6243
+ );
6244
+ },
6245
+ [apiBaseUrl, getAccessToken, authorizationSessionToken]
6246
+ );
6247
+ return { signing, signPayload, error, passkeyDismissed, signTransfer: signTransfer2, regenerateSignPayload };
6142
6248
  }
6143
6249
 
6144
6250
  // src/authorizationOrchestratorOrdering.ts
@@ -7514,6 +7620,9 @@ function resolveStickyPhase(state) {
7514
7620
  accountId: null
7515
7621
  };
7516
7622
  }
7623
+ if (currentPhase.step === "welcome-back" && state.enableFullWidget && !state.welcomeBackAcknowledged && state.privyAuthenticated && state.activeCredentialId != null && !hasActiveWallet(state.accounts) && !state.mobileFlow && !state.loginRequested) {
7624
+ return currentPhase;
7625
+ }
7517
7626
  if (currentPhase.step === "wallet-picker" && currentPhase.reason === "switch" && !state.creatingTransfer && !(state.mobileFlow && state.deeplinkUri)) {
7518
7627
  return currentPhase;
7519
7628
  }
@@ -7538,6 +7647,9 @@ function deriveFreshPhase(state) {
7538
7647
  if (state.loadingData && state.activeCredentialId != null) {
7539
7648
  return { step: "data-loading" };
7540
7649
  }
7650
+ if (state.activeCredentialId != null && !state.initialDataLoaded && state.error == null) {
7651
+ return { step: "data-loading" };
7652
+ }
7541
7653
  if (state.enableFullWidget && state.authenticatedOnOpen && !state.welcomeBackAcknowledged && state.activeCredentialId != null && !hasActiveWallet(state.accounts) && !state.mobileFlow) {
7542
7654
  return { step: "welcome-back" };
7543
7655
  }
@@ -7597,6 +7709,7 @@ function createInitialState(config) {
7597
7709
  amount: config.depositAmount != null ? config.depositAmount.toString() : "",
7598
7710
  transfer: null,
7599
7711
  creatingTransfer: false,
7712
+ signPayloadExpired: false,
7600
7713
  passkeyConfigLoaded: false,
7601
7714
  activeCredentialId: config.activeCredentialId,
7602
7715
  knownCredentialIds: [],
@@ -7622,7 +7735,8 @@ function createInitialState(config) {
7622
7735
  enableFullWidget: config.enableFullWidget ?? false,
7623
7736
  requireAmountEntry: config.depositAmount == null,
7624
7737
  authenticatedOnOpen: false,
7625
- welcomeBackAcknowledged: false
7738
+ welcomeBackAcknowledged: false,
7739
+ initialDataLoaded: false
7626
7740
  };
7627
7741
  }
7628
7742
  function clearAuthenticatedSessionState(state) {
@@ -7642,6 +7756,7 @@ function clearAuthenticatedSessionState(state) {
7642
7756
  savedSelection: null,
7643
7757
  transfer: null,
7644
7758
  creatingTransfer: false,
7759
+ signPayloadExpired: false,
7645
7760
  passkeyConfigLoaded: false,
7646
7761
  activeCredentialId: null,
7647
7762
  knownCredentialIds: [],
@@ -7662,7 +7777,8 @@ function clearAuthenticatedSessionState(state) {
7662
7777
  guestWalletDeeplinksPreparing: false,
7663
7778
  amountTooLow: null,
7664
7779
  authenticatedOnOpen: false,
7665
- welcomeBackAcknowledged: false
7780
+ welcomeBackAcknowledged: false,
7781
+ initialDataLoaded: false
7666
7782
  };
7667
7783
  }
7668
7784
  function paymentReducer(state, action) {
@@ -7725,7 +7841,8 @@ function applyAction(state, action) {
7725
7841
  providers: action.providers,
7726
7842
  accounts: action.accounts,
7727
7843
  chains: action.chains,
7728
- depositSelectionRefreshing: false
7844
+ depositSelectionRefreshing: false,
7845
+ initialDataLoaded: true
7729
7846
  };
7730
7847
  if (action.defaults) {
7731
7848
  next.selectedAccountId = action.defaults.accountId;
@@ -7750,7 +7867,8 @@ function applyAction(state, action) {
7750
7867
  const next = {
7751
7868
  ...state,
7752
7869
  accounts: action.accounts,
7753
- providers: action.providers
7870
+ providers: action.providers,
7871
+ initialDataLoaded: true
7754
7872
  };
7755
7873
  if (action.defaults) {
7756
7874
  next.selectedAccountId = action.defaults.accountId;
@@ -7825,6 +7943,7 @@ function applyAction(state, action) {
7825
7943
  return {
7826
7944
  ...state,
7827
7945
  error: null,
7946
+ signPayloadExpired: false,
7828
7947
  creatingTransfer: true,
7829
7948
  depositSelectionRefreshing: false,
7830
7949
  deeplinkUri: null,
@@ -7841,14 +7960,27 @@ function applyAction(state, action) {
7841
7960
  error: action.error,
7842
7961
  creatingTransfer: false
7843
7962
  };
7963
+ case "SIGN_PAYLOAD_EXPIRED":
7964
+ return {
7965
+ ...state,
7966
+ error: action.error,
7967
+ signPayloadExpired: true,
7968
+ creatingTransfer: false
7969
+ };
7844
7970
  case "TRANSFER_CREATED":
7845
7971
  return { ...state, transfer: action.transfer };
7846
7972
  case "TRANSFER_SIGNED":
7847
- return { ...state, transfer: action.transfer, setupAuthorizationSessionId: null };
7973
+ return {
7974
+ ...state,
7975
+ transfer: action.transfer,
7976
+ signPayloadExpired: false,
7977
+ setupAuthorizationSessionId: null
7978
+ };
7848
7979
  case "TRANSFER_COMPLETED":
7849
7980
  return {
7850
7981
  ...state,
7851
7982
  transfer: action.transfer,
7983
+ signPayloadExpired: false,
7852
7984
  mobileFlow: false,
7853
7985
  deeplinkUri: null,
7854
7986
  setupAuthorizationSessionId: null
@@ -7993,6 +8125,11 @@ function applyAction(state, action) {
7993
8125
  return { ...state, loginRequested: false, error: null };
7994
8126
  case "ACKNOWLEDGE_WELCOME_BACK":
7995
8127
  return { ...state, welcomeBackAcknowledged: true };
8128
+ // Back from WalletPickerScreen. Pin the welcome screen (preserved by the
8129
+ // welcome-back sticky branch in resolvePhase) and clear the acknowledged
8130
+ // flag so the sticky gate treats the user as un-acknowledged again.
8131
+ case "RETURN_TO_WELCOME_BACK":
8132
+ return { ...state, welcomeBackAcknowledged: false, phase: { step: "welcome-back" } };
7996
8133
  case "SET_ERROR":
7997
8134
  return { ...state, error: action.error };
7998
8135
  case "AMOUNT_TOO_LOW":
@@ -13127,14 +13264,34 @@ function SelectDepositSourceScreen({
13127
13264
  const requiresAuth = selectableOptions.filter(
13128
13265
  (opt) => opt.status && opt.status !== "AUTHORIZED"
13129
13266
  );
13130
- const orderedAccounts = (() => {
13131
- if (!selectedAccountId) return accounts;
13132
- const selected = accounts.find((a) => a.id === selectedAccountId);
13133
- if (!selected) return accounts;
13134
- return [selected, ...accounts.filter((a) => a.id !== selectedAccountId)];
13135
- })();
13267
+ const sectionsPerAccount = /* @__PURE__ */ new Map();
13268
+ for (const section of accounts) {
13269
+ sectionsPerAccount.set(section.id, (sectionsPerAccount.get(section.id) ?? 0) + 1);
13270
+ }
13136
13271
  const fallbackAccountId = selectedAccountId ?? accounts[0]?.id ?? null;
13137
13272
  const rowAccountId = (opt) => opt.accountId ?? fallbackAccountId;
13273
+ const rowMatchesSection = (opt, section) => {
13274
+ if (rowAccountId(opt) !== section.id) return false;
13275
+ if (opt.walletAddress != null) return opt.walletAddress === section.address;
13276
+ return (sectionsPerAccount.get(section.id) ?? 0) <= 1;
13277
+ };
13278
+ const orderedAccounts = (() => {
13279
+ const selectedOption = selectedWalletId ? tokenOptions.find((opt) => opt.walletId === selectedWalletId) : void 0;
13280
+ const selectedAddress = selectedOption?.walletAddress ?? null;
13281
+ const selectedSectionId = selectedOption?.accountId ?? selectedAccountId ?? null;
13282
+ if (!selectedSectionId) return accounts;
13283
+ const matchesSelected = (section) => {
13284
+ if (section.id !== selectedSectionId) return false;
13285
+ if (selectedAddress != null) return section.address === selectedAddress;
13286
+ return true;
13287
+ };
13288
+ const selectedIndex = accounts.findIndex(matchesSelected);
13289
+ if (selectedIndex < 0) return accounts;
13290
+ return [
13291
+ accounts[selectedIndex],
13292
+ ...accounts.filter((_, index) => index !== selectedIndex)
13293
+ ];
13294
+ })();
13138
13295
  const isSelected = (opt) => !!selectedTokenSymbol && !!selectedChainName && !!selectedWalletId && opt.symbol === selectedTokenSymbol && opt.chainName === selectedChainName && opt.walletId === selectedWalletId;
13139
13296
  const tokenOptionKey = (opt) => `${opt.accountId ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`;
13140
13297
  const hasPendingMobileSelection = pendingMobileSelection != null;
@@ -13219,8 +13376,8 @@ function SelectDepositSourceScreen({
13219
13376
  /* @__PURE__ */ jsx(ScreenHeader, { title: "Select source", onBack: onBack ?? onDone, onLogout }),
13220
13377
  /* @__PURE__ */ jsxs("div", { "data-testid": "select-deposit-source-scroll-content", style: contentStyle12, children: [
13221
13378
  orderedAccounts.map((account) => {
13222
- const authRows = authorized.filter((opt) => rowAccountId(opt) === account.id);
13223
- const reqRows = requiresAuth.filter((opt) => rowAccountId(opt) === account.id);
13379
+ const authRows = authorized.filter((opt) => rowMatchesSection(opt, account));
13380
+ const reqRows = requiresAuth.filter((opt) => rowMatchesSection(opt, account));
13224
13381
  if (authRows.length === 0 && reqRows.length === 0) return null;
13225
13382
  return /* @__PURE__ */ jsxs("div", { style: accountSectionStyle, children: [
13226
13383
  renderAccountHeader(account),
@@ -13235,7 +13392,7 @@ function SelectDepositSourceScreen({
13235
13392
  selected: !hasPendingMobileSelection && isSelected(opt),
13236
13393
  onClick: () => handleAuthorizedPick(opt)
13237
13394
  },
13238
- `auth-${opt.accountId ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`
13395
+ `auth-${opt.accountId ?? ""}-${opt.walletAddress ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`
13239
13396
  )),
13240
13397
  reqRows.map((opt) => /* @__PURE__ */ jsx(
13241
13398
  TokenSourceRow,
@@ -13248,12 +13405,12 @@ function SelectDepositSourceScreen({
13248
13405
  selected: hasPendingMobileSelection ? pendingMobileSelection?.key === tokenOptionKey(opt) : isSelected(opt),
13249
13406
  onClick: () => handleRequiresAuthPick(account, opt)
13250
13407
  },
13251
- `req-${opt.accountId ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`
13408
+ `req-${opt.accountId ?? ""}-${opt.walletAddress ?? ""}-${opt.chainName}-${opt.symbol}-${opt.walletId ?? ""}`
13252
13409
  ))
13253
13410
  ] })
13254
- ] }, `account-${account.id}`);
13411
+ ] }, `account-${account.id}-${account.address ?? ""}`);
13255
13412
  }),
13256
- (onSendManually || onAddProvider) && /* @__PURE__ */ jsxs("div", { style: groupCardStyle(tokens.bgRecessed), children: [
13413
+ (onSendManually || onAddProvider) && /* @__PURE__ */ jsxs("div", { style: { ...groupCardStyle(tokens.bgRecessed), marginTop: 8 }, children: [
13257
13414
  onSendManually && /* @__PURE__ */ jsx(
13258
13415
  ActionRow,
13259
13416
  {
@@ -13537,14 +13694,16 @@ function DepositScreen({
13537
13694
  const formattedBalance = selectedTokenSymbol != null ? `$${formatUsdTwoDecimals2(availableBalance)}` : void 0;
13538
13695
  const tokenAriaLabel = selectedTokenSymbol && selectedChainName ? `Selected token: ${selectedTokenSymbol} on ${selectedChainName}${formattedBalance ? `, ${formattedBalance}` : ""}` : selectedTokenSymbol ? `Selected token: ${selectedTokenSymbol}${formattedBalance ? `, ${formattedBalance}` : ""}` : "Select token";
13539
13696
  if (tokenPickerOpen && canOpenInlineSheet) {
13540
- const depositSourceAccounts = (accounts ?? []).map((a) => {
13541
- const preferred = getPreferredDepositWallet(a, amount);
13542
- return {
13543
- id: a.id,
13544
- name: a.name,
13545
- logoURI: a.logoURI,
13546
- address: preferred ? getWalletAddress(preferred) : null
13547
- };
13697
+ const depositSourceAccounts = (accounts ?? []).flatMap((a) => {
13698
+ const seen = /* @__PURE__ */ new Set();
13699
+ const sections = [];
13700
+ for (const wallet of getAddressableWallets(a)) {
13701
+ const address = getWalletAddress(wallet);
13702
+ if (!address || seen.has(address)) continue;
13703
+ seen.add(address);
13704
+ sections.push({ id: a.id, name: a.name, logoURI: a.logoURI, address });
13705
+ }
13706
+ return sections;
13548
13707
  });
13549
13708
  return /* @__PURE__ */ jsx(
13550
13709
  SelectDepositSourceScreen,
@@ -13608,7 +13767,6 @@ function DepositScreen({
13608
13767
  }
13609
13768
  };
13610
13769
  const presetsInFooter = mobileEntryWithKeypad;
13611
- const showHeroPresets = isEntryMode && isDesktop;
13612
13770
  const showFooterPresets = presetsInFooter;
13613
13771
  const presetsRow = onPreset ? /* @__PURE__ */ jsx("div", { style: entryPresetsRowStyle, role: "group", "aria-label": "Preset amounts", children: presets.map((preset) => /* @__PURE__ */ jsx(
13614
13772
  "button",
@@ -13736,8 +13894,7 @@ function DepositScreen({
13736
13894
  }
13737
13895
  )
13738
13896
  }
13739
- ) : /* @__PURE__ */ jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" }) }),
13740
- showHeroPresets && presetsRow
13897
+ ) : /* @__PURE__ */ jsx("span", { style: redesignNoFeesStyle(tokens.text), children: "No fees" }) })
13741
13898
  ] }),
13742
13899
  /* @__PURE__ */ jsx("div", { style: bannerSlotStyle2, children: mobileEntryWithKeypad ? null : showLowFunds ? /* @__PURE__ */ jsx(
13743
13900
  NotificationBanner,
@@ -14084,7 +14241,7 @@ function SuccessScreen({
14084
14241
  return /* @__PURE__ */ jsxs(
14085
14242
  ScreenLayout,
14086
14243
  {
14087
- footer: /* @__PURE__ */ jsx(Fragment, { children: onDone ? /* @__PURE__ */ jsx(PrimaryButton, { onClick: onDone, children: succeeded ? "Done" : "Try again" }) : returnMessage ? /* @__PURE__ */ jsx("p", { style: returnMessageStyle(tokens.textSecondary), children: returnMessage }) : null }),
14244
+ footer: /* @__PURE__ */ jsx(Fragment, { children: onDone ? /* @__PURE__ */ jsx(PrimaryButton, { onClick: onDone, children: succeeded ? "Close" : "Try again" }) : returnMessage ? /* @__PURE__ */ jsx("p", { style: returnMessageStyle(tokens.textSecondary), children: returnMessage }) : null }),
14088
14245
  children: [
14089
14246
  /* @__PURE__ */ jsx(
14090
14247
  ScreenHeader,
@@ -14103,7 +14260,7 @@ function SuccessScreen({
14103
14260
  style: illustrationStyle5
14104
14261
  }
14105
14262
  ),
14106
- /* @__PURE__ */ jsx("h2", { style: headingStyle8(tokens.text), children: "You\u2019re all set" })
14263
+ /* @__PURE__ */ jsx("h2", { style: headingStyle8(tokens.text), children: "Deposit complete" })
14107
14264
  ] }) : /* @__PURE__ */ jsxs("div", { style: screenContentStyle, children: [
14108
14265
  /* @__PURE__ */ jsx(IconCircle, { variant: "error", size: 64, children: /* @__PURE__ */ jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z", fill: tokens.error }) }) }),
14109
14266
  /* @__PURE__ */ jsx("h2", { style: failureHeadingStyle(tokens.text), children: "Transfer failed" }),
@@ -14639,6 +14796,7 @@ function TransferStatusLayout({
14639
14796
  retryLabel = "Retry wallet prompt",
14640
14797
  retryAfterDelay = true,
14641
14798
  showRetryOnError = false,
14799
+ errorVariant = "error",
14642
14800
  heading,
14643
14801
  visibleError = error,
14644
14802
  children
@@ -14659,7 +14817,7 @@ function TransferStatusLayout({
14659
14817
  onLogout && /* @__PURE__ */ jsx("div", { style: menuOverlayStyle, children: /* @__PURE__ */ jsx(SettingsMenu, { onLogout }) }),
14660
14818
  /* @__PURE__ */ jsxs("div", { style: contentStyle13, children: [
14661
14819
  /* @__PURE__ */ jsx("h2", { style: headingStyle10(tokens.text), children: heading }),
14662
- visibleError && /* @__PURE__ */ jsx("div", { style: errorBannerStyle7(tokens), children: visibleError }),
14820
+ visibleError && (errorVariant === "info" ? /* @__PURE__ */ jsx(NotificationBanner, { title: "Signing window expired", description: visibleError }) : /* @__PURE__ */ jsx("div", { style: errorBannerStyle7(tokens), children: visibleError })),
14663
14821
  children
14664
14822
  ] })
14665
14823
  ] })
@@ -14768,6 +14926,7 @@ function DepositTransferStatusScreen({
14768
14926
  phase,
14769
14927
  error,
14770
14928
  visibleError = error,
14929
+ errorVariant,
14771
14930
  onLogout,
14772
14931
  onRetry
14773
14932
  }) {
@@ -14778,6 +14937,7 @@ function DepositTransferStatusScreen({
14778
14937
  phase,
14779
14938
  error,
14780
14939
  visibleError,
14940
+ errorVariant,
14781
14941
  onLogout,
14782
14942
  onRetry,
14783
14943
  retryLabel: "Retry",
@@ -16642,7 +16802,12 @@ function buildWalletPickerScreenProps({
16642
16802
  onPrepareProvider: handlers.onPrepareProvider,
16643
16803
  onSelectProvider: handlers.onSelectProvider,
16644
16804
  onSelectWalletConnectWallet: handlers.onSelectWalletConnectWallet,
16645
- onBack: !flow.authenticated ? flow.onBack : () => handlers.onSetPhase({ step: "deposit" }),
16805
+ // Back target depends on the user's state:
16806
+ // • guest → host close (flow.onBack)
16807
+ // • authenticated with a linked (ACTIVE) wallet → DepositScreen
16808
+ // • authenticated, no linked wallet, full-widget → WelcomeBackScreen
16809
+ // • authenticated, no linked wallet, non-full-widget → host close
16810
+ onBack: !flow.authenticated ? flow.onBack : hasActiveWallet(flow.state.accounts) ? () => handlers.onSetPhase({ step: "deposit" }) : flow.state.enableFullWidget ? handlers.onReturnToWelcomeBack : flow.onBack,
16646
16811
  onLogout: flow.authenticated ? handlers.onLogout : void 0,
16647
16812
  isDesktop: flow.isDesktop,
16648
16813
  directLinkCards: isMobile,
@@ -16845,6 +17010,7 @@ function buildDepositScreenProps({
16845
17010
  tokenAddress: source.address,
16846
17011
  chainId: chain?.commonId ?? void 0,
16847
17012
  accountId: account.id,
17013
+ walletAddress: getWalletAddress(wallet) ?? void 0,
16848
17014
  logoURI: source.token.logoURI
16849
17015
  });
16850
17016
  }
@@ -16962,14 +17128,18 @@ function buildProcessingScreenProps({
16962
17128
  }) {
16963
17129
  const bestKnownTransfer = remote.pollingTransfer ?? flow.state.transfer;
16964
17130
  const transferStatus = bestKnownTransfer?.status;
16965
- const canRetryDepositSigning = remote.transferSigningError != null && isTransferSignable(bestKnownTransfer);
16966
- const error = flow.state.error || remote.authExecutorError || remote.transferSigningError || remote.pollingError;
16967
- const visibleError = remote.transferSigningPasskeyDismissed === true && !flow.state.error && !remote.authExecutorError ? remote.pollingError : error;
17131
+ const signExpired = flow.state.signPayloadExpired === true;
17132
+ const canRetryDepositSigning = (remote.transferSigningError != null || signExpired) && isTransferSignable(bestKnownTransfer);
17133
+ const error = signExpired ? flow.state.error : flow.state.error || remote.authExecutorError || remote.transferSigningError || remote.pollingError;
17134
+ const visibleError = signExpired ? flow.state.error : remote.transferSigningPasskeyDismissed === true && !flow.state.error && !remote.authExecutorError ? remote.pollingError : error;
16968
17135
  const transferPhase = flow.state.creatingTransfer ? "creating" : transferStatus === "SENDING" || transferStatus === "SENT" || transferStatus === "COMPLETED" ? "sent" : transferStatus === "AUTHORIZED" ? "submitting" : "signing";
16969
17136
  return {
16970
17137
  phase: transferPhase,
16971
17138
  error,
16972
17139
  visibleError,
17140
+ // An expired SVM signing window is a benign, retryable condition — present it
17141
+ // as an informational banner rather than the alarming red error banner.
17142
+ errorVariant: signExpired ? "info" : "error",
16973
17143
  onRetry: canRetryDepositSigning ? handlers.onRetryTransferSigning : void 0,
16974
17144
  onLogout: handlers.onLogout
16975
17145
  };
@@ -17106,6 +17276,7 @@ function StepRendererContent({
17106
17276
  phase: processingProps.phase,
17107
17277
  error: processingProps.error,
17108
17278
  visibleError: processingProps.visibleError,
17279
+ errorVariant: processingProps.errorVariant,
17109
17280
  onRetry: processingProps.onRetry,
17110
17281
  onLogout: processingProps.onLogout
17111
17282
  }
@@ -17462,6 +17633,7 @@ function resolveStandardTransferSource({
17462
17633
  }
17463
17634
 
17464
17635
  // src/hooks/useTransferHandlers.ts
17636
+ var SIGN_PAYLOAD_EXPIRED_MESSAGE = "This transfer must be signed within 20 seconds. Tap Retry to try again.";
17465
17637
  function useTransferHandlers(deps) {
17466
17638
  const {
17467
17639
  dispatch,
@@ -17485,6 +17657,7 @@ function useTransferHandlers(deps) {
17485
17657
  selectedTokenSymbol,
17486
17658
  quoteId,
17487
17659
  transfer,
17660
+ signPayloadExpired,
17488
17661
  accounts,
17489
17662
  chains
17490
17663
  } = deps;
@@ -17615,6 +17788,10 @@ function useTransferHandlers(deps) {
17615
17788
  if (isTransferSigningPasskeyDismissedError(err)) {
17616
17789
  return;
17617
17790
  }
17791
+ if (isSvmSignExpiredError(err)) {
17792
+ dispatch({ type: "SIGN_PAYLOAD_EXPIRED", error: SIGN_PAYLOAD_EXPIRED_MESSAGE });
17793
+ return;
17794
+ }
17618
17795
  captureException(err);
17619
17796
  const msg = err instanceof Error ? err.message : "Transfer failed";
17620
17797
  dispatch({ type: "PAY_ERROR", error: msg });
@@ -17652,6 +17829,10 @@ function useTransferHandlers(deps) {
17652
17829
  dispatch({ type: "CONFIRM_SIGN_SUCCESS", transfer: signedTransfer });
17653
17830
  polling.startPolling(t.id);
17654
17831
  } catch (err) {
17832
+ if (isSvmSignExpiredError(err)) {
17833
+ dispatch({ type: "SIGN_PAYLOAD_EXPIRED", error: SIGN_PAYLOAD_EXPIRED_MESSAGE });
17834
+ return;
17835
+ }
17655
17836
  captureException(err);
17656
17837
  const msg = err instanceof Error ? err.message : "Failed to sign transfer";
17657
17838
  dispatch({ type: "SET_ERROR", error: msg });
@@ -17661,9 +17842,19 @@ function useTransferHandlers(deps) {
17661
17842
  const handleRetryTransferSigning = useCallback(async () => {
17662
17843
  const t = polling.transfer ?? transfer;
17663
17844
  if (!isTransferSignable(t)) return;
17845
+ const wasExpired = signPayloadExpired;
17664
17846
  try {
17665
17847
  dispatch({ type: "SET_ERROR", error: null });
17666
17848
  processingStartedAtRef.current = Date.now();
17849
+ if (wasExpired) {
17850
+ const refreshed = await transferSigning.regenerateSignPayload(t.id);
17851
+ const signedTransfer2 = await transferSigning.signTransfer(t.id, {
17852
+ knownSignPayload: refreshed.signPayload
17853
+ });
17854
+ dispatch({ type: "TRANSFER_SIGNED", transfer: signedTransfer2 });
17855
+ polling.startPolling(t.id);
17856
+ return;
17857
+ }
17667
17858
  const signedTransfer = await transferSigning.signTransfer(t.id);
17668
17859
  dispatch({ type: "TRANSFER_SIGNED", transfer: signedTransfer });
17669
17860
  polling.startPolling(t.id);
@@ -17671,12 +17862,16 @@ function useTransferHandlers(deps) {
17671
17862
  if (isTransferSigningPasskeyDismissedError(err)) {
17672
17863
  return;
17673
17864
  }
17865
+ if (isSvmSignExpiredError(err)) {
17866
+ dispatch({ type: "SIGN_PAYLOAD_EXPIRED", error: SIGN_PAYLOAD_EXPIRED_MESSAGE });
17867
+ return;
17868
+ }
17674
17869
  captureException(err);
17675
17870
  const msg = err instanceof Error ? err.message : "Failed to sign transfer";
17676
17871
  dispatch({ type: "PAY_ERROR", error: msg });
17677
17872
  onError?.(msg);
17678
17873
  }
17679
- }, [transfer, polling.transfer, polling.startPolling, transferSigning, onError, dispatch]);
17874
+ }, [transfer, polling.transfer, polling.startPolling, transferSigning, onError, dispatch, signPayloadExpired]);
17680
17875
  return {
17681
17876
  reloadAccounts,
17682
17877
  handlePay,
@@ -17830,6 +18025,9 @@ async function replaceOpenProviderForAccountSwitch(input) {
17830
18025
  if (input.chainId != null) {
17831
18026
  result.chainId = `0x${input.chainId.toString(16)}`;
17832
18027
  }
18028
+ if (input.userSolanaWalletPubkey) {
18029
+ result.userSolanaWalletPubkey = input.userSolanaWalletPubkey;
18030
+ }
17833
18031
  const updated = await reportActionCompletion(
17834
18032
  input.apiBaseUrl,
17835
18033
  openProvider.id,
@@ -18825,7 +19023,8 @@ function useProviderHandlers(deps) {
18825
19023
  apiBaseUrl,
18826
19024
  sessionId,
18827
19025
  accounts: change.accounts,
18828
- chainId: change.chainId
19026
+ chainId: change.chainId,
19027
+ ...change.chainId == null ? { userSolanaWalletPubkey: change.accounts[0] } : {}
18829
19028
  });
18830
19029
  if (outcome.status === "success") {
18831
19030
  dispatch({ type: "CLEAR_SETUP_DEPOSIT_TOKEN" });
@@ -19834,6 +20033,31 @@ function useWalletAccountSwitchEffect(deps) {
19834
20033
  });
19835
20034
  }, [enabled, isDesktop, address, chainId, connectorId, addresses, onAccountChanged]);
19836
20035
  }
20036
+ function useSolanaAccountSwitchEffect(deps) {
20037
+ const { enabled = true, onAccountChanged } = deps;
20038
+ const baselineRef = useRef(null);
20039
+ useEffect(() => {
20040
+ if (!enabled) {
20041
+ baselineRef.current = null;
20042
+ return;
20043
+ }
20044
+ const unsubscribe = subscribeSolanaAccountChange((pubkey) => {
20045
+ if (baselineRef.current === pubkey) {
20046
+ return;
20047
+ }
20048
+ const previous = baselineRef.current;
20049
+ baselineRef.current = pubkey;
20050
+ void onAccountChanged({
20051
+ previousAddress: previous,
20052
+ nextAddress: pubkey,
20053
+ connectorId: "phantom",
20054
+ chainId: null,
20055
+ accounts: [pubkey]
20056
+ });
20057
+ });
20058
+ return unsubscribe;
20059
+ }, [enabled, onAccountChanged]);
20060
+ }
19837
20061
  function usePersistAmountToActiveSession({
19838
20062
  apiBaseUrl,
19839
20063
  accounts,
@@ -20017,6 +20241,7 @@ function BlinkPaymentInner({
20017
20241
  selectedTokenSymbol: state.selectedTokenSymbol,
20018
20242
  quoteId: depositFee.quoteId,
20019
20243
  transfer: state.transfer,
20244
+ signPayloadExpired: state.signPayloadExpired,
20020
20245
  accounts: state.accounts,
20021
20246
  chains: state.chains
20022
20247
  });
@@ -20086,6 +20311,10 @@ function BlinkPaymentInner({
20086
20311
  isDesktop,
20087
20312
  onAccountChanged: onWalletAccountChanged
20088
20313
  });
20314
+ useSolanaAccountSwitchEffect({
20315
+ enabled: accountSwitchListenerArmed && isDesktop,
20316
+ onAccountChanged: onWalletAccountChanged
20317
+ });
20089
20318
  const clearLocalSessionArtifacts = useCallback(() => {
20090
20319
  popupAuthRef.current = null;
20091
20320
  clearPopupAuth();
@@ -20391,6 +20620,7 @@ function BlinkPaymentInner({
20391
20620
  onLogin: () => dispatch({ type: "REQUEST_LOGIN" }),
20392
20621
  onCancelLogin: () => dispatch({ type: "CANCEL_LOGIN_REQUEST" }),
20393
20622
  onAcknowledgeWelcomeBack: () => dispatch({ type: "ACKNOWLEDGE_WELCOME_BACK" }),
20623
+ onReturnToWelcomeBack: () => dispatch({ type: "RETURN_TO_WELCOME_BACK" }),
20394
20624
  onSetDepositToken: handleSetDepositToken,
20395
20625
  onConfirmSetupDeposit: handleConfirmSetupDeposit,
20396
20626
  onAmountInput: (value) => dispatch({ type: "SET_AMOUNT_INPUT", value }),
@@ -20483,6 +20713,6 @@ function BlinkPaymentInner({
20483
20713
  ) });
20484
20714
  }
20485
20715
 
20486
- export { ACCOUNT_SWITCH_CONFLICT_MESSAGE, AdvancedSourceScreen, ApprovingInWalletScreen, AuthorizationSessionCancelledError, BLINK_ERROR_ILLUSTRATION, BLINK_LOGO, BLINK_MASCOT, BLINK_PASSKEY_ILLUSTRATION, BLINK_SUCCESS_ILLUSTRATION, BlinkErrorScreen, BlinkInitialLoadingScreen, BlinkLoadingScreen, BlinkPayment, BlinkProvider, ConfirmSignScreen, DepositCompleteScreen, DepositOptionsScreen, DepositScreen, DepositTransferStatusScreen, GuestTokenPickerScreen, IconCircle, InfoBanner, LOGIN_KEY_ILLUSTRATION, LinkTokensScreen, LoginScreen, ManualTransferPasskeyScreen, OpenWalletScreen, OtpVerifyScreen, OutlineButton, PasskeyIframeBlockedError, PasskeyPopupWelcomeScreen, PasskeyScreen, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SecondaryButton, SelectDepositSourceScreen, SelectSourceScreen, SettingsMenu, Spinner, StepList, StepRenderer, SuccessScreen, TokenPickerScreen, VerifyPasskeyScreen, WalletPickerScreen, WelcomeBackScreen, appendDebug, api_exports as blinkApi, clearDebugEntries, createInitialState, credentialIdBase64ToBytes, darkTheme, darkThemeNew, darkTransparentTheme, darkTransparentThemeNew, deviceHasPasskey, encodePermit2ApproveCalldata, findDevicePasskey, findDevicePasskeyViaPopup, getAtomicBatchSupportDebugInfo, getDebugEntries, getDeviceBiometricUnlockText, getTheme, getThemeBase, getWalletCapabilities, isAuthorizationSessionCancelled, isExpectedAuthorizationCancellation, isTerminalTransferStatus, isTransferAwaitingCompletion, isTransparentTheme, isUserDismissedAuthorizationError, isVisibleUsdAmountAtTwoDecimals, lightTheme, lightThemeNew, lightTransparentTheme, lightTransparentThemeNew, mapGuestPickerEntries, replaceOpenProviderForAccountSwitch, resolvePasskeyRpId, screenForPhase, subscribeDebug, supportsAtomicBatch, supportsPaymasterService, useAuthorizationExecutor, useAuthorizationOrchestrator, useBlinkConfig, useBlinkDebugLog, useBlinkDepositAmount, useTransferPolling, useTransferSigning };
20716
+ export { ACCOUNT_SWITCH_CONFLICT_MESSAGE, AdvancedSourceScreen, ApprovingInWalletScreen, AuthorizationSessionCancelledError, BLINK_ERROR_ILLUSTRATION, BLINK_LOGO, BLINK_MASCOT, BLINK_PASSKEY_ILLUSTRATION, BLINK_SUCCESS_ILLUSTRATION, BlinkErrorScreen, BlinkInitialLoadingScreen, BlinkLoadingScreen, BlinkPayment, BlinkProvider, ConfirmSignScreen, DepositCompleteScreen, DepositOptionsScreen, DepositScreen, DepositTransferStatusScreen, GuestTokenPickerScreen, IconCircle, InfoBanner, LOGIN_KEY_ILLUSTRATION, LinkTokensScreen, LoginScreen, ManualTransferPasskeyScreen, OpenWalletScreen, OtpVerifyScreen, OutlineButton, PasskeyIframeBlockedError, PasskeyPopupWelcomeScreen, PasskeyScreen, PoweredByFooter, PrimaryButton, ScreenHeader, ScreenLayout, SecondaryButton, SelectDepositSourceScreen, SelectSourceScreen, SettingsMenu, Spinner, StepList, StepRenderer, SuccessScreen, TokenPickerScreen, VerifyPasskeyScreen, WalletPickerScreen, WelcomeBackScreen, appendDebug, api_exports as blinkApi, clearDebugEntries, createInitialState, credentialIdBase64ToBytes, darkTheme, darkThemeNew, darkTransparentTheme, darkTransparentThemeNew, deviceHasPasskey, encodePermit2ApproveCalldata, findDevicePasskey, findDevicePasskeyViaPopup, getAtomicBatchSupportDebugInfo, getDebugEntries, getDeviceBiometricUnlockText, getTheme, getThemeBase, getWalletCapabilities, isAuthorizationSessionCancelled, isExpectedAuthorizationCancellation, isTerminalTransferStatus, isTransferAwaitingCompletion, isTransparentTheme, isUserDismissedAuthorizationError, isVisibleUsdAmountAtTwoDecimals, lightTheme, lightThemeNew, lightTransparentTheme, lightTransparentThemeNew, mapGuestPickerEntries, replaceOpenProviderForAccountSwitch, resolvePasskeyRpId, screenForPhase, subscribeDebug, supportsAtomicBatch, supportsPaymasterService, useAuthorizationExecutor, useAuthorizationOrchestrator, useBlinkConfig, useBlinkDebugLog, useBlinkDepositAmount, useSolanaAccountSwitchEffect, useTransferPolling, useTransferSigning };
20487
20717
  //# sourceMappingURL=index.js.map
20488
20718
  //# sourceMappingURL=index.js.map