@swype-org/react-sdk 0.2.242 → 0.2.261

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
@@ -1154,12 +1154,225 @@ function useBlinkDepositAmount() {
1154
1154
  };
1155
1155
  }
1156
1156
 
1157
+ // src/walletConnectorResolver.ts
1158
+ function normalize(value) {
1159
+ return (value ?? "").trim().toLowerCase();
1160
+ }
1161
+ function buildTargetMatchers(target) {
1162
+ const values = [target.providerName, target.wagmiConnectorId].map(normalize).filter(Boolean);
1163
+ const aliases = new Set(values);
1164
+ for (const value of values) {
1165
+ if (value.includes("metamask")) {
1166
+ aliases.add("metamask");
1167
+ aliases.add("io.metamask");
1168
+ }
1169
+ if (value === "base" || value === "base account" || value === "base app" || value.includes("coinbase")) {
1170
+ aliases.add("base");
1171
+ aliases.add("coinbase");
1172
+ aliases.add("coinbasewalletsdk");
1173
+ }
1174
+ if (value.includes("trust")) {
1175
+ aliases.add("trust");
1176
+ aliases.add("trustwallet");
1177
+ }
1178
+ if (value.includes("okx")) {
1179
+ aliases.add("okx");
1180
+ aliases.add("okxwallet");
1181
+ aliases.add("com.okx.wallet");
1182
+ }
1183
+ if (value.includes("rabby")) {
1184
+ aliases.add("rabby");
1185
+ aliases.add("io.rabby");
1186
+ }
1187
+ if (value.includes("phantom")) {
1188
+ aliases.add("phantom");
1189
+ aliases.add("app.phantom");
1190
+ }
1191
+ if (value.includes("injected")) {
1192
+ aliases.add("injected");
1193
+ }
1194
+ }
1195
+ return [...aliases];
1196
+ }
1197
+ function connectorMatchesWallet(connector, target) {
1198
+ if (!connector) {
1199
+ return false;
1200
+ }
1201
+ const connectorId = normalize(connector.id);
1202
+ const connectorName = normalize(connector.name);
1203
+ if (target.wagmiConnectorId) {
1204
+ const targetConnectorId = normalize(target.wagmiConnectorId);
1205
+ if (connectorId === targetConnectorId) {
1206
+ return true;
1207
+ }
1208
+ }
1209
+ const matchers = buildTargetMatchers(target);
1210
+ if (matchers.length === 0) {
1211
+ return false;
1212
+ }
1213
+ return matchers.some(
1214
+ (matcher) => connectorId === matcher || connectorName === matcher || connectorId.includes(matcher) || connectorName.includes(matcher)
1215
+ );
1216
+ }
1217
+ function resolveWalletConnector(connectors, target) {
1218
+ if (target.wagmiConnectorId || target.providerName) {
1219
+ return connectors.find((connector) => connectorMatchesWallet(connector, target));
1220
+ }
1221
+ const metaMaskConnector = connectors.find((connector) => connectorMatchesWallet(
1222
+ connector,
1223
+ { wagmiConnectorId: "metamask" }
1224
+ ));
1225
+ return metaMaskConnector ?? connectors[0];
1226
+ }
1227
+
1228
+ // src/wagmiRevokeAndDisconnect.ts
1229
+ var REVOKE_METHOD = "wallet_revokePermissions";
1230
+ var REVOKE_PARAMS = [{ eth_accounts: {} }];
1231
+ var EIP6963_DISCOVERY_MS = 200;
1232
+ async function tryRevoke(provider, context) {
1233
+ if (!provider?.request) return false;
1234
+ try {
1235
+ await provider.request({ method: REVOKE_METHOD, params: REVOKE_PARAMS });
1236
+ console.info("[blink-sdk][revoke] ok", context);
1237
+ return true;
1238
+ } catch (err) {
1239
+ const code = err?.code;
1240
+ const message = err instanceof Error ? err.message : String(err);
1241
+ console.info("[blink-sdk][revoke] failed", { ...context, code, message });
1242
+ return false;
1243
+ }
1244
+ }
1245
+ async function getProviderForConnector(connector) {
1246
+ try {
1247
+ const provider = await connector.getProvider();
1248
+ return provider ?? null;
1249
+ } catch (err) {
1250
+ const message = err instanceof Error ? err.message : String(err);
1251
+ console.info("[blink-sdk][revoke] connector.getProvider() threw", {
1252
+ connectorId: connector.id,
1253
+ message
1254
+ });
1255
+ return null;
1256
+ }
1257
+ }
1258
+ async function discoverEip6963Providers(windowImpl, timeoutMs) {
1259
+ return new Promise((resolve) => {
1260
+ const seen = /* @__PURE__ */ new Map();
1261
+ const onAnnounce = (event) => {
1262
+ const detail = event.detail;
1263
+ if (!detail || !detail.info?.uuid) return;
1264
+ seen.set(detail.info.uuid, detail);
1265
+ };
1266
+ windowImpl.addEventListener("eip6963:announceProvider", onAnnounce);
1267
+ try {
1268
+ windowImpl.dispatchEvent(new Event("eip6963:requestProvider"));
1269
+ } catch {
1270
+ }
1271
+ const timer = setTimeout(() => {
1272
+ windowImpl.removeEventListener(
1273
+ "eip6963:announceProvider",
1274
+ onAnnounce
1275
+ );
1276
+ resolve([...seen.values()]);
1277
+ }, timeoutMs);
1278
+ if (typeof timer.unref === "function") {
1279
+ timer.unref();
1280
+ }
1281
+ });
1282
+ }
1283
+ async function probeAccounts(provider) {
1284
+ if (!provider.request) return [];
1285
+ try {
1286
+ const accounts = await provider.request({ method: "eth_accounts" });
1287
+ return Array.isArray(accounts) ? accounts : [];
1288
+ } catch {
1289
+ return [];
1290
+ }
1291
+ }
1292
+ async function revokeAndDisconnectConnector(deps) {
1293
+ const activeConnector = deps.getActiveConnector();
1294
+ if (activeConnector) {
1295
+ const provider = await getProviderForConnector(activeConnector);
1296
+ await tryRevoke(provider, {
1297
+ layer: 1,
1298
+ source: "wagmi-connector",
1299
+ rdns: activeConnector.id
1300
+ });
1301
+ await deps.disconnectFn(activeConnector).catch(() => {
1302
+ });
1303
+ return;
1304
+ }
1305
+ let recentRdns = null;
1306
+ try {
1307
+ recentRdns = await deps.getRecentConnectorId() ?? null;
1308
+ } catch {
1309
+ }
1310
+ if (recentRdns) {
1311
+ const matchedConnector = resolveWalletConnector(deps.listConnectors(), {
1312
+ wagmiConnectorId: recentRdns
1313
+ });
1314
+ if (matchedConnector) {
1315
+ const provider = await getProviderForConnector(matchedConnector);
1316
+ const revoked = await tryRevoke(provider, {
1317
+ layer: 2,
1318
+ source: "recent-connector",
1319
+ rdns: matchedConnector.id
1320
+ });
1321
+ await deps.disconnectFn(matchedConnector).catch(() => {
1322
+ });
1323
+ if (revoked) return;
1324
+ } else {
1325
+ console.info("[blink-sdk][revoke] recentConnectorId has no matching connector", {
1326
+ recentRdns
1327
+ });
1328
+ }
1329
+ }
1330
+ const win = deps.windowImpl ?? (typeof window === "undefined" ? null : window);
1331
+ if (!win) {
1332
+ console.warn("[blink-sdk][revoke] no candidate found and no window for EIP-6963 fallback");
1333
+ return;
1334
+ }
1335
+ const announces = await discoverEip6963Providers(
1336
+ win,
1337
+ deps.eip6963TimeoutMs ?? EIP6963_DISCOVERY_MS
1338
+ );
1339
+ if (announces.length === 0) {
1340
+ console.warn("[blink-sdk][revoke] EIP-6963 enumeration found no providers");
1341
+ return;
1342
+ }
1343
+ let anyRevoked = false;
1344
+ for (const detail of announces) {
1345
+ const accounts = await probeAccounts(detail.provider);
1346
+ if (accounts.length === 0) continue;
1347
+ const ok = await tryRevoke(detail.provider, {
1348
+ layer: 3,
1349
+ source: "eip6963-broadcast",
1350
+ rdns: detail.info.rdns
1351
+ });
1352
+ if (ok) anyRevoked = true;
1353
+ }
1354
+ if (!anyRevoked) {
1355
+ console.warn("[blink-sdk][revoke] EIP-6963 enumeration found no providers with a grant");
1356
+ }
1357
+ }
1358
+ async function revokeAndDisconnectActiveWagmiConnector(wagmiConfig) {
1359
+ await revokeAndDisconnectConnector({
1360
+ getActiveConnector: () => core.getAccount(wagmiConfig).connector,
1361
+ getRecentConnectorId: async () => {
1362
+ const value = await wagmiConfig.storage?.getItem("recentConnectorId");
1363
+ return typeof value === "string" ? value : null;
1364
+ },
1365
+ listConnectors: () => core.getConnectors(wagmiConfig),
1366
+ disconnectFn: (connector) => core.disconnect(wagmiConfig, { connector })
1367
+ });
1368
+ }
1369
+
1157
1370
  // src/otherWalletConnect.ts
1158
1371
  function findWalletConnectConnector(connectors) {
1159
1372
  return connectors.find((connector) => {
1160
- const id = normalize(connector.id);
1161
- const name = normalize(connector.name);
1162
- const type = normalize(connector.type);
1373
+ const id = normalize2(connector.id);
1374
+ const name = normalize2(connector.name);
1375
+ const type = normalize2(connector.type);
1163
1376
  return id === "walletconnect" || id === "wallet-connect" || name === "walletconnect" || name === "wallet connect" || type === "walletconnect";
1164
1377
  });
1165
1378
  }
@@ -1176,9 +1389,9 @@ function findReownExtensionConnector(connectors, wallet) {
1176
1389
  const eligible = connectors.filter((connector) => !findWalletConnectConnector([connector]));
1177
1390
  const match = eligible.find((connector) => {
1178
1391
  const connectorValues = [
1179
- normalize(connector.id),
1180
- normalize(connector.name),
1181
- normalize(connector.type)
1392
+ normalize2(connector.id),
1393
+ normalize2(connector.name),
1394
+ normalize2(connector.type)
1182
1395
  ].filter(Boolean);
1183
1396
  return targets.some(
1184
1397
  (target) => connectorValues.some(
@@ -1207,8 +1420,8 @@ async function findReownExtensionConnectorAsync(connectors, wallet, bridgeClient
1207
1420
  });
1208
1421
  return void 0;
1209
1422
  }
1210
- const targetRdns = normalize(resolved.rdns);
1211
- const match = connectors.find((connector) => normalize(connector.id) === targetRdns);
1423
+ const targetRdns = normalize2(resolved.rdns);
1424
+ const match = connectors.find((connector) => normalize2(connector.id) === targetRdns);
1212
1425
  console.info("[blink-bridge] findReownExtensionConnectorAsync \u2192 match", {
1213
1426
  walletName: wallet.name,
1214
1427
  flag: resolved.flag,
@@ -1226,84 +1439,59 @@ function getReownExtensionTargets(wallet) {
1226
1439
  return [...targets];
1227
1440
  }
1228
1441
  function addTarget(targets, value) {
1229
- const normalized = normalize(value);
1442
+ const normalized = normalize2(value);
1230
1443
  if (normalized) {
1231
1444
  targets.add(normalized);
1232
1445
  }
1233
1446
  }
1234
- function normalize(value) {
1447
+ function normalize2(value) {
1235
1448
  return (value ?? "").trim().toLowerCase().replaceAll(/[^a-z0-9.]/g, "");
1236
1449
  }
1237
1450
 
1238
- // src/walletConnectorResolver.ts
1239
- function normalize2(value) {
1240
- return (value ?? "").trim().toLowerCase();
1241
- }
1242
- function buildTargetMatchers(target) {
1243
- const values = [target.providerName, target.wagmiConnectorId].map(normalize2).filter(Boolean);
1244
- const aliases = new Set(values);
1245
- for (const value of values) {
1246
- if (value.includes("metamask")) {
1247
- aliases.add("metamask");
1248
- aliases.add("io.metamask");
1249
- }
1250
- if (value === "base" || value === "base account" || value === "base app" || value.includes("coinbase")) {
1251
- aliases.add("base");
1252
- aliases.add("coinbase");
1253
- aliases.add("coinbasewalletsdk");
1254
- }
1255
- if (value.includes("trust")) {
1256
- aliases.add("trust");
1257
- aliases.add("trustwallet");
1258
- }
1259
- if (value.includes("okx")) {
1260
- aliases.add("okx");
1261
- aliases.add("okxwallet");
1262
- aliases.add("com.okx.wallet");
1263
- }
1264
- if (value.includes("rabby")) {
1265
- aliases.add("rabby");
1266
- aliases.add("io.rabby");
1267
- }
1268
- if (value.includes("phantom")) {
1269
- aliases.add("phantom");
1270
- aliases.add("app.phantom");
1271
- }
1272
- if (value.includes("injected")) {
1273
- aliases.add("injected");
1274
- }
1275
- }
1276
- return [...aliases];
1277
- }
1278
- function connectorMatchesWallet(connector, target) {
1279
- if (!connector) {
1280
- return false;
1281
- }
1282
- const connectorId = normalize2(connector.id);
1283
- const connectorName = normalize2(connector.name);
1284
- if (target.wagmiConnectorId) {
1285
- const targetConnectorId = normalize2(target.wagmiConnectorId);
1286
- if (connectorId === targetConnectorId) {
1287
- return true;
1288
- }
1289
- }
1290
- const matchers = buildTargetMatchers(target);
1291
- if (matchers.length === 0) {
1292
- return false;
1293
- }
1294
- return matchers.some(
1295
- (matcher) => connectorId === matcher || connectorName === matcher || connectorId.includes(matcher) || connectorName.includes(matcher)
1296
- );
1297
- }
1298
- function resolveWalletConnector(connectors, target) {
1299
- if (target.wagmiConnectorId || target.providerName) {
1300
- return connectors.find((connector) => connectorMatchesWallet(connector, target));
1451
+ // src/resolveScreen.ts
1452
+ function screenForPhase(phase) {
1453
+ switch (phase.step) {
1454
+ case "initializing":
1455
+ case "data-loading":
1456
+ return "loading";
1457
+ case "manual-transfer":
1458
+ return "manual-transfer";
1459
+ case "login":
1460
+ return "login";
1461
+ case "deposit-options":
1462
+ return "deposit-options";
1463
+ case "wallet-picker":
1464
+ return "wallet-picker";
1465
+ case "wallet-setup":
1466
+ if (phase.mobile) {
1467
+ return "open-wallet";
1468
+ }
1469
+ if (phase.desktopWait) {
1470
+ return "open-wallet";
1471
+ }
1472
+ return "setup-deposit";
1473
+ case "select-source":
1474
+ return phase.isDesktop ? "setup" : "select-source";
1475
+ case "one-tap-setup":
1476
+ return "setup";
1477
+ case "token-picker":
1478
+ return "token-picker";
1479
+ case "guest-source-picker":
1480
+ return "guest-source-picker";
1481
+ case "deposit":
1482
+ return "deposit";
1483
+ case "processing":
1484
+ return "processing";
1485
+ case "confirm-sign":
1486
+ return "confirm-sign";
1487
+ case "completed":
1488
+ case "failed":
1489
+ return "success";
1490
+ case "amount-too-low":
1491
+ return "amount-too-low";
1492
+ case "enter-amount":
1493
+ return "enter-amount";
1301
1494
  }
1302
- const metaMaskConnector = connectors.find((connector) => connectorMatchesWallet(
1303
- connector,
1304
- { wagmiConnectorId: "metamask" }
1305
- ));
1306
- return metaMaskConnector ?? connectors[0];
1307
1495
  }
1308
1496
 
1309
1497
  // src/passkey-delegation.ts
@@ -1747,8 +1935,9 @@ async function fetchAccount(apiBaseUrl, token, accountId, credentialId) {
1747
1935
  return await res.json();
1748
1936
  }
1749
1937
  async function createAccount(apiBaseUrl, token, params) {
1938
+ const provisionalId = params.id ?? crypto.randomUUID();
1750
1939
  const body = {
1751
- id: params.id ?? crypto.randomUUID(),
1940
+ id: provisionalId,
1752
1941
  name: params.name,
1753
1942
  credentialId: params.credentialId
1754
1943
  };
@@ -1776,7 +1965,8 @@ async function createAccount(apiBaseUrl, token, params) {
1776
1965
  body: JSON.stringify(body)
1777
1966
  });
1778
1967
  if (!res.ok) await throwApiError(res);
1779
- return await res.json();
1968
+ const account = await res.json();
1969
+ return { ...account, id: account.id ?? provisionalId };
1780
1970
  }
1781
1971
  async function createAccountAuthorizationSession(apiBaseUrl, token, accountId, credentialId, options) {
1782
1972
  const body = { credentialId };
@@ -7369,289 +7559,692 @@ function EffectiveDepositAmountProvider({
7369
7559
  return /* @__PURE__ */ jsxRuntime.jsx(EffectiveDepositAmountContext.Provider, { value: memoValue, children });
7370
7560
  }
7371
7561
 
7372
- // src/resolveScreen.ts
7373
- function screenForPhase(phase) {
7374
- switch (phase.step) {
7375
- case "initializing":
7376
- case "data-loading":
7377
- return "loading";
7378
- case "manual-transfer":
7379
- return "manual-transfer";
7380
- case "login":
7381
- return "login";
7382
- case "deposit-options":
7383
- return "deposit-options";
7384
- case "wallet-picker":
7385
- return "wallet-picker";
7386
- case "wallet-setup":
7387
- if (phase.mobile) {
7388
- return "open-wallet";
7389
- }
7390
- if (phase.desktopWait) {
7391
- return "open-wallet";
7392
- }
7393
- return "setup-deposit";
7394
- case "select-source":
7395
- return phase.isDesktop ? "setup" : "select-source";
7396
- case "one-tap-setup":
7397
- return "setup";
7398
- case "token-picker":
7399
- return "token-picker";
7400
- case "guest-source-picker":
7401
- return "guest-source-picker";
7402
- case "deposit":
7403
- return "deposit";
7404
- case "processing":
7405
- return "processing";
7406
- case "confirm-sign":
7407
- return "confirm-sign";
7562
+ // src/manualTransferUtils.ts
7563
+ function screenForSession(session) {
7564
+ if (!session) return "source-selector";
7565
+ switch (session.status) {
7566
+ case "awaiting_deposit":
7567
+ return "awaiting-deposit";
7568
+ case "deposit_received":
7569
+ return "deposit-received";
7570
+ case "routing":
7571
+ return "deposit-routing";
7408
7572
  case "completed":
7573
+ return "deposit-complete";
7574
+ case "refunded":
7409
7575
  case "failed":
7410
- return "success";
7411
- case "amount-too-low":
7412
- return "amount-too-low";
7413
- case "enter-amount":
7414
- return "enter-amount";
7576
+ case "wrong_token":
7577
+ return "deposit-failed";
7415
7578
  }
7416
7579
  }
7417
-
7418
- // src/processingStatus.ts
7419
- var PROCESSING_TIMEOUT_MS = 18e4;
7420
- var TERMINAL_TRANSFER_STATUSES = /* @__PURE__ */ new Set(["COMPLETED", "FAILED", "EXPIRED"]);
7421
- var SIGNABLE_TRANSFER_STATUSES = /* @__PURE__ */ new Set(["CREATED", "AUTHORIZED"]);
7422
- function isTerminalTransferStatus(status) {
7423
- return TERMINAL_TRANSFER_STATUSES.has(status);
7424
- }
7425
- function isTransferSignable(transfer) {
7426
- return transfer != null && SIGNABLE_TRANSFER_STATUSES.has(transfer.status);
7427
- }
7428
- function isTransferAwaitingCompletion(transfer) {
7429
- if (!transfer) return false;
7430
- return !isTerminalTransferStatus(transfer.status);
7431
- }
7432
- function resolvePreferredTransfer(polledTransfer, localTransfer) {
7433
- return polledTransfer ?? localTransfer;
7434
- }
7435
- function getTransferStatus(polledTransfer, localTransfer) {
7436
- const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
7437
- return transfer?.status ?? "UNKNOWN";
7580
+ function feeCopy(_session) {
7581
+ return "No fees";
7438
7582
  }
7439
- function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
7440
- if (!processingStartedAtMs) return false;
7441
- return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
7583
+ function toTransfer(session) {
7584
+ return {
7585
+ id: session.sessionId,
7586
+ status: session.status,
7587
+ amount: {
7588
+ amount: Number(session.deliveredAmountUsd ?? session.minAmountUsd),
7589
+ currency: "USD"
7590
+ },
7591
+ sources: [],
7592
+ destinations: [{
7593
+ id: session.sessionId,
7594
+ chainId: session.destination.chainId,
7595
+ address: session.destination.address,
7596
+ token: { address: session.destination.token.address, symbol: "" },
7597
+ amount: { amount: Number(session.deliveredAmountUsd ?? session.minAmountUsd), currency: "USD" }
7598
+ }],
7599
+ createDate: session.createDate,
7600
+ updateDate: session.updateDate
7601
+ };
7442
7602
  }
7443
- var STATUS_DISPLAY_LABELS = {
7444
- CREATED: "created",
7445
- AUTHORIZED: "authorized",
7446
- SENDING: "sending",
7447
- SENT: "confirming delivery",
7448
- COMPLETED: "completed",
7449
- FAILED: "failed"
7450
- };
7451
- function getStatusDisplayLabel(status) {
7452
- return STATUS_DISPLAY_LABELS[status] ?? status;
7603
+ var SOLANA_CHAIN_ID = 792703809;
7604
+ var SOLANA_NATIVE_SOL_ADDRESS = "11111111111111111111111111111111";
7605
+ function formatDepositUri(address, chainId, depToken) {
7606
+ if (chainId === SOLANA_CHAIN_ID) {
7607
+ if (!depToken || depToken === SOLANA_NATIVE_SOL_ADDRESS) {
7608
+ return `solana:${address}`;
7609
+ }
7610
+ return `solana:${address}?spl-token=${depToken}`;
7611
+ }
7612
+ return address;
7453
7613
  }
7454
- function buildProcessingTimeoutMessage(status) {
7455
- const label = getStatusDisplayLabel(status);
7456
- return `Payment is taking longer than expected (status: ${label}). Please try again.`;
7614
+ var dataUrlCache = /* @__PURE__ */ new Map();
7615
+ var inFlight = /* @__PURE__ */ new Map();
7616
+ function keyFor(uri, colors) {
7617
+ return `${uri}|${colors.dark}|${colors.light}`;
7618
+ }
7619
+ function getCachedQrDataUrl(uri, colors) {
7620
+ return dataUrlCache.get(keyFor(uri, colors)) ?? null;
7621
+ }
7622
+ function getOrRenderQrDataUrl(uri, colors) {
7623
+ const key = keyFor(uri, colors);
7624
+ const cached = dataUrlCache.get(key);
7625
+ if (cached) return Promise.resolve(cached);
7626
+ const pending = inFlight.get(key);
7627
+ if (pending) return pending;
7628
+ const promise = QRCode__namespace.toDataURL(uri, {
7629
+ errorCorrectionLevel: "H",
7630
+ margin: 1,
7631
+ width: 203,
7632
+ color: { dark: colors.dark, light: colors.light }
7633
+ }).then((url) => {
7634
+ dataUrlCache.set(key, url);
7635
+ inFlight.delete(key);
7636
+ return url;
7637
+ }).catch((err) => {
7638
+ inFlight.delete(key);
7639
+ throw err;
7640
+ });
7641
+ inFlight.set(key, promise);
7642
+ return promise;
7457
7643
  }
7458
7644
 
7459
- // src/paymentResolvePhase.ts
7460
- function hasActiveWallet(accounts) {
7461
- return accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
7462
- }
7463
- function resolveTerminalPhase(state) {
7464
- if (state.amountTooLow != null) {
7465
- return {
7466
- step: "amount-too-low",
7467
- minAmountUsd: state.amountTooLow.minAmountUsd
7468
- };
7469
- }
7470
- const transferCompleted = state.transfer?.status === "COMPLETED";
7471
- const needsPasskeyBootstrap = state.privyAuthenticated && !state.activeCredentialId;
7472
- if (transferCompleted && !needsPasskeyBootstrap && !state.loginRequested) {
7473
- return { step: "completed", transfer: state.transfer };
7474
- }
7475
- if (state.transfer?.status === "FAILED") {
7476
- return {
7477
- step: "failed",
7478
- transfer: state.transfer,
7479
- error: state.error ?? "Transfer failed."
7480
- };
7481
- }
7482
- if (state.creatingTransfer || isTransferAwaitingCompletion(state.transfer)) {
7483
- return { step: "processing", transfer: state.transfer };
7484
- }
7485
- return null;
7645
+ // src/hooks/useManualTransferSession.ts
7646
+ function perTokenKey(chainId, tokenAddress) {
7647
+ return `${chainId}:${tokenAddress.toLowerCase()}`;
7486
7648
  }
7487
- function resolveStickyPhase(state) {
7488
- const currentPhase = state.phase;
7489
- if (currentPhase.step === "manual-transfer" && !state.loginRequested) {
7490
- return currentPhase;
7491
- }
7492
- if (currentPhase.step === "deposit-options" && !state.loginRequested && !state.privyAuthenticated) {
7493
- return currentPhase;
7494
- }
7495
- if (!state.loginRequested && state.setupFlowScreen === "one-tap-setup") {
7496
- return { step: "one-tap-setup", action: null };
7497
- }
7498
- if (!state.loginRequested && state.setupFlowScreen === "deposit-confirm" && state.selectedAccountId != null) {
7499
- return {
7500
- step: "wallet-setup",
7501
- mobile: null,
7502
- accountId: state.selectedAccountId
7503
- };
7504
- }
7505
- if (!state.loginRequested && state.mobileTokenAuthorizationPending) {
7506
- return {
7507
- step: "wallet-setup",
7508
- mobile: { deeplinkUri: "", providerId: state.selectedProviderId },
7509
- accountId: null
7510
- };
7511
- }
7512
- const isFundingSourceSubflow = !state.loginRequested && (currentPhase.step === "token-picker" || currentPhase.step === "guest-source-picker" || currentPhase.step === "select-source" || currentPhase.step === "confirm-sign");
7513
- if (isFundingSourceSubflow) {
7514
- return currentPhase;
7515
- }
7516
- if ((state.standardDesktopInlineOpenWallet || state.desktopWait != null) && state.privyAuthenticated && state.activeCredentialId != null && state.selectedAccountId != null && !state.loginRequested) {
7517
- return {
7518
- step: "wallet-setup",
7519
- mobile: null,
7520
- desktopWait: state.desktopWait,
7521
- accountId: state.selectedAccountId
7649
+ function useManualTransferSession({
7650
+ destination,
7651
+ merchantAuthorization,
7652
+ idempotencyKey,
7653
+ onComplete,
7654
+ onError,
7655
+ pollEnabled = true
7656
+ }) {
7657
+ const { apiBaseUrl, tokens } = useBlinkConfig();
7658
+ const [sourceOptions, setSourceOptions] = react.useState(null);
7659
+ const [loadingSources, setLoadingSources] = react.useState(true);
7660
+ const [selectedToken, setSelectedToken] = react.useState("");
7661
+ const [selectedChainId, setSelectedChainId] = react.useState("");
7662
+ const [sessionsByFamily, setSessionsByFamily] = react.useState({});
7663
+ const [perTokenSessions, setPerTokenSessions] = react.useState({});
7664
+ const [loading, setLoading] = react.useState(false);
7665
+ const [error, setError] = react.useState(null);
7666
+ const [copiedAddress, setCopiedAddress] = react.useState(null);
7667
+ const completedRef = react.useRef(/* @__PURE__ */ new Set());
7668
+ const premintedFamiliesRef = react.useRef(/* @__PURE__ */ new Set());
7669
+ const inFlightPerTokenRef = react.useRef(/* @__PURE__ */ new Set());
7670
+ react.useEffect(() => {
7671
+ if (!merchantAuthorization) return;
7672
+ let cancelled = false;
7673
+ setLoadingSources(true);
7674
+ fetchManualTransferSources(apiBaseUrl, { merchantAuthorization, destination }).then((sources) => {
7675
+ if (cancelled) return;
7676
+ setSourceOptions(sources);
7677
+ setLoadingSources(false);
7678
+ const svmCanonical = sources.find((s) => s.canonical && s.chainFamily === "svm");
7679
+ const evmCanonical = sources.find((s) => s.canonical && s.chainFamily === "evm");
7680
+ const defaultOption = svmCanonical ?? evmCanonical ?? sources[0];
7681
+ if (defaultOption) {
7682
+ setSelectedToken(defaultOption.tokenSymbol);
7683
+ setSelectedChainId(String(defaultOption.chainId));
7684
+ }
7685
+ }).catch((err) => {
7686
+ if (!cancelled) {
7687
+ setError(err instanceof Error ? err.message : String(err));
7688
+ setLoadingSources(false);
7689
+ }
7690
+ });
7691
+ return () => {
7692
+ cancelled = true;
7522
7693
  };
7523
- }
7524
- if (state.mobileFlow && state.deeplinkUri != null) {
7525
- return {
7526
- step: "wallet-setup",
7527
- mobile: { deeplinkUri: state.deeplinkUri, providerId: state.selectedProviderId },
7528
- accountId: null
7694
+ }, [apiBaseUrl, destination, merchantAuthorization]);
7695
+ react.useEffect(() => {
7696
+ if (!sourceOptions || !merchantAuthorization) return;
7697
+ const canonicals = sourceOptions.filter((s) => s.canonical);
7698
+ if (canonicals.length === 0) return;
7699
+ let cancelled = false;
7700
+ for (const opt of canonicals) {
7701
+ if (premintedFamiliesRef.current.has(opt.chainFamily)) continue;
7702
+ premintedFamiliesRef.current.add(opt.chainFamily);
7703
+ const family = opt.chainFamily;
7704
+ const run = async () => {
7705
+ try {
7706
+ const created = await createManualTransfer(apiBaseUrl, {
7707
+ merchantAuthorization,
7708
+ destination,
7709
+ idempotencyKey,
7710
+ source: { chainId: opt.chainId, tokenAddress: opt.tokenAddress }
7711
+ });
7712
+ if (cancelled) return;
7713
+ setSessionsByFamily((prev) => ({ ...prev, [family]: created }));
7714
+ const uri = formatDepositUri(created.depositAddress, opt.chainId, opt.tokenAddress);
7715
+ void getOrRenderQrDataUrl(uri, { dark: tokens.text, light: tokens.bg });
7716
+ } catch (err) {
7717
+ if (cancelled) return;
7718
+ premintedFamiliesRef.current.delete(family);
7719
+ setError(err instanceof Error ? err.message : String(err));
7720
+ }
7721
+ };
7722
+ void run();
7723
+ }
7724
+ return () => {
7725
+ cancelled = true;
7529
7726
  };
7530
- }
7531
- if (currentPhase.step === "wallet-picker" && currentPhase.reason === "switch" && !state.creatingTransfer && !(state.mobileFlow && state.deeplinkUri)) {
7532
- return currentPhase;
7533
- }
7534
- return null;
7535
- }
7536
- function deriveFreshPhase(state) {
7537
- if (!state.privyReady) {
7538
- return { step: "initializing" };
7539
- }
7540
- if (state.privyAuthenticated && !state.activeCredentialId && !state.passkeyConfigLoaded) {
7541
- return { step: "initializing" };
7542
- }
7543
- if (state.loginRequested) {
7544
- return { step: "login" };
7545
- }
7546
- if (state.enableFullWidget && !state.privyAuthenticated) {
7547
- return { step: "deposit-options" };
7548
- }
7549
- if (!state.privyAuthenticated) {
7550
- return { step: "login" };
7551
- }
7552
- if (state.loadingData && state.activeCredentialId != null) {
7553
- return { step: "data-loading" };
7554
- }
7555
- if (state.requireAmountEntry) {
7556
- return { step: "enter-amount" };
7557
- }
7558
- if (state.activeCredentialId != null && !hasActiveWallet(state.accounts) && !state.mobileFlow) {
7559
- return { step: "wallet-picker", reason: "link" };
7560
- }
7561
- if (state.activeCredentialId != null && hasActiveWallet(state.accounts) && !state.loadingData) {
7562
- return { step: "deposit" };
7563
- }
7564
- return { step: "wallet-picker", reason: "entry" };
7565
- }
7566
- function resolvePhase(state) {
7567
- return resolveTerminalPhase(state) ?? resolveStickyPhase(state) ?? deriveFreshPhase(state);
7568
- }
7569
-
7570
- // src/paymentReducer.ts
7571
- var DEFAULT_ONE_TAP_LIMIT = 1e4;
7572
- function deriveSourceTypeAndId(state) {
7573
- if (state.selectedWalletId) {
7574
- return { sourceType: "walletId", sourceId: state.selectedWalletId };
7575
- }
7576
- if (state.selectedAccountId) {
7577
- return { sourceType: "accountId", sourceId: state.selectedAccountId };
7578
- }
7579
- return { sourceType: "accountId", sourceId: "" };
7580
- }
7581
- function clearStaleSelection(state) {
7582
- if (state.selectedAccountId == null) return state;
7583
- const stillExists = state.accounts.some((a) => a.id === state.selectedAccountId);
7584
- if (stillExists) return state;
7727
+ }, [
7728
+ apiBaseUrl,
7729
+ destination,
7730
+ idempotencyKey,
7731
+ merchantAuthorization,
7732
+ sourceOptions,
7733
+ tokens.text,
7734
+ tokens.bg
7735
+ ]);
7736
+ const tokenChoices = react.useMemo(
7737
+ () => Array.from(new Set((sourceOptions ?? []).map((opt) => opt.tokenSymbol))),
7738
+ [sourceOptions]
7739
+ );
7740
+ const chainChoices = react.useMemo(() => {
7741
+ const seen = /* @__PURE__ */ new Set();
7742
+ return (sourceOptions ?? []).filter((opt) => {
7743
+ if (seen.has(opt.chainId)) return false;
7744
+ seen.add(opt.chainId);
7745
+ return true;
7746
+ });
7747
+ }, [sourceOptions]);
7748
+ const tokenLogoUriBySymbol = react.useMemo(() => {
7749
+ const out = {};
7750
+ for (const opt of sourceOptions ?? []) {
7751
+ if (out[opt.tokenSymbol] == null) {
7752
+ out[opt.tokenSymbol] = opt.tokenLogoUri;
7753
+ }
7754
+ }
7755
+ return out;
7756
+ }, [sourceOptions]);
7757
+ const selectedOption = react.useMemo(
7758
+ () => (sourceOptions ?? []).find(
7759
+ (opt) => opt.tokenSymbol === selectedToken && String(opt.chainId) === selectedChainId
7760
+ ) ?? null,
7761
+ [sourceOptions, selectedToken, selectedChainId]
7762
+ );
7763
+ const tokensForSelectedChain = react.useMemo(() => {
7764
+ if (!selectedChainId) return null;
7765
+ return new Set(
7766
+ (sourceOptions ?? []).filter((opt) => String(opt.chainId) === selectedChainId).map((opt) => opt.tokenSymbol)
7767
+ );
7768
+ }, [sourceOptions, selectedChainId]);
7769
+ const chainsForSelectedToken = react.useMemo(() => {
7770
+ if (!selectedToken) return null;
7771
+ return new Set(
7772
+ (sourceOptions ?? []).filter((opt) => opt.tokenSymbol === selectedToken).map((opt) => opt.chainId)
7773
+ );
7774
+ }, [sourceOptions, selectedToken]);
7775
+ const session = react.useMemo(() => {
7776
+ if (!selectedOption) return null;
7777
+ const familySession = sessionsByFamily[selectedOption.chainFamily];
7778
+ if (familySession) return familySession;
7779
+ return perTokenSessions[perTokenKey(selectedOption.chainId, selectedOption.tokenAddress)] ?? null;
7780
+ }, [selectedOption, sessionsByFamily, perTokenSessions]);
7781
+ const depositAddress = session?.depositAddress;
7782
+ const qrReady = !!depositAddress;
7783
+ const lastFeeCopyRef = react.useRef(null);
7784
+ const nextFeeCopy = session ? feeCopy() : null;
7785
+ const sessionFeeCopy = nextFeeCopy === lastFeeCopyRef.current ? lastFeeCopyRef.current : lastFeeCopyRef.current = nextFeeCopy;
7786
+ const activeSessionId = session?.sessionId;
7787
+ const activeSessionStatus = session?.status;
7788
+ react.useEffect(() => {
7789
+ if (!pollEnabled) return;
7790
+ if (!activeSessionId) return;
7791
+ if (activeSessionStatus && ["completed", "failed", "refunded", "wrong_token"].includes(activeSessionStatus)) return;
7792
+ const timer = window.setInterval(() => {
7793
+ fetchManualTransferSession(apiBaseUrl, activeSessionId).then((updated) => {
7794
+ setSessionsByFamily((prev) => {
7795
+ for (const family of Object.keys(prev)) {
7796
+ if (prev[family]?.sessionId === updated.sessionId) {
7797
+ return { ...prev, [family]: updated };
7798
+ }
7799
+ }
7800
+ return prev;
7801
+ });
7802
+ setPerTokenSessions((prev) => {
7803
+ for (const key of Object.keys(prev)) {
7804
+ if (prev[key]?.sessionId === updated.sessionId) {
7805
+ return { ...prev, [key]: updated };
7806
+ }
7807
+ }
7808
+ return prev;
7809
+ });
7810
+ }).catch((err) => setError(err instanceof Error ? err.message : String(err)));
7811
+ }, 2e3);
7812
+ return () => window.clearInterval(timer);
7813
+ }, [apiBaseUrl, activeSessionId, activeSessionStatus, pollEnabled]);
7814
+ const completionSignature = react.useMemo(() => {
7815
+ const entries2 = [];
7816
+ for (const family of Object.keys(sessionsByFamily)) {
7817
+ const s = sessionsByFamily[family];
7818
+ if (s) entries2.push([s.sessionId, s.status]);
7819
+ }
7820
+ for (const key of Object.keys(perTokenSessions)) {
7821
+ const s = perTokenSessions[key];
7822
+ entries2.push([s.sessionId, s.status]);
7823
+ }
7824
+ entries2.sort((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0);
7825
+ return JSON.stringify(entries2);
7826
+ }, [sessionsByFamily, perTokenSessions]);
7827
+ react.useEffect(() => {
7828
+ const all = [];
7829
+ for (const family of Object.keys(sessionsByFamily)) {
7830
+ const s = sessionsByFamily[family];
7831
+ if (s) all.push(s);
7832
+ }
7833
+ for (const key of Object.keys(perTokenSessions)) {
7834
+ all.push(perTokenSessions[key]);
7835
+ }
7836
+ for (const s of all) {
7837
+ if (s.status !== "completed") continue;
7838
+ if (completedRef.current.has(s.sessionId)) continue;
7839
+ completedRef.current.add(s.sessionId);
7840
+ onComplete?.(toTransfer(s));
7841
+ break;
7842
+ }
7843
+ }, [completionSignature, onComplete]);
7844
+ react.useEffect(() => {
7845
+ if (!error) return;
7846
+ onError?.(error);
7847
+ }, [error, onError]);
7848
+ const createSession = react.useCallback(async (option) => {
7849
+ if (!merchantAuthorization) return;
7850
+ setLoading(true);
7851
+ setError(null);
7852
+ try {
7853
+ const created = await createManualTransfer(apiBaseUrl, {
7854
+ merchantAuthorization,
7855
+ destination,
7856
+ idempotencyKey,
7857
+ source: { chainId: option.chainId, tokenAddress: option.tokenAddress }
7858
+ });
7859
+ const key = perTokenKey(option.chainId, option.tokenAddress);
7860
+ setPerTokenSessions((prev) => ({ ...prev, [key]: created }));
7861
+ const uri = formatDepositUri(created.depositAddress, option.chainId, option.tokenAddress);
7862
+ void getOrRenderQrDataUrl(uri, { dark: tokens.text, light: tokens.bg });
7863
+ } catch (err) {
7864
+ setError(err instanceof Error ? err.message : String(err));
7865
+ } finally {
7866
+ setLoading(false);
7867
+ }
7868
+ }, [apiBaseUrl, destination, idempotencyKey, merchantAuthorization, tokens.text, tokens.bg]);
7869
+ react.useEffect(() => {
7870
+ if (!selectedOption) return;
7871
+ if (sessionsByFamily[selectedOption.chainFamily]) return;
7872
+ if (premintedFamiliesRef.current.has(selectedOption.chainFamily)) return;
7873
+ const key = perTokenKey(selectedOption.chainId, selectedOption.tokenAddress);
7874
+ if (perTokenSessions[key]) return;
7875
+ if (inFlightPerTokenRef.current.has(key)) return;
7876
+ inFlightPerTokenRef.current.add(key);
7877
+ void createSession(selectedOption).finally(() => {
7878
+ inFlightPerTokenRef.current.delete(key);
7879
+ });
7880
+ }, [selectedOption, sessionsByFamily, perTokenSessions, createSession]);
7881
+ const resetSessionForNewSelection = react.useCallback(() => {
7882
+ setError(null);
7883
+ }, []);
7884
+ const selectToken = react.useCallback((value) => {
7885
+ setSelectedToken(value);
7886
+ const pairValid = (sourceOptions ?? []).some(
7887
+ (opt) => opt.tokenSymbol === value && String(opt.chainId) === selectedChainId
7888
+ );
7889
+ if (!pairValid) {
7890
+ const firstChain = (sourceOptions ?? []).find(
7891
+ (opt) => opt.tokenSymbol === value
7892
+ );
7893
+ setSelectedChainId(
7894
+ firstChain ? String(firstChain.chainId) : ""
7895
+ );
7896
+ }
7897
+ }, [selectedChainId, sourceOptions]);
7898
+ const selectChainId = react.useCallback((value) => {
7899
+ setSelectedChainId(value);
7900
+ const pairValid = (sourceOptions ?? []).some(
7901
+ (opt) => opt.tokenSymbol === selectedToken && String(opt.chainId) === value
7902
+ );
7903
+ if (!pairValid) {
7904
+ const firstToken = (sourceOptions ?? []).find(
7905
+ (opt) => String(opt.chainId) === value
7906
+ );
7907
+ setSelectedToken(
7908
+ firstToken ? firstToken.tokenSymbol : ""
7909
+ );
7910
+ }
7911
+ }, [selectedToken, sourceOptions]);
7912
+ const screen = screenForSession(session);
7913
+ const copyDepositAddress = react.useCallback(async (address) => {
7914
+ try {
7915
+ await navigator.clipboard.writeText(address);
7916
+ } catch {
7917
+ const textarea = document.createElement("textarea");
7918
+ textarea.value = address;
7919
+ textarea.setAttribute("readonly", "");
7920
+ textarea.style.position = "absolute";
7921
+ textarea.style.opacity = "0";
7922
+ document.body.appendChild(textarea);
7923
+ textarea.select();
7924
+ try {
7925
+ document.execCommand("copy");
7926
+ } finally {
7927
+ document.body.removeChild(textarea);
7928
+ }
7929
+ }
7930
+ setCopiedAddress(address);
7931
+ window.setTimeout(() => setCopiedAddress((cur) => cur === address ? null : cur), 1500);
7932
+ }, []);
7933
+ const backToSourceSelector = react.useCallback(() => {
7934
+ setCopiedAddress(null);
7935
+ setError(null);
7936
+ if (!selectedOption) return;
7937
+ const family = selectedOption.chainFamily;
7938
+ setSessionsByFamily((prev) => {
7939
+ if (!prev[family]) return prev;
7940
+ const next = { ...prev };
7941
+ delete next[family];
7942
+ return next;
7943
+ });
7944
+ premintedFamiliesRef.current.delete(family);
7945
+ const ptKey = perTokenKey(selectedOption.chainId, selectedOption.tokenAddress);
7946
+ setPerTokenSessions((prev) => {
7947
+ if (!prev[ptKey]) return prev;
7948
+ const next = { ...prev };
7949
+ delete next[ptKey];
7950
+ return next;
7951
+ });
7952
+ }, [selectedOption]);
7585
7953
  return {
7586
- ...state,
7587
- selectedAccountId: null,
7588
- selectedWalletId: null,
7589
- selectedTokenSymbol: null
7954
+ sourceOptions,
7955
+ loadingSources,
7956
+ selectedToken,
7957
+ selectedChainId,
7958
+ session,
7959
+ loading,
7960
+ error,
7961
+ qrReady,
7962
+ copiedAddress,
7963
+ tokenChoices,
7964
+ chainChoices,
7965
+ tokenLogoUriBySymbol,
7966
+ selectedOption,
7967
+ tokensForSelectedChain,
7968
+ chainsForSelectedToken,
7969
+ screen,
7970
+ sessionFeeCopy,
7971
+ depositAddress,
7972
+ createSession,
7973
+ copyDepositAddress,
7974
+ backToSourceSelector,
7975
+ resetSessionForNewSelection,
7976
+ selectToken,
7977
+ selectChainId
7590
7978
  };
7591
7979
  }
7592
- function createInitialState(config) {
7593
- return {
7594
- phase: config.initialPhase ?? { step: "initializing" },
7595
- error: null,
7596
- setupFlowScreen: null,
7597
- providers: [],
7598
- accounts: [],
7599
- chains: [],
7600
- loadingData: false,
7601
- depositSelectionRefreshing: false,
7602
- selectedProviderId: null,
7603
- selectedAccountId: null,
7604
- selectedWalletId: null,
7605
- selectedTokenSymbol: null,
7606
- savedSelection: null,
7607
- amount: config.depositAmount != null ? config.depositAmount.toString() : "",
7608
- transfer: null,
7609
- pendingTransferId: null,
7610
- creatingTransfer: false,
7611
- passkeyConfigLoaded: false,
7612
- activeCredentialId: config.activeCredentialId,
7613
- knownCredentialIds: [],
7614
- oneTapLimit: DEFAULT_ONE_TAP_LIMIT,
7615
- oneTapLimitSavedDuringSetup: false,
7616
- mobileFlow: false,
7617
- deeplinkUri: null,
7618
- increasingLimit: false,
7619
- activePublicKey: null,
7620
- loginRequested: false,
7621
- standardDesktopInlineOpenWallet: false,
7622
- desktopWait: null,
7623
- setupAuthorizationSessionId: null,
7624
- mobileTokenAuthorizationPending: false,
7625
- privyReady: false,
7626
- privyAuthenticated: false,
7627
- lastResumedAt: 0,
7628
- setupDepositAmount: null,
7629
- setupDepositToken: null,
7630
- setupDepositConfirmed: false,
7631
- guestWalletPrepared: null,
7632
- guestWalletDeeplinksPreparing: false,
7633
- amountTooLow: null,
7634
- enableFullWidget: config.enableFullWidget ?? false,
7635
- requireAmountEntry: config.depositAmount == null
7636
- };
7980
+ var ManualTransferSessionContext = react.createContext(null);
7981
+ function ManualTransferSessionProvider({
7982
+ destination,
7983
+ merchantAuthorization,
7984
+ idempotencyKey,
7985
+ onComplete,
7986
+ onError,
7987
+ pollEnabled,
7988
+ children
7989
+ }) {
7990
+ const session = useManualTransferSession({
7991
+ destination,
7992
+ merchantAuthorization,
7993
+ idempotencyKey,
7994
+ onComplete,
7995
+ onError,
7996
+ pollEnabled
7997
+ });
7998
+ return /* @__PURE__ */ jsxRuntime.jsx(ManualTransferSessionContext.Provider, { value: session, children });
7637
7999
  }
7638
- function clearAuthenticatedSessionState(state) {
7639
- return {
7640
- ...state,
7641
- error: null,
7642
- setupFlowScreen: null,
7643
- providers: [],
7644
- accounts: [],
7645
- chains: [],
7646
- loadingData: false,
7647
- depositSelectionRefreshing: false,
7648
- selectedProviderId: null,
7649
- selectedAccountId: null,
7650
- selectedWalletId: null,
7651
- selectedTokenSymbol: null,
7652
- savedSelection: null,
7653
- transfer: null,
7654
- pendingTransferId: null,
8000
+ function useManualTransferSessionContext() {
8001
+ const ctx = react.useContext(ManualTransferSessionContext);
8002
+ if (!ctx) {
8003
+ throw new Error(
8004
+ "useManualTransferSessionContext must be used within a <ManualTransferSessionProvider>"
8005
+ );
8006
+ }
8007
+ return ctx;
8008
+ }
8009
+
8010
+ // src/processingStatus.ts
8011
+ var PROCESSING_TIMEOUT_MS = 18e4;
8012
+ var TERMINAL_TRANSFER_STATUSES = /* @__PURE__ */ new Set(["COMPLETED", "FAILED", "EXPIRED"]);
8013
+ var SIGNABLE_TRANSFER_STATUSES = /* @__PURE__ */ new Set(["CREATED", "AUTHORIZED"]);
8014
+ function isTerminalTransferStatus(status) {
8015
+ return TERMINAL_TRANSFER_STATUSES.has(status);
8016
+ }
8017
+ function isTransferSignable(transfer) {
8018
+ return transfer != null && SIGNABLE_TRANSFER_STATUSES.has(transfer.status);
8019
+ }
8020
+ function isTransferAwaitingCompletion(transfer) {
8021
+ if (!transfer) return false;
8022
+ return !isTerminalTransferStatus(transfer.status);
8023
+ }
8024
+ function resolvePreferredTransfer(polledTransfer, localTransfer) {
8025
+ return polledTransfer ?? localTransfer;
8026
+ }
8027
+ function getTransferStatus(polledTransfer, localTransfer) {
8028
+ const transfer = resolvePreferredTransfer(polledTransfer, localTransfer);
8029
+ return transfer?.status ?? "UNKNOWN";
8030
+ }
8031
+ function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
8032
+ if (!processingStartedAtMs) return false;
8033
+ return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
8034
+ }
8035
+ var STATUS_DISPLAY_LABELS = {
8036
+ CREATED: "created",
8037
+ AUTHORIZED: "authorized",
8038
+ SENDING: "sending",
8039
+ SENT: "confirming delivery",
8040
+ COMPLETED: "completed",
8041
+ FAILED: "failed"
8042
+ };
8043
+ function getStatusDisplayLabel(status) {
8044
+ return STATUS_DISPLAY_LABELS[status] ?? status;
8045
+ }
8046
+ function buildProcessingTimeoutMessage(status) {
8047
+ const label = getStatusDisplayLabel(status);
8048
+ return `Payment is taking longer than expected (status: ${label}). Please try again.`;
8049
+ }
8050
+
8051
+ // src/paymentResolvePhase.ts
8052
+ function hasActiveWallet(accounts) {
8053
+ return accounts.some((a) => a.wallets.some((w) => w.status === "ACTIVE"));
8054
+ }
8055
+ function resolveTerminalPhase(state) {
8056
+ if (state.amountTooLow != null) {
8057
+ return {
8058
+ step: "amount-too-low",
8059
+ minAmountUsd: state.amountTooLow.minAmountUsd
8060
+ };
8061
+ }
8062
+ const transferCompleted = state.transfer?.status === "COMPLETED";
8063
+ const needsPasskeyBootstrap = state.privyAuthenticated && !state.activeCredentialId;
8064
+ if (transferCompleted && !needsPasskeyBootstrap && !state.loginRequested) {
8065
+ return { step: "completed", transfer: state.transfer };
8066
+ }
8067
+ if (state.transfer?.status === "FAILED") {
8068
+ return {
8069
+ step: "failed",
8070
+ transfer: state.transfer,
8071
+ error: state.error ?? "Transfer failed."
8072
+ };
8073
+ }
8074
+ if (state.creatingTransfer || isTransferAwaitingCompletion(state.transfer)) {
8075
+ return { step: "processing", transfer: state.transfer };
8076
+ }
8077
+ return null;
8078
+ }
8079
+ function resolveStickyPhase(state) {
8080
+ const currentPhase = state.phase;
8081
+ if (currentPhase.step === "manual-transfer" && !state.loginRequested) {
8082
+ return currentPhase;
8083
+ }
8084
+ if (currentPhase.step === "deposit-options" && !state.loginRequested && !state.privyAuthenticated) {
8085
+ return currentPhase;
8086
+ }
8087
+ if (!state.loginRequested && state.setupFlowScreen === "one-tap-setup") {
8088
+ return { step: "one-tap-setup", action: null };
8089
+ }
8090
+ if (!state.loginRequested && state.setupFlowScreen === "deposit-confirm" && state.selectedAccountId != null) {
8091
+ return {
8092
+ step: "wallet-setup",
8093
+ mobile: null,
8094
+ accountId: state.selectedAccountId
8095
+ };
8096
+ }
8097
+ if (!state.loginRequested && state.mobileTokenAuthorizationPending) {
8098
+ return {
8099
+ step: "wallet-setup",
8100
+ mobile: { deeplinkUri: "", providerId: state.selectedProviderId },
8101
+ accountId: null
8102
+ };
8103
+ }
8104
+ const isFundingSourceSubflow = !state.loginRequested && (currentPhase.step === "token-picker" || currentPhase.step === "guest-source-picker" || currentPhase.step === "select-source" || currentPhase.step === "confirm-sign");
8105
+ if (isFundingSourceSubflow) {
8106
+ return currentPhase;
8107
+ }
8108
+ if ((state.standardDesktopInlineOpenWallet || state.desktopWait != null) && state.privyAuthenticated && state.activeCredentialId != null && state.selectedAccountId != null && !state.loginRequested) {
8109
+ return {
8110
+ step: "wallet-setup",
8111
+ mobile: null,
8112
+ desktopWait: state.desktopWait,
8113
+ accountId: state.selectedAccountId
8114
+ };
8115
+ }
8116
+ if (state.mobileFlow && state.deeplinkUri != null) {
8117
+ return {
8118
+ step: "wallet-setup",
8119
+ mobile: { deeplinkUri: state.deeplinkUri, providerId: state.selectedProviderId },
8120
+ accountId: null
8121
+ };
8122
+ }
8123
+ if (currentPhase.step === "wallet-picker" && currentPhase.reason === "switch" && !state.creatingTransfer && !(state.mobileFlow && state.deeplinkUri)) {
8124
+ return currentPhase;
8125
+ }
8126
+ return null;
8127
+ }
8128
+ function deriveFreshPhase(state) {
8129
+ if (!state.privyReady) {
8130
+ return { step: "initializing" };
8131
+ }
8132
+ if (state.privyAuthenticated && !state.activeCredentialId && !state.passkeyConfigLoaded) {
8133
+ return { step: "initializing" };
8134
+ }
8135
+ if (state.loginRequested) {
8136
+ return { step: "login" };
8137
+ }
8138
+ if (state.enableFullWidget && !state.privyAuthenticated) {
8139
+ return { step: "deposit-options" };
8140
+ }
8141
+ if (!state.privyAuthenticated) {
8142
+ return { step: "login" };
8143
+ }
8144
+ if (state.loadingData && state.activeCredentialId != null) {
8145
+ return { step: "data-loading" };
8146
+ }
8147
+ if (state.requireAmountEntry) {
8148
+ return { step: "enter-amount" };
8149
+ }
8150
+ if (state.activeCredentialId != null && !hasActiveWallet(state.accounts) && !state.mobileFlow) {
8151
+ return { step: "wallet-picker", reason: "link" };
8152
+ }
8153
+ if (state.activeCredentialId != null && hasActiveWallet(state.accounts) && !state.loadingData) {
8154
+ return { step: "deposit" };
8155
+ }
8156
+ return { step: "wallet-picker", reason: "entry" };
8157
+ }
8158
+ function resolvePhase(state) {
8159
+ return resolveTerminalPhase(state) ?? resolveStickyPhase(state) ?? deriveFreshPhase(state);
8160
+ }
8161
+
8162
+ // src/paymentReducer.ts
8163
+ var DEFAULT_ONE_TAP_LIMIT = 1e4;
8164
+ function deriveSourceTypeAndId(state) {
8165
+ if (state.selectedWalletId) {
8166
+ return { sourceType: "walletId", sourceId: state.selectedWalletId };
8167
+ }
8168
+ if (state.selectedAccountId) {
8169
+ return { sourceType: "accountId", sourceId: state.selectedAccountId };
8170
+ }
8171
+ return { sourceType: "accountId", sourceId: "" };
8172
+ }
8173
+ function clearStaleSelection(state) {
8174
+ if (state.selectedAccountId == null) return state;
8175
+ if (state.desktopWait != null) return state;
8176
+ const stillExists = state.accounts.some((a) => a.id === state.selectedAccountId);
8177
+ if (stillExists) return state;
8178
+ return {
8179
+ ...state,
8180
+ selectedAccountId: null,
8181
+ selectedWalletId: null,
8182
+ selectedTokenSymbol: null
8183
+ };
8184
+ }
8185
+ function createInitialState(config) {
8186
+ return {
8187
+ phase: config.initialPhase ?? { step: "initializing" },
8188
+ error: null,
8189
+ setupFlowScreen: null,
8190
+ providers: [],
8191
+ accounts: [],
8192
+ chains: [],
8193
+ loadingData: false,
8194
+ depositSelectionRefreshing: false,
8195
+ selectedProviderId: null,
8196
+ selectedAccountId: null,
8197
+ selectedWalletId: null,
8198
+ selectedTokenSymbol: null,
8199
+ savedSelection: null,
8200
+ amount: config.depositAmount != null ? config.depositAmount.toString() : "",
8201
+ transfer: null,
8202
+ pendingTransferId: null,
8203
+ creatingTransfer: false,
8204
+ passkeyConfigLoaded: false,
8205
+ activeCredentialId: config.activeCredentialId,
8206
+ knownCredentialIds: [],
8207
+ oneTapLimit: DEFAULT_ONE_TAP_LIMIT,
8208
+ oneTapLimitSavedDuringSetup: false,
8209
+ mobileFlow: false,
8210
+ deeplinkUri: null,
8211
+ increasingLimit: false,
8212
+ activePublicKey: null,
8213
+ loginRequested: false,
8214
+ standardDesktopInlineOpenWallet: false,
8215
+ desktopWait: null,
8216
+ setupAuthorizationSessionId: null,
8217
+ mobileTokenAuthorizationPending: false,
8218
+ privyReady: false,
8219
+ privyAuthenticated: false,
8220
+ lastResumedAt: 0,
8221
+ setupDepositAmount: null,
8222
+ setupDepositToken: null,
8223
+ setupDepositConfirmed: false,
8224
+ guestWalletPrepared: null,
8225
+ guestWalletDeeplinksPreparing: false,
8226
+ amountTooLow: null,
8227
+ enableFullWidget: config.enableFullWidget ?? false,
8228
+ requireAmountEntry: config.depositAmount == null
8229
+ };
8230
+ }
8231
+ function clearAuthenticatedSessionState(state) {
8232
+ return {
8233
+ ...state,
8234
+ error: null,
8235
+ setupFlowScreen: null,
8236
+ providers: [],
8237
+ accounts: [],
8238
+ chains: [],
8239
+ loadingData: false,
8240
+ depositSelectionRefreshing: false,
8241
+ selectedProviderId: null,
8242
+ selectedAccountId: null,
8243
+ selectedWalletId: null,
8244
+ selectedTokenSymbol: null,
8245
+ savedSelection: null,
8246
+ transfer: null,
8247
+ pendingTransferId: null,
7655
8248
  creatingTransfer: false,
7656
8249
  passkeyConfigLoaded: false,
7657
8250
  activeCredentialId: null,
@@ -8095,552 +8688,118 @@ function applyAction(state, action) {
8095
8688
  // unauthenticated path) preserves the flag implicitly via the
8096
8689
  // surrounding `...state` spread, so no change is needed there.
8097
8690
  enableFullWidget: state.enableFullWidget
8098
- };
8099
- case "SYNC_PRIVY_SESSION":
8100
- if (action.ready && !action.authenticated) {
8101
- return {
8102
- ...clearAuthenticatedSessionState(state),
8103
- privyReady: true,
8104
- privyAuthenticated: false
8105
- };
8106
- }
8107
- return {
8108
- ...state,
8109
- privyReady: action.ready,
8110
- privyAuthenticated: action.authenticated,
8111
- ...action.authenticated ? { loginRequested: false } : {}
8112
- };
8113
- case "SYNC_AMOUNT":
8114
- return {
8115
- ...state,
8116
- amount: action.amount,
8117
- requireAmountEntry: action.amount === "" ? state.requireAmountEntry : false
8118
- };
8119
- case "SET_AMOUNT_INPUT":
8120
- return { ...state, amount: action.value };
8121
- case "FINALIZE_AMOUNT":
8122
- return { ...state, requireAmountEntry: false };
8123
- case "PAGE_RESUMED":
8124
- return { ...state, lastResumedAt: Date.now() };
8125
- // ── Setup deposit (combined first-time flow) ────────────────
8126
- case "SET_SETUP_DEPOSIT_AMOUNT":
8127
- return { ...state, setupDepositAmount: action.amount };
8128
- case "SET_SETUP_DEPOSIT_TOKEN":
8129
- return {
8130
- ...state,
8131
- setupDepositToken: {
8132
- symbol: action.symbol,
8133
- chainName: action.chainName,
8134
- ...action.walletId ? { walletId: action.walletId } : {},
8135
- ...action.tokenAddress ? { tokenAddress: action.tokenAddress } : {},
8136
- ...action.chainId != null ? { chainId: action.chainId } : {}
8137
- }
8138
- };
8139
- case "CLEAR_SETUP_DEPOSIT_TOKEN":
8140
- return { ...state, setupDepositToken: null };
8141
- case "CONFIRM_SETUP_DEPOSIT":
8142
- return { ...state, setupDepositConfirmed: true };
8143
- default:
8144
- return state;
8145
- }
8146
- }
8147
-
8148
- // src/setupDepositConfirmation.ts
8149
- function deriveEffectiveSetupSource(setupDepositToken, setupSelectedSourceOption) {
8150
- if (setupDepositToken) return setupDepositToken;
8151
- if (setupSelectedSourceOption) {
8152
- return {
8153
- symbol: setupSelectedSourceOption.tokenSymbol,
8154
- chainName: setupSelectedSourceOption.chainName,
8155
- walletId: setupSelectedSourceOption.walletId,
8156
- tokenAddress: setupSelectedSourceOption.tokenAddress,
8157
- chainId: setupSelectedSourceOption.chainId != null ? Number(setupSelectedSourceOption.chainId) : void 0
8158
- };
8159
- }
8160
- return null;
8161
- }
8162
- function planConfirmSetupDeposit(input) {
8163
- const effective = deriveEffectiveSetupSource(
8164
- input.setupDepositToken,
8165
- input.setupSelectedSourceOption
8166
- );
8167
- if (!effective) {
8168
- return { kind: "error", error: "Select a source token before continuing." };
8169
- }
8170
- if (!effective.walletId) {
8171
- return {
8172
- kind: "error",
8173
- error: "Selected source is not ready yet. Wait for wallet discovery to finish."
8174
- };
8175
- }
8176
- const actions = [
8177
- { type: "SET_ERROR", error: null },
8178
- {
8179
- type: "SET_SETUP_DEPOSIT_TOKEN",
8180
- symbol: effective.symbol,
8181
- chainName: effective.chainName,
8182
- walletId: effective.walletId,
8183
- tokenAddress: effective.tokenAddress,
8184
- chainId: effective.chainId
8185
- }
8186
- ];
8187
- if (input.selectedAccountId) {
8188
- actions.push({
8189
- type: "SELECT_ACCOUNT",
8190
- accountId: input.selectedAccountId,
8191
- walletId: effective.walletId
8192
- });
8193
- actions.push({
8194
- type: "SELECT_TOKEN",
8195
- walletId: effective.walletId,
8196
- tokenSymbol: effective.symbol
8197
- });
8198
- }
8199
- actions.push({ type: "SET_SETUP_FLOW_SCREEN", screen: "one-tap-setup" });
8200
- actions.push({ type: "CONFIRM_SETUP_DEPOSIT" });
8201
- return {
8202
- kind: "proceed",
8203
- actions,
8204
- resolveSource: {
8205
- chainName: effective.chainName,
8206
- tokenSymbol: effective.symbol
8207
- }
8208
- };
8209
- }
8210
-
8211
- // src/manualTransferUtils.ts
8212
- function screenForSession(session) {
8213
- if (!session) return "source-selector";
8214
- switch (session.status) {
8215
- case "awaiting_deposit":
8216
- return "awaiting-deposit";
8217
- case "deposit_received":
8218
- return "deposit-received";
8219
- case "routing":
8220
- return "deposit-routing";
8221
- case "completed":
8222
- return "deposit-complete";
8223
- case "refunded":
8224
- case "failed":
8225
- case "wrong_token":
8226
- return "deposit-failed";
8227
- }
8228
- }
8229
- function feeCopy(_session) {
8230
- return "No fees";
8231
- }
8232
- function toTransfer(session) {
8233
- return {
8234
- id: session.sessionId,
8235
- status: session.status,
8236
- amount: {
8237
- amount: Number(session.deliveredAmountUsd ?? session.minAmountUsd),
8238
- currency: "USD"
8239
- },
8240
- sources: [],
8241
- destinations: [{
8242
- id: session.sessionId,
8243
- chainId: session.destination.chainId,
8244
- address: session.destination.address,
8245
- token: { address: session.destination.token.address, symbol: "" },
8246
- amount: { amount: Number(session.deliveredAmountUsd ?? session.minAmountUsd), currency: "USD" }
8247
- }],
8248
- createDate: session.createDate,
8249
- updateDate: session.updateDate
8250
- };
8251
- }
8252
- var SOLANA_CHAIN_ID = 792703809;
8253
- var SOLANA_NATIVE_SOL_ADDRESS = "11111111111111111111111111111111";
8254
- function formatDepositUri(address, chainId, depToken) {
8255
- if (chainId === SOLANA_CHAIN_ID) {
8256
- if (!depToken || depToken === SOLANA_NATIVE_SOL_ADDRESS) {
8257
- return `solana:${address}`;
8258
- }
8259
- return `solana:${address}?spl-token=${depToken}`;
8260
- }
8261
- return address;
8262
- }
8263
-
8264
- // src/hooks/useManualTransferSession.ts
8265
- function useManualTransferSession({
8266
- destination,
8267
- merchantAuthorization,
8268
- idempotencyKey,
8269
- mock = false,
8270
- onComplete,
8271
- onError
8272
- }) {
8273
- const { apiBaseUrl } = useBlinkConfig();
8274
- const [sourceOptions, setSourceOptions] = react.useState(null);
8275
- const [loadingSources, setLoadingSources] = react.useState(true);
8276
- const [selectedToken, setSelectedToken] = react.useState("");
8277
- const [selectedChainId, setSelectedChainId] = react.useState("");
8278
- const [session, setSession] = react.useState(null);
8279
- const [loading, setLoading] = react.useState(false);
8280
- const [error, setError] = react.useState(null);
8281
- const [qrReady, setQrReady] = react.useState(false);
8282
- const [copiedAddress, setCopiedAddress] = react.useState(null);
8283
- const completedRef = react.useRef(null);
8284
- const lastCreatedOptionRef = react.useRef(null);
8285
- react.useEffect(() => {
8286
- if (!merchantAuthorization) return;
8287
- let cancelled = false;
8288
- setLoadingSources(true);
8289
- fetchManualTransferSources(apiBaseUrl, { merchantAuthorization, destination }).then((sources) => {
8290
- if (!cancelled) {
8291
- setSourceOptions(sources);
8292
- setLoadingSources(false);
8293
- const defaultOption = sources.find(
8294
- (opt) => opt.tokenSymbol === "USDC" && opt.chainId === 792703809
8295
- );
8296
- if (defaultOption) {
8297
- setSelectedToken("USDC");
8298
- setSelectedChainId("792703809");
8299
- }
8300
- }
8301
- }).catch((err) => {
8302
- if (!cancelled) {
8303
- setError(err instanceof Error ? err.message : String(err));
8304
- setLoadingSources(false);
8305
- }
8306
- });
8307
- return () => {
8308
- cancelled = true;
8309
- };
8310
- }, [apiBaseUrl, destination, merchantAuthorization]);
8311
- const depositAddress = session?.depositAddress;
8312
- react.useEffect(() => {
8313
- if (!depositAddress) {
8314
- setQrReady(false);
8315
- return;
8316
- }
8317
- const timer = window.setTimeout(() => setQrReady(true), 1200);
8318
- return () => window.clearTimeout(timer);
8319
- }, [depositAddress]);
8320
- const lastFeeCopyRef = react.useRef(null);
8321
- const nextFeeCopy = session ? feeCopy() : null;
8322
- const sessionFeeCopy = nextFeeCopy === lastFeeCopyRef.current ? lastFeeCopyRef.current : lastFeeCopyRef.current = nextFeeCopy;
8323
- const advanceMockStatus = react.useCallback((status) => {
8324
- setSession((prev) => prev ? { ...prev, status } : prev);
8325
- }, []);
8326
- react.useEffect(() => {
8327
- if (mock) return;
8328
- if (!session?.sessionId || ["completed", "failed", "refunded", "wrong_token"].includes(session.status)) return;
8329
- const timer = window.setInterval(() => {
8330
- fetchManualTransferSession(apiBaseUrl, session.sessionId).then(setSession).catch((err) => setError(err instanceof Error ? err.message : String(err)));
8331
- }, 2e3);
8332
- return () => window.clearInterval(timer);
8333
- }, [apiBaseUrl, mock, session?.sessionId, session?.status]);
8334
- react.useEffect(() => {
8335
- if (mock) return;
8336
- if (!session?.sessionId || session.status !== "awaiting_deposit") return;
8337
- const sessionId = session.sessionId;
8338
- const timer = window.setInterval(() => {
8339
- refreshManualTransferQuote(apiBaseUrl, sessionId).then(({ quoteValidUntil, minAmountUsd, slippage }) => {
8340
- setSession(
8341
- (prev) => prev && prev.sessionId === sessionId ? { ...prev, quoteValidUntil, minAmountUsd, slippage } : prev
8342
- );
8343
- }).catch((err) => {
8344
- console.warn("[manual-transfer] refresh quote failed", err);
8345
- });
8346
- }, 1e4);
8347
- return () => window.clearInterval(timer);
8348
- }, [apiBaseUrl, mock, session?.sessionId, session?.status]);
8349
- react.useEffect(() => {
8350
- if (session?.status !== "completed") return;
8351
- if (completedRef.current === session.sessionId) return;
8352
- completedRef.current = session.sessionId;
8353
- onComplete?.(toTransfer(session));
8354
- }, [onComplete, session]);
8355
- react.useEffect(() => {
8356
- if (!error) return;
8357
- onError?.(error);
8358
- }, [error, onError]);
8359
- const tokenChoices = react.useMemo(
8360
- () => Array.from(new Set((sourceOptions ?? []).map((opt) => opt.tokenSymbol))),
8361
- [sourceOptions]
8362
- );
8363
- const chainChoices = react.useMemo(() => {
8364
- const seen = /* @__PURE__ */ new Set();
8365
- return (sourceOptions ?? []).filter((opt) => {
8366
- if (seen.has(opt.chainId)) return false;
8367
- seen.add(opt.chainId);
8368
- return true;
8369
- });
8370
- }, [sourceOptions]);
8371
- const tokenLogoUriBySymbol = react.useMemo(() => {
8372
- const out = {};
8373
- for (const opt of sourceOptions ?? []) {
8374
- if (out[opt.tokenSymbol] == null) {
8375
- out[opt.tokenSymbol] = opt.tokenLogoUri;
8376
- }
8377
- }
8378
- return out;
8379
- }, [sourceOptions]);
8380
- const selectedOption = react.useMemo(
8381
- () => (sourceOptions ?? []).find(
8382
- (opt) => opt.tokenSymbol === selectedToken && String(opt.chainId) === selectedChainId
8383
- ) ?? null,
8384
- [sourceOptions, selectedToken, selectedChainId]
8385
- );
8386
- const tokensForSelectedChain = react.useMemo(() => {
8387
- if (!selectedChainId) return null;
8388
- return new Set(
8389
- (sourceOptions ?? []).filter((opt) => String(opt.chainId) === selectedChainId).map((opt) => opt.tokenSymbol)
8390
- );
8391
- }, [sourceOptions, selectedChainId]);
8392
- const chainsForSelectedToken = react.useMemo(() => {
8393
- if (!selectedToken) return null;
8394
- return new Set(
8395
- (sourceOptions ?? []).filter((opt) => opt.tokenSymbol === selectedToken).map((opt) => opt.chainId)
8396
- );
8397
- }, [sourceOptions, selectedToken]);
8398
- const createSession = react.useCallback(async (option) => {
8399
- if (!merchantAuthorization) return;
8400
- setLoading(true);
8401
- setError(null);
8402
- try {
8403
- if (mock) {
8404
- const mockSession = {
8405
- sessionId: `mock-${Date.now()}`,
8406
- idempotencyKey: idempotencyKey ?? `mock-idem-${Date.now()}`,
8407
- merchantId: "mock-merchant",
8408
- destination,
8409
- source: {
8410
- chainId: option.chainId,
8411
- tokenAddress: option.tokenAddress,
8412
- tokenSymbol: option.tokenSymbol
8413
- },
8414
- refundTo: null,
8415
- depositAddress: "0x" + "a1b2c3d4e5f6".repeat(3).slice(0, 40),
8416
- requestId: `mock-req-${Date.now()}`,
8417
- status: "awaiting_deposit",
8418
- minAmountUsd: option.minAmountUsd,
8419
- quoteValidUntil: new Date(Date.now() + 36e5).toISOString(),
8420
- depositTxHashes: [],
8421
- destinationTxHash: null,
8422
- refundTxHashes: [],
8423
- deliveredAmountUsd: null,
8424
- errorCode: null,
8425
- errorMessage: null,
8426
- slippage: null,
8427
- createDate: (/* @__PURE__ */ new Date()).toISOString(),
8428
- updateDate: (/* @__PURE__ */ new Date()).toISOString()
8429
- };
8430
- setSession(mockSession);
8431
- } else {
8432
- const created = await createManualTransfer(apiBaseUrl, {
8433
- merchantAuthorization,
8434
- destination,
8435
- idempotencyKey,
8436
- source: {
8437
- chainId: option.chainId,
8438
- tokenAddress: option.tokenAddress
8439
- }
8440
- });
8441
- setSession(created);
8442
- }
8443
- } catch (err) {
8444
- setError(err instanceof Error ? err.message : String(err));
8445
- } finally {
8446
- setLoading(false);
8447
- }
8448
- }, [apiBaseUrl, destination, idempotencyKey, merchantAuthorization, mock]);
8449
- const resetSessionForNewSelection = react.useCallback(() => {
8450
- setSession(null);
8451
- lastCreatedOptionRef.current = null;
8452
- }, []);
8453
- const selectToken = react.useCallback((value) => {
8454
- setSelectedToken(value);
8455
- resetSessionForNewSelection();
8456
- const pairValid = (sourceOptions ?? []).some(
8457
- (opt) => opt.tokenSymbol === value && String(opt.chainId) === selectedChainId
8458
- );
8459
- if (!pairValid) {
8460
- const firstChain = (sourceOptions ?? []).find(
8461
- (opt) => opt.tokenSymbol === value
8462
- );
8463
- setSelectedChainId(
8464
- firstChain ? String(firstChain.chainId) : ""
8465
- );
8466
- }
8467
- }, [resetSessionForNewSelection, selectedChainId, sourceOptions]);
8468
- const selectChainId = react.useCallback((value) => {
8469
- setSelectedChainId(value);
8470
- resetSessionForNewSelection();
8471
- const pairValid = (sourceOptions ?? []).some(
8472
- (opt) => opt.tokenSymbol === selectedToken && String(opt.chainId) === value
8473
- );
8474
- if (!pairValid) {
8475
- const firstToken = (sourceOptions ?? []).find(
8476
- (opt) => String(opt.chainId) === value
8477
- );
8478
- setSelectedToken(
8479
- firstToken ? firstToken.tokenSymbol : ""
8480
- );
8481
- }
8482
- }, [resetSessionForNewSelection, selectedToken, sourceOptions]);
8483
- react.useEffect(() => {
8484
- if (!selectedOption) return;
8485
- const optionKey = `${selectedOption.chainId}:${selectedOption.tokenAddress}`;
8486
- if (lastCreatedOptionRef.current === optionKey) return;
8487
- lastCreatedOptionRef.current = optionKey;
8488
- void createSession(selectedOption);
8489
- }, [selectedOption, createSession]);
8490
- const screen = screenForSession(session);
8491
- const copyDepositAddress = react.useCallback(async (address) => {
8492
- try {
8493
- await navigator.clipboard.writeText(address);
8494
- } catch {
8495
- const textarea = document.createElement("textarea");
8496
- textarea.value = address;
8497
- textarea.setAttribute("readonly", "");
8498
- textarea.style.position = "absolute";
8499
- textarea.style.opacity = "0";
8500
- document.body.appendChild(textarea);
8501
- textarea.select();
8502
- try {
8503
- document.execCommand("copy");
8504
- } finally {
8505
- document.body.removeChild(textarea);
8691
+ };
8692
+ case "SYNC_PRIVY_SESSION":
8693
+ if (action.ready && !action.authenticated) {
8694
+ return {
8695
+ ...clearAuthenticatedSessionState(state),
8696
+ privyReady: true,
8697
+ privyAuthenticated: false
8698
+ };
8506
8699
  }
8700
+ return {
8701
+ ...state,
8702
+ privyReady: action.ready,
8703
+ privyAuthenticated: action.authenticated,
8704
+ ...action.authenticated ? { loginRequested: false } : {}
8705
+ };
8706
+ case "SYNC_AMOUNT":
8707
+ return {
8708
+ ...state,
8709
+ amount: action.amount,
8710
+ requireAmountEntry: action.amount === "" ? state.requireAmountEntry : false
8711
+ };
8712
+ case "SET_AMOUNT_INPUT":
8713
+ return { ...state, amount: action.value };
8714
+ case "FINALIZE_AMOUNT":
8715
+ return { ...state, requireAmountEntry: false };
8716
+ case "PAGE_RESUMED":
8717
+ return { ...state, lastResumedAt: Date.now() };
8718
+ // ── Setup deposit (combined first-time flow) ────────────────
8719
+ case "SET_SETUP_DEPOSIT_AMOUNT":
8720
+ return { ...state, setupDepositAmount: action.amount };
8721
+ case "SET_SETUP_DEPOSIT_TOKEN":
8722
+ return {
8723
+ ...state,
8724
+ setupDepositToken: {
8725
+ symbol: action.symbol,
8726
+ chainName: action.chainName,
8727
+ ...action.walletId ? { walletId: action.walletId } : {},
8728
+ ...action.tokenAddress ? { tokenAddress: action.tokenAddress } : {},
8729
+ ...action.chainId != null ? { chainId: action.chainId } : {}
8730
+ }
8731
+ };
8732
+ case "CLEAR_SETUP_DEPOSIT_TOKEN":
8733
+ return { ...state, setupDepositToken: null };
8734
+ case "CONFIRM_SETUP_DEPOSIT":
8735
+ return { ...state, setupDepositConfirmed: true };
8736
+ default:
8737
+ return state;
8738
+ }
8739
+ }
8740
+
8741
+ // src/setupDepositConfirmation.ts
8742
+ function deriveEffectiveSetupSource(setupDepositToken, setupSelectedSourceOption) {
8743
+ if (setupDepositToken) return setupDepositToken;
8744
+ if (setupSelectedSourceOption) {
8745
+ return {
8746
+ symbol: setupSelectedSourceOption.tokenSymbol,
8747
+ chainName: setupSelectedSourceOption.chainName,
8748
+ walletId: setupSelectedSourceOption.walletId,
8749
+ tokenAddress: setupSelectedSourceOption.tokenAddress,
8750
+ chainId: setupSelectedSourceOption.chainId != null ? Number(setupSelectedSourceOption.chainId) : void 0
8751
+ };
8752
+ }
8753
+ return null;
8754
+ }
8755
+ function planConfirmSetupDeposit(input) {
8756
+ const effective = deriveEffectiveSetupSource(
8757
+ input.setupDepositToken,
8758
+ input.setupSelectedSourceOption
8759
+ );
8760
+ if (!effective) {
8761
+ return { kind: "error", error: "Select a source token before continuing." };
8762
+ }
8763
+ if (!effective.walletId) {
8764
+ return {
8765
+ kind: "error",
8766
+ error: "Selected source is not ready yet. Wait for wallet discovery to finish."
8767
+ };
8768
+ }
8769
+ const actions = [
8770
+ { type: "SET_ERROR", error: null },
8771
+ {
8772
+ type: "SET_SETUP_DEPOSIT_TOKEN",
8773
+ symbol: effective.symbol,
8774
+ chainName: effective.chainName,
8775
+ walletId: effective.walletId,
8776
+ tokenAddress: effective.tokenAddress,
8777
+ chainId: effective.chainId
8507
8778
  }
8508
- setCopiedAddress(address);
8509
- window.setTimeout(() => setCopiedAddress((cur) => cur === address ? null : cur), 1500);
8510
- }, []);
8511
- const backToSourceSelector = react.useCallback(() => {
8512
- setSession(null);
8513
- setCopiedAddress(null);
8514
- setError(null);
8515
- lastCreatedOptionRef.current = null;
8516
- if (selectedOption) {
8517
- void createSession(selectedOption);
8518
- }
8519
- }, [selectedOption, createSession]);
8779
+ ];
8780
+ if (input.selectedAccountId) {
8781
+ actions.push({
8782
+ type: "SELECT_ACCOUNT",
8783
+ accountId: input.selectedAccountId,
8784
+ walletId: effective.walletId
8785
+ });
8786
+ actions.push({
8787
+ type: "SELECT_TOKEN",
8788
+ walletId: effective.walletId,
8789
+ tokenSymbol: effective.symbol
8790
+ });
8791
+ }
8792
+ actions.push({ type: "SET_SETUP_FLOW_SCREEN", screen: "one-tap-setup" });
8793
+ actions.push({ type: "CONFIRM_SETUP_DEPOSIT" });
8520
8794
  return {
8521
- sourceOptions,
8522
- loadingSources,
8523
- selectedToken,
8524
- selectedChainId,
8525
- session,
8526
- loading,
8527
- error,
8528
- qrReady,
8529
- copiedAddress,
8530
- tokenChoices,
8531
- chainChoices,
8532
- tokenLogoUriBySymbol,
8533
- selectedOption,
8534
- tokensForSelectedChain,
8535
- chainsForSelectedToken,
8536
- screen,
8537
- sessionFeeCopy,
8538
- depositAddress,
8539
- createSession,
8540
- copyDepositAddress,
8541
- backToSourceSelector,
8542
- resetSessionForNewSelection,
8543
- advanceMockStatus,
8544
- selectToken,
8545
- selectChainId
8795
+ kind: "proceed",
8796
+ actions,
8797
+ resolveSource: {
8798
+ chainName: effective.chainName,
8799
+ tokenSymbol: effective.symbol
8800
+ }
8546
8801
  };
8547
8802
  }
8548
- var MOCK_STATUSES = [
8549
- "awaiting_deposit",
8550
- "deposit_received",
8551
- "routing",
8552
- "completed",
8553
- "failed"
8554
- ];
8555
- function DevMockPanel({
8556
- status,
8557
- onSetStatus,
8558
- hasSession
8559
- }) {
8560
- const [collapsed, setCollapsed] = react.useState(true);
8561
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: devPanelContainerStyle, children: [
8562
- /* @__PURE__ */ jsxRuntime.jsx(
8563
- "button",
8564
- {
8565
- type: "button",
8566
- onClick: () => setCollapsed((c) => !c),
8567
- style: devPanelToggleStyle,
8568
- children: collapsed ? "DEV" : "\u2715"
8569
- }
8570
- ),
8571
- !collapsed && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: devPanelBodyStyle, children: [
8572
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: devPanelLabelStyle, children: hasSession ? `Status: ${status}` : "No session" }),
8573
- MOCK_STATUSES.map((s) => /* @__PURE__ */ jsxRuntime.jsx(
8574
- "button",
8575
- {
8576
- type: "button",
8577
- disabled: !hasSession || status === s,
8578
- onClick: () => onSetStatus(s),
8579
- style: devPanelButtonStyle(status === s),
8580
- children: s.replace(/_/g, " ")
8581
- },
8582
- s
8583
- ))
8584
- ] })
8585
- ] });
8586
- }
8587
- var devPanelContainerStyle = {
8588
- position: "fixed",
8589
- bottom: 16,
8590
- right: 16,
8591
- zIndex: 99999,
8592
- display: "flex",
8593
- flexDirection: "column",
8594
- alignItems: "flex-end",
8595
- gap: 8,
8596
- fontFamily: "monospace",
8597
- fontSize: 12
8598
- };
8599
- var devPanelToggleStyle = {
8600
- background: "#1a1a1a",
8601
- border: "1px solid #333",
8602
- borderRadius: 8,
8603
- color: "#0f0",
8604
- cursor: "pointer",
8605
- fontSize: 11,
8606
- fontFamily: "monospace",
8607
- fontWeight: 700,
8608
- height: 36,
8609
- width: 36,
8610
- display: "flex",
8611
- alignItems: "center",
8612
- justifyContent: "center"
8613
- };
8614
- var devPanelBodyStyle = {
8615
- background: "#1a1a1a",
8616
- border: "1px solid #333",
8617
- borderRadius: 10,
8618
- display: "flex",
8619
- flexDirection: "column",
8620
- gap: 4,
8621
- padding: 10,
8622
- minWidth: 160
8623
- };
8624
- var devPanelLabelStyle = {
8625
- color: "#888",
8626
- fontSize: 10,
8627
- marginBottom: 4,
8628
- textTransform: "uppercase",
8629
- letterSpacing: "0.05em"
8630
- };
8631
- var devPanelButtonStyle = (active) => ({
8632
- background: active ? "#0f0" : "#2a2a2a",
8633
- border: "1px solid #444",
8634
- borderRadius: 6,
8635
- color: active ? "#000" : "#ccc",
8636
- cursor: active ? "default" : "pointer",
8637
- fontFamily: "monospace",
8638
- fontSize: 11,
8639
- fontWeight: active ? 700 : 400,
8640
- padding: "6px 10px",
8641
- textAlign: "left",
8642
- textTransform: "capitalize"
8643
- });
8644
8803
  function ScreenLayout({ children, footer }) {
8645
8804
  const { tokens, theme, isMobileApp } = useBlinkConfig();
8646
8805
  const isRedesign = theme.endsWith("New");
@@ -10355,16 +10514,21 @@ function DepositQrCodeImpl({
10355
10514
  depToken
10356
10515
  }) {
10357
10516
  const { tokens } = useBlinkConfig();
10358
- const [dataUrl, setDataUrl] = react.useState(null);
10359
10517
  const uri = formatDepositUri(address, chainId, depToken);
10518
+ const dark = tokens.text;
10519
+ const light = tokens.bg;
10520
+ const [dataUrl, setDataUrl] = react.useState(
10521
+ () => getCachedQrDataUrl(uri, { dark, light })
10522
+ );
10360
10523
  react.useEffect(() => {
10524
+ const cached = getCachedQrDataUrl(uri, { dark, light });
10525
+ if (cached) {
10526
+ setDataUrl(cached);
10527
+ return;
10528
+ }
10361
10529
  let cancelled = false;
10362
- QRCode__namespace.toDataURL(uri, {
10363
- errorCorrectionLevel: "H",
10364
- margin: 1,
10365
- width: 203,
10366
- color: { dark: tokens.text, light: tokens.bg }
10367
- }).then((url) => {
10530
+ setDataUrl(null);
10531
+ getOrRenderQrDataUrl(uri, { dark, light }).then((url) => {
10368
10532
  if (!cancelled) setDataUrl(url);
10369
10533
  }).catch(() => {
10370
10534
  if (!cancelled) setDataUrl(null);
@@ -10372,7 +10536,7 @@ function DepositQrCodeImpl({
10372
10536
  return () => {
10373
10537
  cancelled = true;
10374
10538
  };
10375
- }, [uri]);
10539
+ }, [uri, dark, light]);
10376
10540
  return /* @__PURE__ */ jsxRuntime.jsx("div", { style: qrFrameStyle(tokens.bgCardTranslucent), children: dataUrl ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: qrContainerStyle, children: [
10377
10541
  /* @__PURE__ */ jsxRuntime.jsx("img", { src: dataUrl, alt: "Deposit QR code", style: qrImageStyle }),
10378
10542
  /* @__PURE__ */ jsxRuntime.jsx("img", { src: BLINK_QR_LOGO, alt: "", style: qrLogoStyle })
@@ -11460,7 +11624,7 @@ function EnterAmountScreen({
11460
11624
  )
11461
11625
  ] }),
11462
11626
  children: [
11463
- /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: headerRight }),
11627
+ /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { title: "Deposit", right: headerRight }),
11464
11628
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle4(isDesktop), children: [
11465
11629
  isDesktop ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: desktopHeroRowStyle(heroColor, getDesktopHeroFontSize(value)), children: [
11466
11630
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", style: dollarStyle2(isZero), children: "$" }),
@@ -12801,7 +12965,8 @@ function SetupScreen({
12801
12965
  onLogout,
12802
12966
  loading,
12803
12967
  loadingShimmersEnabled = false,
12804
- error
12968
+ error,
12969
+ selectedTokenSymbol
12805
12970
  }) {
12806
12971
  const { tokens } = useBlinkConfig();
12807
12972
  const [selectedPreset, setSelectedPreset] = react.useState(DEFAULT_MAX);
@@ -12910,7 +13075,8 @@ function SetupScreen({
12910
13075
  style: illustrationStyle3
12911
13076
  }
12912
13077
  ),
12913
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(tokens.text), children: "Next time deposit\nUSDC in one tap" }),
13078
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle7(tokens.text), children: `Next time deposit
13079
+ ${selectedTokenSymbol ?? "USDC"} in one tap` }),
12914
13080
  /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle5(tokens.textSecondary), children: "Set a cap for passkey deposits.\nYou always stay in control." }),
12915
13081
  error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle5(tokens), children: error }),
12916
13082
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: chipsRowStyle, children: PRESETS.map(({ label, value }) => {
@@ -12938,8 +13104,8 @@ var contentStyle9 = {
12938
13104
  alignItems: "center",
12939
13105
  justifyContent: "center",
12940
13106
  textAlign: "center",
12941
- gap: 16,
12942
- padding: "48px 8px 8px"
13107
+ gap: 12,
13108
+ padding: "8px 0"
12943
13109
  };
12944
13110
  var shimmerContentStyle = {
12945
13111
  display: "flex",
@@ -13310,7 +13476,7 @@ function SetupDepositScreen({
13310
13476
  NotificationBanner,
13311
13477
  {
13312
13478
  variant: "negative",
13313
- title: "Deposit amount exceeds balance",
13479
+ title: "Deposit amount exceeds stablecoin balance",
13314
13480
  description: "Pick another asset or add more funds to your wallet"
13315
13481
  }
13316
13482
  ) : /* @__PURE__ */ jsxRuntime.jsx(NotificationBanner, { title: "Something went wrong", description: error }) })
@@ -13928,7 +14094,7 @@ function DepositScreen({
13928
14094
  NotificationBanner,
13929
14095
  {
13930
14096
  variant: "negative",
13931
- title: "Deposit amount exceeds balance",
14097
+ title: "Deposit amount exceeds stablecoin balance",
13932
14098
  description: "Pick another asset or add more funds to your wallet"
13933
14099
  }
13934
14100
  ) : needsAuthorization ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -14850,12 +15016,12 @@ function DepositAddressScreen({
14850
15016
  ),
14851
15017
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle16, children: [
14852
15018
  /* @__PURE__ */ jsxRuntime.jsx("h2", { style: depositTitleStyle(tokens.text), children: "Deposit Address" }),
14853
- qrReady && session ? /* @__PURE__ */ jsxRuntime.jsx(
15019
+ qrReady && session && selectedOption ? /* @__PURE__ */ jsxRuntime.jsx(
14854
15020
  DepositQrCode,
14855
15021
  {
14856
15022
  address: session.depositAddress,
14857
- chainId: session.source.chainId,
14858
- depToken: session.source.tokenAddress
15023
+ chainId: selectedOption.chainId,
15024
+ depToken: selectedOption.tokenAddress
14859
15025
  }
14860
15026
  ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
14861
15027
  /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
@@ -15874,12 +16040,7 @@ var errorBannerStyle8 = (themeTokens) => ({
15874
16040
  textAlign: "left"
15875
16041
  });
15876
16042
  function ManualTransferFlow({
15877
- destination,
15878
- merchantAuthorization,
15879
- idempotencyKey,
15880
- mock = false,
15881
- onComplete,
15882
- onError,
16043
+ hasMerchantAuthorization,
15883
16044
  onCreatePasskey,
15884
16045
  createPasskeyLoading = false,
15885
16046
  createPasskeyError = null,
@@ -15908,17 +16069,9 @@ function ManualTransferFlow({
15908
16069
  depositAddress,
15909
16070
  copyDepositAddress,
15910
16071
  backToSourceSelector,
15911
- advanceMockStatus,
15912
16072
  selectToken,
15913
16073
  selectChainId
15914
- } = useManualTransferSession({
15915
- destination,
15916
- merchantAuthorization,
15917
- idempotencyKey,
15918
- mock,
15919
- onComplete,
15920
- onError
15921
- });
16074
+ } = useManualTransferSessionContext();
15922
16075
  const [passkeyCreated, setPasskeyCreated] = react.useState(false);
15923
16076
  const prevPasskeyLoadingRef = react.useRef(false);
15924
16077
  react.useEffect(() => {
@@ -15927,9 +16080,8 @@ function ManualTransferFlow({
15927
16080
  }
15928
16081
  prevPasskeyLoadingRef.current = createPasskeyLoading;
15929
16082
  }, [createPasskeyLoading, createPasskeyError]);
15930
- const DEV_MOCK_STATUS = mock;
15931
16083
  let screenContent = null;
15932
- if (!merchantAuthorization) {
16084
+ if (!hasMerchantAuthorization) {
15933
16085
  screenContent = /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { footer: /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onBack, children: "Back" }), children: [
15934
16086
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { onBack }),
15935
16087
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle20, children: [
@@ -15999,17 +16151,7 @@ function ManualTransferFlow({
15999
16151
  if (!screenContent) {
16000
16152
  return null;
16001
16153
  }
16002
- return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
16003
- screenContent,
16004
- DEV_MOCK_STATUS && merchantAuthorization && /* @__PURE__ */ jsxRuntime.jsx(
16005
- DevMockPanel,
16006
- {
16007
- status: session?.status ?? null,
16008
- onSetStatus: advanceMockStatus,
16009
- hasSession: !!session
16010
- }
16011
- )
16012
- ] });
16154
+ return screenContent;
16013
16155
  }
16014
16156
  var contentStyle20 = {
16015
16157
  alignItems: "center",
@@ -16309,6 +16451,7 @@ function buildSetupDepositScreenProps({
16309
16451
  function buildSetupScreenProps({
16310
16452
  flow,
16311
16453
  remote,
16454
+ derived,
16312
16455
  forms,
16313
16456
  handlers
16314
16457
  }) {
@@ -16317,11 +16460,12 @@ function buildSetupScreenProps({
16317
16460
  const waitingForDesktopWalletConnection = flow.isDesktop && remote.authExecutorExecuting && remote.pendingOneTapSetup == null && (remote.authExecutorCurrentAction == null || remote.authExecutorCurrentAction.type === "OPEN_PROVIDER");
16318
16461
  return {
16319
16462
  onSetupOneTap: handlers.onSetupOneTap,
16320
- onBack: handlers.onBackFromSubflow,
16321
- onLogout: handlers.onLogout,
16463
+ onBack: flow.isDesktop ? handlers.onBackFromSubflow : void 0,
16464
+ onLogout: flow.isDesktop ? handlers.onLogout : void 0,
16322
16465
  loading: savingOneTapLimit,
16323
16466
  loadingShimmersEnabled: waitingForDesktopWalletConnection,
16324
- error: state.error
16467
+ error: state.error,
16468
+ selectedTokenSymbol: derived.selectedSource?.token.symbol
16325
16469
  };
16326
16470
  }
16327
16471
  function buildConfirmSignScreenProps({
@@ -16598,12 +16742,7 @@ function StepRendererContent({
16598
16742
  return /* @__PURE__ */ jsxRuntime.jsx(
16599
16743
  ManualTransferFlow,
16600
16744
  {
16601
- destination: flow.destination,
16602
- merchantAuthorization: flow.merchantAuthorization,
16603
- idempotencyKey: flow.idempotencyKey,
16604
- mock: flow.mock,
16605
- onComplete: flow.onComplete,
16606
- onError: flow.onError,
16745
+ hasMerchantAuthorization: flow.merchantAuthorization != null,
16607
16746
  onCreatePasskey: handlers.onSignupWithPasskey,
16608
16747
  createPasskeyLoading: flow.passkeyLoading,
16609
16748
  createPasskeyError: flow.state.error,
@@ -18821,8 +18960,11 @@ function usePasskeyCheckEffect(deps) {
18821
18960
  setupAccountIdRef,
18822
18961
  reauthSessionIdRef,
18823
18962
  reauthTokenRef,
18824
- pollingTransferIdRef
18963
+ pollingTransferIdRef,
18964
+ reloadAccounts
18825
18965
  } = deps;
18966
+ const reloadAccountsRef = react.useRef(reloadAccounts);
18967
+ reloadAccountsRef.current = reloadAccounts;
18826
18968
  const { getAccessToken: privyGetAccessToken, user } = reactAuth.usePrivy();
18827
18969
  const effectiveGetAccessToken = deps.getAccessToken ?? privyGetAccessToken;
18828
18970
  const onCompleteRef = react.useRef(deps.onComplete);
@@ -18952,6 +19094,11 @@ function usePasskeyCheckEffect(deps) {
18952
19094
  mobileSetupFlowRef.current = false;
18953
19095
  setupAccountIdRef.current = null;
18954
19096
  clearMobileFlowState();
19097
+ try {
19098
+ await reloadAccountsRef.current();
19099
+ } catch {
19100
+ }
19101
+ dispatch({ type: "MOBILE_SETUP_COMPLETE" });
18955
19102
  return;
18956
19103
  }
18957
19104
  if (persisted.accountId && !persisted.transferId) {
@@ -18960,7 +19107,14 @@ function usePasskeyCheckEffect(deps) {
18960
19107
  const session = await fetchAuthorizationSession(apiBaseUrl, persisted.sessionId);
18961
19108
  if (cancelled) return;
18962
19109
  if (session.status === "AUTHORIZED") {
19110
+ mobileSetupFlowRef.current = false;
19111
+ setupAccountIdRef.current = null;
18963
19112
  clearMobileFlowState();
19113
+ try {
19114
+ await reloadAccountsRef.current();
19115
+ } catch {
19116
+ }
19117
+ dispatch({ type: "MOBILE_SETUP_COMPLETE" });
18964
19118
  return;
18965
19119
  }
18966
19120
  } catch {
@@ -19425,25 +19579,24 @@ function useMobilePollingEffect(deps) {
19425
19579
  dispatch({ type: "MOBILE_SETUP_COMPLETE" });
19426
19580
  };
19427
19581
  const pollWalletActive = async () => {
19582
+ if (setupSessionId) {
19583
+ try {
19584
+ const session = await fetchAuthorizationSession(apiBaseUrl, setupSessionId);
19585
+ if (cancelled) return;
19586
+ if (session.status === "AUTHORIZED") {
19587
+ await completeSetup();
19588
+ return;
19589
+ }
19590
+ } catch {
19591
+ }
19592
+ }
19428
19593
  try {
19429
19594
  const token = await getAccessTokenRef.current();
19430
19595
  if (!token || cancelled) return;
19431
19596
  const acct = await fetchAccount(apiBaseUrl, token, accountId, credentialId);
19432
19597
  if (cancelled) return;
19433
- const hasActive = acct.wallets.some((w) => w.status === "ACTIVE");
19434
- if (hasActive) {
19598
+ if (acct.wallets.some((w) => w.status === "ACTIVE")) {
19435
19599
  await completeSetup();
19436
- return;
19437
- }
19438
- if (setupSessionId) {
19439
- try {
19440
- const session = await fetchAuthorizationSession(apiBaseUrl, setupSessionId);
19441
- if (cancelled) return;
19442
- if (session.status === "AUTHORIZED") {
19443
- await completeSetup();
19444
- }
19445
- } catch {
19446
- }
19447
19600
  }
19448
19601
  } catch {
19449
19602
  }
@@ -19878,7 +20031,6 @@ function BlinkPayment(props) {
19878
20031
  function BlinkPaymentInner({
19879
20032
  destination,
19880
20033
  initialScreen,
19881
- mock,
19882
20034
  onComplete,
19883
20035
  onError,
19884
20036
  useWalletConnector: useWalletConnectorProp,
@@ -19967,6 +20119,7 @@ function BlinkPaymentInner({
19967
20119
  minTransferAmountUsd: effectiveMinDepositAmountUsd
19968
20120
  });
19969
20121
  const feeEstimateEnabled = effectiveAuthenticated && (state.phase.step === "deposit" || state.phase.step === "wallet-setup" || state.phase.step === "one-tap-setup");
20122
+ const manualTransferActive = screenForPhase(state.phase) === "manual-transfer";
19970
20123
  const setupSelectedSourceOption = react.useMemo(() => {
19971
20124
  const options = sourceSelection.pendingSelectSourceAction?.metadata?.options ?? [];
19972
20125
  return resolveSelectSourceOption(
@@ -20113,13 +20266,14 @@ function BlinkPaymentInner({
20113
20266
  idempotencyKey
20114
20267
  });
20115
20268
  const accountSwitchSessionId = state.setupAuthorizationSessionId;
20269
+ const accountSwitchListenerArmed = accountSwitchSessionId != null && state.desktopWait == null;
20116
20270
  const handleProviderWalletAccountSwitch = provider.handleWalletAccountSwitch;
20117
20271
  const onWalletAccountChanged = react.useCallback(async (change) => {
20118
20272
  if (!accountSwitchSessionId) return;
20119
20273
  await handleProviderWalletAccountSwitch(change, accountSwitchSessionId);
20120
20274
  }, [handleProviderWalletAccountSwitch, accountSwitchSessionId]);
20121
20275
  useWalletAccountSwitchEffect({
20122
- enabled: accountSwitchSessionId != null,
20276
+ enabled: accountSwitchListenerArmed,
20123
20277
  isDesktop,
20124
20278
  onAccountChanged: onWalletAccountChanged
20125
20279
  });
@@ -20215,6 +20369,7 @@ function BlinkPaymentInner({
20215
20369
  reauthTokenRef: mobileFlowRefs.reauthTokenRef,
20216
20370
  pollingTransferIdRef: transfer.pollingTransferIdRef,
20217
20371
  handleAuthorizedMobileReturn: mobileFlow.handleAuthorizedMobileReturn,
20372
+ reloadAccounts: transfer.reloadAccounts,
20218
20373
  onComplete,
20219
20374
  getAccessToken: effectiveGetAccessToken,
20220
20375
  pendingTransferFlowKey
@@ -20381,15 +20536,18 @@ function BlinkPaymentInner({
20381
20536
  dispatch,
20382
20537
  orchestrator
20383
20538
  ]);
20384
- const handleBackFromSetupDeposit = react.useCallback(() => {
20539
+ const handleBackFromSetupDeposit = react.useCallback(async () => {
20385
20540
  orchestrator.cancelPendingFlow();
20386
20541
  authExecutor.cancelPendingExecution();
20387
20542
  clearScreenErrors();
20543
+ if (isDesktop) {
20544
+ await revokeAndDisconnectActiveWagmiConnector(wagmiConfig);
20545
+ }
20388
20546
  dispatch({ type: "RESTORE_SELECTION" });
20389
20547
  dispatch({ type: "CLEAR_SETUP_DEPOSIT_TOKEN" });
20390
20548
  dispatch({ type: "SET_SETUP_FLOW_SCREEN", screen: null });
20391
20549
  dispatch({ type: "SET_USER_INTENT", intent: { step: "wallet-picker", reason: "switch" } });
20392
- }, [authExecutor, clearScreenErrors, orchestrator, dispatch]);
20550
+ }, [authExecutor, clearScreenErrors, orchestrator, dispatch, isDesktop, wagmiConfig]);
20393
20551
  const handleAuthorizationRetry = react.useCallback(() => {
20394
20552
  void (async () => {
20395
20553
  try {
@@ -20429,6 +20587,11 @@ function BlinkPaymentInner({
20429
20587
  clearScreenErrors();
20430
20588
  dispatch({ type: "SET_SETUP_DEPOSIT_AMOUNT", amount });
20431
20589
  dispatch({ type: "SET_USER_INTENT", intent: { step: "wallet-picker", reason: "switch" } });
20590
+ void (async () => {
20591
+ await revokeAndDisconnectActiveWagmiConnector(wagmiConfig);
20592
+ await authExecutor.resetWalletConnect().catch(() => {
20593
+ });
20594
+ })();
20432
20595
  },
20433
20596
  onBackFromSetupDeposit: handleBackFromSetupDeposit,
20434
20597
  onBackFromSubflow: () => {
@@ -20477,65 +20640,76 @@ function BlinkPaymentInner({
20477
20640
  handleSetDepositToken,
20478
20641
  handleConfirmSetupDeposit,
20479
20642
  handleBackFromSetupDeposit,
20480
- handleAuthorizationRetry
20643
+ handleAuthorizationRetry,
20644
+ wagmiConfig
20481
20645
  ]);
20482
20646
  return /* @__PURE__ */ jsxRuntime.jsx(EffectiveDepositAmountProvider, { value: effectiveDepositAmount, children: /* @__PURE__ */ jsxRuntime.jsx(
20483
- StepRenderer,
20647
+ ManualTransferSessionProvider,
20484
20648
  {
20485
- flow: {
20486
- state,
20487
- authenticated: effectiveAuthenticated,
20488
- passkeyLoading: auth.passkeyLoginStatus !== "initial" && auth.passkeyLoginStatus !== "done" && auth.passkeyLoginStatus !== "error" || auth.passkeySignupStatus !== "initial" && auth.passkeySignupStatus !== "done" && auth.passkeySignupStatus !== "error" || auth.passkeySignupPopupActive,
20489
- isDesktop,
20490
- isMobileApp: isMobileApp ?? false,
20491
- merchantName,
20492
- onBack,
20493
- onDismiss,
20494
- depositAmount,
20495
- effectiveDepositAmount,
20496
- minTransferAmountUsd,
20497
- destination,
20498
- merchantAuthorization,
20499
- idempotencyKey,
20500
- mock,
20501
- onComplete,
20502
- onError
20503
- },
20504
- remote: {
20505
- pollingTransfer: polling.transfer,
20506
- pollingError: polling.error,
20507
- authExecutorError: authExecutor.error,
20508
- authExecutorExecuting: authExecutor.executing,
20509
- authExecutorCurrentAction: authExecutor.currentAction,
20510
- pendingOneTapSetup: orchestrator.pendingOneTapAction,
20511
- setupAuthorizationComplete,
20512
- transferSigningSigning: transferSigning.signing,
20513
- transferSigningError: transferSigning.error,
20514
- transferSigningPasskeyDismissed: transferSigning.passkeyDismissed,
20515
- pendingSelectSource: orchestrator.pendingSelectSourceAction
20516
- },
20517
- derived: {
20518
- pendingConnections: derived.pendingConnections,
20519
- depositEligibleAccounts: derived.depositEligibleAccounts,
20520
- sourceName: derived.sourceName,
20521
- maxSourceBalance: derived.maxSourceBalance,
20522
- tokenCount: derived.tokenCount,
20523
- selectedAccount: derived.selectedAccount,
20524
- selectedSource: derived.selectedSource,
20525
- selectSourceChoices: sourceSelection.selectSourceChoices,
20526
- selectSourceRecommended: sourceSelection.selectSourceRecommended,
20527
- selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance
20528
- },
20529
- forms: {
20530
- selectSourceChainName: sourceSelection.selectSourceChainName,
20531
- selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
20532
- savingOneTapLimit: oneTapSetup.savingOneTapLimit,
20533
- depositQuoteId: depositFee.quoteId,
20534
- depositQuoteFee: depositFee.quoteFee,
20535
- depositQuoteLoading: depositFee.quoteLoading,
20536
- depositQuoteError: depositFee.quoteError
20537
- },
20538
- handlers
20649
+ destination,
20650
+ merchantAuthorization,
20651
+ idempotencyKey,
20652
+ onComplete,
20653
+ onError,
20654
+ pollEnabled: manualTransferActive,
20655
+ children: /* @__PURE__ */ jsxRuntime.jsx(
20656
+ StepRenderer,
20657
+ {
20658
+ flow: {
20659
+ state,
20660
+ authenticated: effectiveAuthenticated,
20661
+ passkeyLoading: auth.passkeyLoginStatus !== "initial" && auth.passkeyLoginStatus !== "done" && auth.passkeyLoginStatus !== "error" || auth.passkeySignupStatus !== "initial" && auth.passkeySignupStatus !== "done" && auth.passkeySignupStatus !== "error" || auth.passkeySignupPopupActive,
20662
+ isDesktop,
20663
+ isMobileApp: isMobileApp ?? false,
20664
+ merchantName,
20665
+ onBack,
20666
+ onDismiss,
20667
+ depositAmount,
20668
+ effectiveDepositAmount,
20669
+ minTransferAmountUsd,
20670
+ destination,
20671
+ merchantAuthorization,
20672
+ idempotencyKey,
20673
+ onComplete,
20674
+ onError
20675
+ },
20676
+ remote: {
20677
+ pollingTransfer: polling.transfer,
20678
+ pollingError: polling.error,
20679
+ authExecutorError: authExecutor.error,
20680
+ authExecutorExecuting: authExecutor.executing,
20681
+ authExecutorCurrentAction: authExecutor.currentAction,
20682
+ pendingOneTapSetup: orchestrator.pendingOneTapAction,
20683
+ setupAuthorizationComplete,
20684
+ transferSigningSigning: transferSigning.signing,
20685
+ transferSigningError: transferSigning.error,
20686
+ transferSigningPasskeyDismissed: transferSigning.passkeyDismissed,
20687
+ pendingSelectSource: orchestrator.pendingSelectSourceAction
20688
+ },
20689
+ derived: {
20690
+ pendingConnections: derived.pendingConnections,
20691
+ depositEligibleAccounts: derived.depositEligibleAccounts,
20692
+ sourceName: derived.sourceName,
20693
+ maxSourceBalance: derived.maxSourceBalance,
20694
+ tokenCount: derived.tokenCount,
20695
+ selectedAccount: derived.selectedAccount,
20696
+ selectedSource: derived.selectedSource,
20697
+ selectSourceChoices: sourceSelection.selectSourceChoices,
20698
+ selectSourceRecommended: sourceSelection.selectSourceRecommended,
20699
+ selectSourceAvailableBalance: sourceSelection.selectSourceAvailableBalance
20700
+ },
20701
+ forms: {
20702
+ selectSourceChainName: sourceSelection.selectSourceChainName,
20703
+ selectSourceTokenSymbol: sourceSelection.selectSourceTokenSymbol,
20704
+ savingOneTapLimit: oneTapSetup.savingOneTapLimit,
20705
+ depositQuoteId: depositFee.quoteId,
20706
+ depositQuoteFee: depositFee.quoteFee,
20707
+ depositQuoteLoading: depositFee.quoteLoading,
20708
+ depositQuoteError: depositFee.quoteError
20709
+ },
20710
+ handlers
20711
+ }
20712
+ )
20539
20713
  }
20540
20714
  ) });
20541
20715
  }