@rhinestone/deposit-modal 0.17.0 → 0.18.0

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.
Files changed (61) hide show
  1. package/README.md +68 -0
  2. package/dist/{DepositModalReown-6CXXGQ7X.cjs → DepositModalReown-TVO3ZQQK.cjs} +17 -17
  3. package/dist/{DepositModalReown-ELDSTIME.mjs → DepositModalReown-VCY65DJX.mjs} +4 -4
  4. package/dist/WithdrawModal-GR3HLYXU.cjs +11 -0
  5. package/dist/{WithdrawModal-QRMDR5T4.mjs → WithdrawModal-RMCYJJ4S.mjs} +3 -3
  6. package/dist/bearer-transport-CVHNBgoV.d.cts +14 -0
  7. package/dist/bearer-transport-CVHNBgoV.d.ts +14 -0
  8. package/dist/{chunk-2W4BJ5I3.cjs → chunk-34JVJZEZ.cjs} +71 -71
  9. package/dist/{chunk-XCBDJPBD.mjs → chunk-3TEVMP76.mjs} +1 -1
  10. package/dist/chunk-ADEWTBSN.cjs +19 -0
  11. package/dist/{chunk-QMGEK623.mjs → chunk-AIZKGFVA.mjs} +468 -207
  12. package/dist/{chunk-H66WM5AO.mjs → chunk-EKC2IRQG.mjs} +180 -67
  13. package/dist/{chunk-XJMDCBHS.cjs → chunk-JV5OGG53.cjs} +629 -516
  14. package/dist/{chunk-QGNJFDEO.cjs → chunk-N3AAT6BS.cjs} +623 -362
  15. package/dist/{chunk-JCWJ6HW7.cjs → chunk-N6JJG57R.cjs} +1 -1
  16. package/dist/chunk-O4JZKKCZ.cjs +41 -0
  17. package/dist/chunk-PP4QE32S.mjs +19 -0
  18. package/dist/{chunk-ZHX4QROC.mjs → chunk-R2B6VD4E.mjs} +1 -1
  19. package/dist/chunk-X4EUXBHJ.mjs +41 -0
  20. package/dist/compliance.cjs +14 -0
  21. package/dist/compliance.d.cts +10 -0
  22. package/dist/compliance.d.ts +10 -0
  23. package/dist/compliance.mjs +14 -0
  24. package/dist/constants.cjs +3 -3
  25. package/dist/constants.d.cts +1 -1
  26. package/dist/constants.d.ts +1 -1
  27. package/dist/constants.mjs +3 -3
  28. package/dist/deposit.cjs +4 -4
  29. package/dist/deposit.d.cts +2 -2
  30. package/dist/deposit.d.ts +2 -2
  31. package/dist/deposit.mjs +3 -3
  32. package/dist/embed.cjs +16 -16
  33. package/dist/embed.d.cts +1 -1
  34. package/dist/embed.d.ts +1 -1
  35. package/dist/embed.mjs +8 -8
  36. package/dist/index.cjs +5 -5
  37. package/dist/index.d.cts +1 -1
  38. package/dist/index.d.ts +1 -1
  39. package/dist/index.mjs +4 -4
  40. package/dist/offramp.cjs +25 -0
  41. package/dist/offramp.d.cts +28 -0
  42. package/dist/offramp.d.ts +28 -0
  43. package/dist/offramp.mjs +25 -0
  44. package/dist/onramp.cjs +27 -0
  45. package/dist/onramp.d.cts +37 -0
  46. package/dist/onramp.d.ts +37 -0
  47. package/dist/onramp.mjs +27 -0
  48. package/dist/ramp-types-C6aUz4SZ.d.cts +43 -0
  49. package/dist/ramp-types-C6aUz4SZ.d.ts +43 -0
  50. package/dist/server.cjs +54 -2
  51. package/dist/server.d.cts +13 -1
  52. package/dist/server.d.ts +13 -1
  53. package/dist/server.mjs +52 -0
  54. package/dist/{types-B9SbZQIG.d.ts → types-CZYypODQ.d.ts} +3 -2
  55. package/dist/{types-SwGGf0eH.d.cts → types-CbUs4boj.d.cts} +3 -2
  56. package/dist/withdraw.cjs +4 -4
  57. package/dist/withdraw.d.cts +2 -2
  58. package/dist/withdraw.d.ts +2 -2
  59. package/dist/withdraw.mjs +3 -3
  60. package/package.json +35 -5
  61. package/dist/WithdrawModal-QB2U756L.cjs +0 -11
@@ -1,7 +1,3 @@
1
- import {
2
- MODAL_VERSION,
3
- serviceHeaders
4
- } from "./chunk-XCBDJPBD.mjs";
5
1
  import {
6
2
  CHAIN_DISPLAY_ORDER,
7
3
  HYPERCORE_CHAIN_ID,
@@ -36,6 +32,10 @@ import {
36
32
  toEvmCaip2,
37
33
  tokenIconUrl
38
34
  } from "./chunk-EAGMY3QE.mjs";
35
+ import {
36
+ MODAL_VERSION,
37
+ serviceHeaders
38
+ } from "./chunk-3TEVMP76.mjs";
39
39
 
40
40
  // src/components/ui/Modal.tsx
41
41
  import {
@@ -736,6 +736,31 @@ function resolveAppFeeBps(appFees, sourceChainCaip2, sourceToken) {
736
736
  const bps = override ?? appFees?.feeBps ?? 0;
737
737
  return bps > 0 ? bps : 0;
738
738
  }
739
+ function isRawAmount(value) {
740
+ return typeof value === "string" && /^\d+$/.test(value);
741
+ }
742
+ function isDepositPolicyRefusal(value) {
743
+ return typeof value === "object" && value !== null && "policyRefusal" in value;
744
+ }
745
+ var DEPOSIT_POLICY_REFUSAL_CODES = [
746
+ "TOKEN-3",
747
+ "BALANCE-3",
748
+ "BALANCE-4"
749
+ ];
750
+ var DEPOSIT_PREFLIGHT_TIMEOUT_MS = 1e4;
751
+ async function readPolicyRefusal(response) {
752
+ if (response.status !== 422) return null;
753
+ try {
754
+ const body = await response.json();
755
+ if (!body || typeof body.code !== "string" || !DEPOSIT_POLICY_REFUSAL_CODES.includes(body.code)) {
756
+ return null;
757
+ }
758
+ const message = typeof body.error === "string" && body.error !== "" ? body.error : "This deposit is not accepted.";
759
+ return { policyRefusal: { code: body.code, message } };
760
+ } catch {
761
+ return null;
762
+ }
763
+ }
739
764
  var PermitDepositSubmitError = class extends Error {
740
765
  constructor(message, safeToTransfer) {
741
766
  super(message);
@@ -762,29 +787,60 @@ function createDepositService(baseUrl, options) {
762
787
  if (value.length <= 20) return value;
763
788
  return `${value.slice(0, 10)}...${value.slice(-8)}`;
764
789
  }
790
+ function withPreflightTimeout(label, fallback, run) {
791
+ const controller = new AbortController();
792
+ return new Promise((resolve) => {
793
+ const timer = setTimeout(() => {
794
+ debugLog(debug, scope, `${label}:timeout`, {
795
+ timeoutMs: DEPOSIT_PREFLIGHT_TIMEOUT_MS
796
+ });
797
+ controller.abort();
798
+ resolve(fallback);
799
+ }, DEPOSIT_PREFLIGHT_TIMEOUT_MS);
800
+ run(controller.signal).then(
801
+ (value) => {
802
+ clearTimeout(timer);
803
+ resolve(value);
804
+ },
805
+ () => {
806
+ clearTimeout(timer);
807
+ resolve(fallback);
808
+ }
809
+ );
810
+ });
811
+ }
765
812
  const PORTFOLIO_CACHE_TTL_MS = 3e4;
766
813
  const portfolioCache = /* @__PURE__ */ new Map();
767
814
  let topTokensPromise = null;
768
815
  let chainsPromise = null;
769
- function parseTopTokens(value) {
770
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
816
+ function parseDepositOptions(value) {
817
+ if (typeof value !== "object" || value === null) return null;
818
+ const { restricted, tokens: rawTokens } = value;
819
+ if (typeof restricted !== "boolean") return null;
820
+ if (typeof rawTokens !== "object" || rawTokens === null || Array.isArray(rawTokens)) {
771
821
  return null;
772
822
  }
773
- const parsed = {};
774
- for (const [chainKey, list] of Object.entries(value)) {
823
+ const tokens = {};
824
+ for (const [chainKey, list] of Object.entries(rawTokens)) {
775
825
  if (!Array.isArray(list)) return null;
776
- const tokens = [];
826
+ const parsed = [];
777
827
  for (const entry of list) {
778
828
  if (typeof entry !== "object" || entry === null) return null;
779
- const { symbol, address, decimals } = entry;
829
+ const { symbol, address, decimals, minAmount, maxAmount } = entry;
780
830
  if (typeof symbol !== "string" || typeof address !== "string" || typeof decimals !== "number") {
781
831
  return null;
782
832
  }
783
- tokens.push({ symbol, address, decimals });
833
+ parsed.push({
834
+ symbol,
835
+ address,
836
+ decimals,
837
+ ...isRawAmount(minAmount) && { minAmount },
838
+ ...isRawAmount(maxAmount) && { maxAmount }
839
+ });
784
840
  }
785
- parsed[chainKey] = tokens;
841
+ tokens[chainKey] = parsed;
786
842
  }
787
- return parsed;
843
+ return { restricted, tokens };
788
844
  }
789
845
  async function requestTopTokens() {
790
846
  try {
@@ -796,21 +852,21 @@ function createDepositService(baseUrl, options) {
796
852
  debugLog(debug, scope, "getTopTokens:unavailable", {
797
853
  status: response.status
798
854
  });
799
- return null;
855
+ return { ok: false };
800
856
  }
801
- const data = await response.json();
802
- const tokens = parseTopTokens(data.tokens);
803
- if (!tokens) {
857
+ const options2 = parseDepositOptions(await response.json());
858
+ if (!options2) {
804
859
  debugLog(debug, scope, "getTopTokens:malformed");
805
- return null;
860
+ return { ok: false };
806
861
  }
807
862
  debugLog(debug, scope, "getTopTokens:success", {
808
- chains: Object.keys(tokens).length
863
+ chains: Object.keys(options2.tokens).length,
864
+ restricted: options2.restricted
809
865
  });
810
- return tokens;
866
+ return { ok: true, options: options2 };
811
867
  } catch (error) {
812
868
  debugError(debug, scope, "getTopTokens:error", error);
813
- return null;
869
+ return { ok: false };
814
870
  }
815
871
  }
816
872
  async function requestChains() {
@@ -1303,36 +1359,46 @@ function createDepositService(baseUrl, options) {
1303
1359
  sourceToken: params.sourceToken,
1304
1360
  amount: params.amount
1305
1361
  });
1306
- try {
1307
- const response = await fetch(url, {
1308
- method: "POST",
1309
- headers: serviceHeaders(),
1310
- body: JSON.stringify(body),
1311
- cache: "no-store"
1312
- });
1313
- if (!response.ok) {
1314
- debugLog(debug, scope, "getQuotePreview:unavailable", {
1315
- status: response.status
1362
+ return withPreflightTimeout("getQuotePreview", null, async (signal) => {
1363
+ try {
1364
+ const response = await fetch(url, {
1365
+ method: "POST",
1366
+ headers: serviceHeaders(),
1367
+ body: JSON.stringify(body),
1368
+ cache: "no-store",
1369
+ signal
1316
1370
  });
1317
- return null;
1318
- }
1319
- const quote = parseQuotePreview(await response.json());
1320
- if (!quote) {
1321
- debugLog(debug, scope, "getQuotePreview:unavailable", {
1322
- status: response.status,
1323
- reason: "malformed"
1371
+ if (!response.ok) {
1372
+ const refusal = await readPolicyRefusal(response);
1373
+ if (refusal) {
1374
+ debugLog(debug, scope, "getQuotePreview:refused", {
1375
+ code: refusal.policyRefusal.code
1376
+ });
1377
+ return refusal;
1378
+ }
1379
+ debugLog(debug, scope, "getQuotePreview:unavailable", {
1380
+ status: response.status
1381
+ });
1382
+ return null;
1383
+ }
1384
+ const quote = parseQuotePreview(await response.json());
1385
+ if (!quote) {
1386
+ debugLog(debug, scope, "getQuotePreview:unavailable", {
1387
+ status: response.status,
1388
+ reason: "malformed"
1389
+ });
1390
+ return null;
1391
+ }
1392
+ debugLog(debug, scope, "getQuotePreview:success", {
1393
+ settlementLayer: quote.settlementLayer,
1394
+ totalUsd: quote.fees.total.usd
1324
1395
  });
1396
+ return quote;
1397
+ } catch (error) {
1398
+ debugError(debug, scope, "getQuotePreview:error", error);
1325
1399
  return null;
1326
1400
  }
1327
- debugLog(debug, scope, "getQuotePreview:success", {
1328
- settlementLayer: quote.settlementLayer,
1329
- totalUsd: quote.fees.total.usd
1330
- });
1331
- return quote;
1332
- } catch (error) {
1333
- debugError(debug, scope, "getQuotePreview:error", error);
1334
- return null;
1335
- }
1401
+ });
1336
1402
  },
1337
1403
  async preparePermitDeposit(params) {
1338
1404
  const url = apiUrl("/deposits/permit/prepare");
@@ -1343,88 +1409,98 @@ function createDepositService(baseUrl, options) {
1343
1409
  token: params.token,
1344
1410
  amount: params.amount
1345
1411
  });
1346
- try {
1347
- const response = await fetch(url, {
1348
- method: "POST",
1349
- headers: serviceHeaders(),
1350
- body: JSON.stringify({ ...params, kind: "auto" }),
1351
- cache: "no-store"
1352
- });
1353
- if (!response.ok) {
1354
- debugLog(debug, scope, "preparePermitDeposit:unavailable", {
1355
- status: response.status
1356
- });
1357
- return null;
1358
- }
1359
- const data = await response.json();
1360
- if (!data || typeof data.available !== "boolean") {
1361
- debugLog(debug, scope, "preparePermitDeposit:unavailable", {
1362
- status: response.status,
1363
- reason: "malformed"
1412
+ return withPreflightTimeout("preparePermitDeposit", null, async (signal) => {
1413
+ try {
1414
+ const response = await fetch(url, {
1415
+ method: "POST",
1416
+ headers: serviceHeaders(),
1417
+ body: JSON.stringify({ ...params, kind: "auto" }),
1418
+ cache: "no-store",
1419
+ signal
1364
1420
  });
1365
- return null;
1366
- }
1367
- if (data.available === false) {
1368
- const reason = data.reason;
1369
- if (reason !== "permit2-approval-required" && reason !== "unsupported-token" && reason !== "unsupported-owner") {
1421
+ if (!response.ok) {
1422
+ const refusal = await readPolicyRefusal(response);
1423
+ if (refusal) {
1424
+ debugLog(debug, scope, "preparePermitDeposit:refused", {
1425
+ code: refusal.policyRefusal.code
1426
+ });
1427
+ return refusal;
1428
+ }
1429
+ debugLog(debug, scope, "preparePermitDeposit:unavailable", {
1430
+ status: response.status
1431
+ });
1370
1432
  return null;
1371
1433
  }
1372
- debugLog(debug, scope, "preparePermitDeposit:unavailable", {
1373
- status: response.status,
1374
- reason
1375
- });
1376
- const approval = data.approval;
1377
- const parsedApproval = approval && typeof approval.token === "string" && typeof approval.spender === "string" && typeof approval.minimumAmount === "string" ? {
1378
- token: approval.token,
1379
- spender: approval.spender,
1380
- minimumAmount: approval.minimumAmount
1381
- } : void 0;
1382
- return {
1383
- available: false,
1384
- reason,
1385
- ...parsedApproval && { approval: parsedApproval }
1386
- };
1387
- }
1388
- const authorization = data.authorization;
1389
- if (!authorization || typeof authorization.typedData !== "object" || authorization.typedData === null || typeof authorization.nonce !== "string") {
1390
- return null;
1391
- }
1392
- if (authorization.kind === "erc3009" && typeof authorization.validAfter === "string" && typeof authorization.validBefore === "string") {
1393
- debugLog(debug, scope, "preparePermitDeposit:success", {
1394
- kind: authorization.kind,
1395
- nonce: authorization.nonce
1396
- });
1397
- return {
1398
- available: true,
1399
- authorization: {
1400
- kind: "erc3009",
1401
- typedData: authorization.typedData,
1402
- nonce: authorization.nonce,
1403
- validAfter: authorization.validAfter,
1404
- validBefore: authorization.validBefore
1434
+ const data = await response.json();
1435
+ if (!data || typeof data.available !== "boolean") {
1436
+ debugLog(debug, scope, "preparePermitDeposit:unavailable", {
1437
+ status: response.status,
1438
+ reason: "malformed"
1439
+ });
1440
+ return null;
1441
+ }
1442
+ if (data.available === false) {
1443
+ const reason = data.reason;
1444
+ if (reason !== "permit2-approval-required" && reason !== "unsupported-token" && reason !== "unsupported-owner") {
1445
+ return null;
1405
1446
  }
1406
- };
1407
- }
1408
- if ((authorization.kind === "erc2612" || authorization.kind === "permit2") && typeof authorization.deadline === "string") {
1409
- debugLog(debug, scope, "preparePermitDeposit:success", {
1410
- kind: authorization.kind,
1411
- nonce: authorization.nonce
1412
- });
1413
- return {
1414
- available: true,
1415
- authorization: {
1447
+ debugLog(debug, scope, "preparePermitDeposit:unavailable", {
1448
+ status: response.status,
1449
+ reason
1450
+ });
1451
+ const approval = data.approval;
1452
+ const parsedApproval = approval && typeof approval.token === "string" && typeof approval.spender === "string" && typeof approval.minimumAmount === "string" ? {
1453
+ token: approval.token,
1454
+ spender: approval.spender,
1455
+ minimumAmount: approval.minimumAmount
1456
+ } : void 0;
1457
+ return {
1458
+ available: false,
1459
+ reason,
1460
+ ...parsedApproval && { approval: parsedApproval }
1461
+ };
1462
+ }
1463
+ const authorization = data.authorization;
1464
+ if (!authorization || typeof authorization.typedData !== "object" || authorization.typedData === null || typeof authorization.nonce !== "string") {
1465
+ return null;
1466
+ }
1467
+ if (authorization.kind === "erc3009" && typeof authorization.validAfter === "string" && typeof authorization.validBefore === "string") {
1468
+ debugLog(debug, scope, "preparePermitDeposit:success", {
1416
1469
  kind: authorization.kind,
1417
- typedData: authorization.typedData,
1418
- nonce: authorization.nonce,
1419
- deadline: authorization.deadline
1420
- }
1421
- };
1470
+ nonce: authorization.nonce
1471
+ });
1472
+ return {
1473
+ available: true,
1474
+ authorization: {
1475
+ kind: "erc3009",
1476
+ typedData: authorization.typedData,
1477
+ nonce: authorization.nonce,
1478
+ validAfter: authorization.validAfter,
1479
+ validBefore: authorization.validBefore
1480
+ }
1481
+ };
1482
+ }
1483
+ if ((authorization.kind === "erc2612" || authorization.kind === "permit2") && typeof authorization.deadline === "string") {
1484
+ debugLog(debug, scope, "preparePermitDeposit:success", {
1485
+ kind: authorization.kind,
1486
+ nonce: authorization.nonce
1487
+ });
1488
+ return {
1489
+ available: true,
1490
+ authorization: {
1491
+ kind: authorization.kind,
1492
+ typedData: authorization.typedData,
1493
+ nonce: authorization.nonce,
1494
+ deadline: authorization.deadline
1495
+ }
1496
+ };
1497
+ }
1498
+ return null;
1499
+ } catch (error) {
1500
+ debugError(debug, scope, "preparePermitDeposit:error", error);
1501
+ return null;
1422
1502
  }
1423
- return null;
1424
- } catch (error) {
1425
- debugError(debug, scope, "preparePermitDeposit:error", error);
1426
- return null;
1427
- }
1503
+ });
1428
1504
  },
1429
1505
  async submitPermitDeposit(params) {
1430
1506
  const url = apiUrl("/deposits/permit");
@@ -1733,9 +1809,11 @@ function createDepositService(baseUrl, options) {
1733
1809
  },
1734
1810
  getTopTokens() {
1735
1811
  if (!topTokensPromise) {
1736
- const attempt = requestTopTokens().then((tokens) => {
1737
- if (!tokens && topTokensPromise === attempt) topTokensPromise = null;
1738
- return tokens;
1812
+ const attempt = requestTopTokens().then((result) => {
1813
+ if (!result.ok && topTokensPromise === attempt) {
1814
+ topTokensPromise = null;
1815
+ }
1816
+ return result;
1739
1817
  });
1740
1818
  topTokensPromise = attempt;
1741
1819
  }
@@ -1964,6 +2042,7 @@ function useStableRpcUrls(rpcUrls) {
1964
2042
  // src/core/chain-catalog-context.tsx
1965
2043
  import {
1966
2044
  createContext as createContext2,
2045
+ useCallback as useCallback2,
1967
2046
  useContext as useContext2,
1968
2047
  useEffect as useEffect2,
1969
2048
  useMemo as useMemo2,
@@ -1971,6 +2050,59 @@ import {
1971
2050
  useSyncExternalStore
1972
2051
  } from "react";
1973
2052
 
2053
+ // src/core/deposit-bounds.ts
2054
+ import { formatUnits } from "viem";
2055
+ function findListed(options, chain, token) {
2056
+ const normalize2 = (value) => chain === "solana" ? value : value.toLowerCase();
2057
+ return options.tokens[String(chain)]?.find(
2058
+ (listed) => normalize2(listed.address) === normalize2(token)
2059
+ );
2060
+ }
2061
+ function isExcludedByRestrictedList(options, chain, token) {
2062
+ return Boolean(options?.restricted && !findListed(options, chain, token));
2063
+ }
2064
+ function findDepositBounds(options, chain, token) {
2065
+ if (!options?.restricted) return void 0;
2066
+ const entry = findListed(options, chain, token);
2067
+ if (!entry || entry.minAmount === void 0 && entry.maxAmount === void 0) {
2068
+ return void 0;
2069
+ }
2070
+ return {
2071
+ symbol: entry.symbol,
2072
+ decimals: entry.decimals,
2073
+ ...entry.minAmount !== void 0 && { minAmount: BigInt(entry.minAmount) },
2074
+ ...entry.maxAmount !== void 0 && { maxAmount: BigInt(entry.maxAmount) }
2075
+ };
2076
+ }
2077
+ function boundMessage(bounds, bound) {
2078
+ const raw = bounds?.[bound];
2079
+ if (!bounds || raw === void 0) return null;
2080
+ const amount = new Intl.NumberFormat("en-US", {
2081
+ maximumFractionDigits: Math.min(bounds.decimals, 20)
2082
+ }).format(Number(formatUnits(raw, bounds.decimals)));
2083
+ return `${bound === "minAmount" ? "Minimum" : "Maximum"} deposit is ${amount} ${bounds.symbol}`;
2084
+ }
2085
+ function depositBoundsViolation(bounds, units) {
2086
+ if (!bounds || units === null || units <= 0n) return null;
2087
+ if (bounds.minAmount !== void 0 && units < bounds.minAmount) {
2088
+ return boundMessage(bounds, "minAmount");
2089
+ }
2090
+ if (bounds.maxAmount !== void 0 && units > bounds.maxAmount) {
2091
+ return boundMessage(bounds, "maxAmount");
2092
+ }
2093
+ return null;
2094
+ }
2095
+ function describePolicyRefusal(refusal, bounds) {
2096
+ switch (refusal.code) {
2097
+ case "BALANCE-4":
2098
+ return boundMessage(bounds, "minAmount") ?? "This amount is below the minimum deposit";
2099
+ case "BALANCE-3":
2100
+ return boundMessage(bounds, "maxAmount") ?? "This amount is above the maximum deposit";
2101
+ default:
2102
+ return "This token isn't accepted for deposits";
2103
+ }
2104
+ }
2105
+
1974
2106
  // src/core/public-client.ts
1975
2107
  import { createPublicClient, http } from "viem";
1976
2108
  var clientCache = /* @__PURE__ */ new Map();
@@ -2135,6 +2267,9 @@ var EMPTY = {
2135
2267
  viemChains: null,
2136
2268
  isLoading: true,
2137
2269
  shortlist: null,
2270
+ depositOptions: { status: "loading" },
2271
+ retryDepositOptions: () => {
2272
+ },
2138
2273
  clientCapableChainIds: null
2139
2274
  };
2140
2275
  var ChainCatalogContext = createContext2(EMPTY);
@@ -2292,6 +2427,48 @@ function useTargetTokens() {
2292
2427
  };
2293
2428
  }, [catalog, shortlist]);
2294
2429
  }
2430
+ function createDepositOffer(state, retry) {
2431
+ const options = state.status === "ready" ? state.options : null;
2432
+ const restricted = options?.restricted ?? false;
2433
+ const listed = (chain) => options?.tokens[String(chain)];
2434
+ const isChainOffered = (chain) => {
2435
+ if (!options) return false;
2436
+ const entry = listed(chain);
2437
+ return restricted ? (entry?.length ?? 0) > 0 : entry?.length !== 0;
2438
+ };
2439
+ return {
2440
+ status: state.status,
2441
+ restricted,
2442
+ retry,
2443
+ tokensFor: (chain) => {
2444
+ if (!options) return [];
2445
+ const entry = listed(chain);
2446
+ return restricted ? entry ?? [] : entry;
2447
+ },
2448
+ isChainOffered
2449
+ };
2450
+ }
2451
+ function useDepositOffer() {
2452
+ const { depositOptions, retryDepositOptions } = useChainCatalog();
2453
+ return useMemo2(
2454
+ () => createDepositOffer(depositOptions, retryDepositOptions),
2455
+ [depositOptions, retryDepositOptions]
2456
+ );
2457
+ }
2458
+ function useDepositBounds() {
2459
+ const { depositOptions } = useChainCatalog();
2460
+ return useMemo2(() => {
2461
+ const options = depositOptions.status === "ready" ? depositOptions.options : null;
2462
+ return (chain, token) => findDepositBounds(options, chain, token);
2463
+ }, [depositOptions]);
2464
+ }
2465
+ function useRestrictedListExclusion() {
2466
+ const { depositOptions } = useChainCatalog();
2467
+ return useMemo2(() => {
2468
+ const options = depositOptions.status === "ready" ? depositOptions.options : null;
2469
+ return (chain, token) => isExcludedByRestrictedList(options, chain, token);
2470
+ }, [depositOptions]);
2471
+ }
2295
2472
  function useIsDepositChain() {
2296
2473
  const ids = useDepositChainIds();
2297
2474
  const { clientCapableChainIds } = useChainCatalog();
@@ -2384,19 +2561,36 @@ function ChainCatalogProvider({
2384
2561
  const chains = toViemChains(catalog, overrides, viemChains);
2385
2562
  if (chains.length > 0) setRuntimeChains(chains);
2386
2563
  }, [catalog, viemChains, overrides]);
2387
- const [shortlist, setShortlist] = useState(null);
2564
+ const [depositOptions, setDepositOptions] = useState({
2565
+ status: "loading"
2566
+ });
2567
+ const [depositOptionsAttempt, setDepositOptionsAttempt] = useState(0);
2388
2568
  useEffect2(() => {
2389
2569
  let active = true;
2390
- setShortlist(null);
2391
- service.getTopTokens().then((tokens) => {
2392
- if (!active || !tokens) return;
2393
- publishShortlistTokens(tokens);
2394
- setShortlist(tokens);
2395
- });
2570
+ setDepositOptions({ status: "loading" });
2571
+ service.getTopTokens().then(
2572
+ (result) => {
2573
+ if (!active) return;
2574
+ if (!result?.ok) {
2575
+ setDepositOptions({ status: "error" });
2576
+ return;
2577
+ }
2578
+ publishShortlistTokens(result.options.tokens);
2579
+ setDepositOptions({ status: "ready", options: result.options });
2580
+ },
2581
+ () => {
2582
+ if (active) setDepositOptions({ status: "error" });
2583
+ }
2584
+ );
2396
2585
  return () => {
2397
2586
  active = false;
2398
2587
  };
2399
- }, [service]);
2588
+ }, [service, depositOptionsAttempt]);
2589
+ const retryDepositOptions = useCallback2(
2590
+ () => setDepositOptionsAttempt((attempt) => attempt + 1),
2591
+ []
2592
+ );
2593
+ const shortlist = depositOptions.status === "ready" ? depositOptions.options.tokens : null;
2400
2594
  const clientCapableChainIds = useMemo2(() => {
2401
2595
  if (!catalog) return null;
2402
2596
  return new Set(
@@ -2409,9 +2603,19 @@ function ChainCatalogProvider({
2409
2603
  viemChains,
2410
2604
  isLoading,
2411
2605
  shortlist,
2606
+ depositOptions,
2607
+ retryDepositOptions,
2412
2608
  clientCapableChainIds
2413
2609
  }),
2414
- [catalog, viemChains, isLoading, shortlist, clientCapableChainIds]
2610
+ [
2611
+ catalog,
2612
+ viemChains,
2613
+ isLoading,
2614
+ shortlist,
2615
+ depositOptions,
2616
+ retryDepositOptions,
2617
+ clientCapableChainIds
2618
+ ]
2415
2619
  );
2416
2620
  return /* @__PURE__ */ jsx3(ChainCatalogContext.Provider, { value, children });
2417
2621
  }
@@ -2429,7 +2633,7 @@ function useLatestRef(value) {
2429
2633
  // src/core/analytics.tsx
2430
2634
  import {
2431
2635
  createContext as createContext3,
2432
- useCallback as useCallback2,
2636
+ useCallback as useCallback3,
2433
2637
  useContext as useContext3,
2434
2638
  useEffect as useEffect4,
2435
2639
  useMemo as useMemo3,
@@ -2817,7 +3021,7 @@ function AnalyticsSession(props) {
2817
3021
  nowRef.current = props.now ?? (() => /* @__PURE__ */ new Date());
2818
3022
  const modal = props.modal;
2819
3023
  const reportedFailuresRef = useRef3(/* @__PURE__ */ new Set());
2820
- const emitFailure = useCallback2(
3024
+ const emitFailure = useCallback3(
2821
3025
  ({ reason, status, droppedEvents }) => {
2822
3026
  if (reportedFailuresRef.current.has(reason)) return;
2823
3027
  reportedFailuresRef.current.add(reason);
@@ -2867,7 +3071,7 @@ function AnalyticsSession(props) {
2867
3071
  ingestRef.current = createIngest();
2868
3072
  }
2869
3073
  const pendingDisposalRef = useRef3(null);
2870
- const enqueue = useCallback2(
3074
+ const enqueue = useCallback3(
2871
3075
  (event) => {
2872
3076
  try {
2873
3077
  ingestRef.current?.transport?.enqueue(
@@ -2902,7 +3106,7 @@ function AnalyticsSession(props) {
2902
3106
  }, 0);
2903
3107
  };
2904
3108
  }, [emitFailure]);
2905
- const emitDeposit = useCallback2(
3109
+ const emitDeposit = useCallback3(
2906
3110
  (...[payload, fundingMethod]) => {
2907
3111
  if (modal !== "deposit") return;
2908
3112
  const event = {
@@ -2921,7 +3125,7 @@ function AnalyticsSession(props) {
2921
3125
  },
2922
3126
  [enqueue, modal, nowRef, onEventRef]
2923
3127
  );
2924
- const emitWithdraw = useCallback2(
3128
+ const emitWithdraw = useCallback3(
2925
3129
  (payload) => {
2926
3130
  if (modal !== "withdraw") return;
2927
3131
  const event = {
@@ -2937,7 +3141,7 @@ function AnalyticsSession(props) {
2937
3141
  },
2938
3142
  [enqueue, modal, nowRef, onEventRef]
2939
3143
  );
2940
- const emitClaim = useCallback2(
3144
+ const emitClaim = useCallback3(
2941
3145
  (payload) => {
2942
3146
  if (modal !== "claim") return;
2943
3147
  const event = {
@@ -3011,17 +3215,38 @@ function recoveryFailureCause(sourceChainName) {
3011
3215
  }
3012
3216
 
3013
3217
  // src/core/history-status.ts
3014
- function normalizeStatus(raw, isSpam) {
3218
+ var UNKNOWN = ["pending", false, []];
3219
+ var STATUS_FACTS = [
3220
+ // `delayed` is held up but still coming on the corridor's schedule.
3221
+ ["pending", true, ["pending", "delayed"]],
3222
+ // Nothing emits `submitted` or `bridging`; they are here so the badge and
3223
+ // the arrival line cannot diverge should a row ever carry them.
3224
+ ["processing", true, ["processing", "submitted", "bridging"]],
3225
+ ["completed", false, ["completed", "complete", "bridge-complete", "processed"]],
3226
+ // `rejected` is recoverable upstream exactly like `failed`, so it must not
3227
+ // read as Pending — a row offering recovery would otherwise claim to still
3228
+ // be on its way.
3229
+ ["failed", false, ["failed", "error", "bridge-failed", "rejected"]],
3230
+ // Where a recovered deposit lands. Terminal and not recoverable again.
3231
+ ["refunded", false, ["refunded"]],
3232
+ // These read Pending but are not arriving: the deposit is bound for a refund
3233
+ // or for manual reconciliation rather than for the recipient, so an arrival
3234
+ // expectation would be a claim about a corridor it never enters.
3235
+ ["pending", false, ["ignored", "expecting_refund", "reconciliation_required"]]
3236
+ ];
3237
+ function lookup(raw) {
3015
3238
  const lower = raw.toLowerCase();
3016
- if (lower === "completed" || lower === "complete" || lower === "bridge-complete" || lower === "processed")
3017
- return "completed";
3018
- if (lower === "failed" || lower === "error" || lower === "bridge-failed" || lower === "rejected") {
3019
- return isSpam ? "spam" : "failed";
3020
- }
3021
- if (lower === "refunded") return "refunded";
3022
- if (lower === "processing" || lower === "submitted" || lower === "bridging")
3023
- return "processing";
3024
- return "pending";
3239
+ return STATUS_FACTS.find((facts) => facts[2].includes(lower)) ?? UNKNOWN;
3240
+ }
3241
+ function normalizeStatus(raw, isSpam) {
3242
+ const [badge] = lookup(raw);
3243
+ return isSpam && badge === "failed" ? "spam" : badge;
3244
+ }
3245
+ function isInFlight(status) {
3246
+ return status === "pending" || status === "processing";
3247
+ }
3248
+ function expectsArrival(raw) {
3249
+ return lookup(raw)[1];
3025
3250
  }
3026
3251
  var STATUS_LABEL = {
3027
3252
  pending: "Pending",
@@ -3041,20 +3266,37 @@ function filterVisibleDeposits(deposits) {
3041
3266
  }
3042
3267
 
3043
3268
  // src/core/history-badge.ts
3044
- function deriveHistoryBadge(deposits, { recipient, canRecover }) {
3269
+ var RECOVERY_INDICATOR_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
3270
+ function deriveHistoryBadgeState(deposits, {
3271
+ recipient,
3272
+ canRecover,
3273
+ evaluatedAt = Date.now()
3274
+ }) {
3045
3275
  const visible = filterVisibleDeposits(deposits);
3046
3276
  if (canRecover) {
3047
- const needsYou = visible.filter(
3048
- (row) => toRecoverableDeposit(row, { recipient }) !== null
3049
- ).length;
3050
- if (needsYou > 0) return { state: "needs-you", count: needsYou };
3051
- }
3052
- const inProgress = visible.filter((row) => {
3053
- const status = normalizeStatus(row.status, row.isSpam);
3054
- return status === "pending" || status === "processing";
3055
- }).length;
3056
- if (inProgress > 0) return { state: "progress", count: inProgress };
3057
- return null;
3277
+ let count = 0;
3278
+ let nextExpiryAt = null;
3279
+ for (const row of visible) {
3280
+ if (toRecoverableDeposit(row, { recipient }) === null) continue;
3281
+ const expiryAt = Date.parse(row.createdAt ?? "") + RECOVERY_INDICATOR_WINDOW_MS;
3282
+ if (!(evaluatedAt < expiryAt)) continue;
3283
+ count += 1;
3284
+ nextExpiryAt = nextExpiryAt === null ? expiryAt : Math.min(nextExpiryAt, expiryAt);
3285
+ }
3286
+ if (count > 0) {
3287
+ return {
3288
+ badge: { state: "needs-you", count },
3289
+ nextExpiryAt
3290
+ };
3291
+ }
3292
+ }
3293
+ const inProgress = visible.filter(
3294
+ (row) => isInFlight(normalizeStatus(row.status, row.isSpam))
3295
+ ).length;
3296
+ return {
3297
+ badge: inProgress > 0 ? { state: "progress", count: inProgress } : null,
3298
+ nextExpiryAt: null
3299
+ };
3058
3300
  }
3059
3301
  function historyBadgeAriaLabel(badge) {
3060
3302
  if (!badge) return "Deposit history";
@@ -3094,11 +3336,11 @@ function HistoryEntryButton({
3094
3336
  }
3095
3337
 
3096
3338
  // src/components/history/HistoryOverlay.tsx
3097
- import { useCallback as useCallback6, useEffect as useEffect10, useMemo as useMemo6, useRef as useRef7, useState as useState8 } from "react";
3339
+ import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo6, useRef as useRef7, useState as useState8 } from "react";
3098
3340
 
3099
3341
  // src/components/history/DepositHistoryPanel.tsx
3100
3342
  import {
3101
- useCallback as useCallback3,
3343
+ useCallback as useCallback4,
3102
3344
  useEffect as useEffect5,
3103
3345
  useId,
3104
3346
  useMemo as useMemo4,
@@ -3428,14 +3670,13 @@ function advancePendingTimingState(previous, next) {
3428
3670
  }
3429
3671
 
3430
3672
  // src/core/history-timing.ts
3431
- var TIMED_HISTORY_STATUSES = /* @__PURE__ */ new Set(["pending", "processing", "delayed"]);
3432
3673
  function resolveHistoryTiming({
3433
3674
  status,
3434
3675
  timing,
3435
3676
  createdAt,
3436
3677
  nowMs
3437
3678
  }) {
3438
- if (!TIMED_HISTORY_STATUSES.has(status) || !timing || !createdAt) {
3679
+ if (!expectsArrival(status) || !timing || !createdAt) {
3439
3680
  return void 0;
3440
3681
  }
3441
3682
  const createdAtMs = Date.parse(createdAt);
@@ -3559,7 +3800,7 @@ function DepositHistoryPanel({
3559
3800
  useEffect5(() => {
3560
3801
  rootRef.current?.toggleAttribute("inert", Boolean(obscured));
3561
3802
  }, [obscured]);
3562
- const handleKeyDown = useCallback3(
3803
+ const handleKeyDown = useCallback4(
3563
3804
  (e) => {
3564
3805
  if (obscured) return;
3565
3806
  if (e.key === "Escape") {
@@ -3860,7 +4101,7 @@ function HistoryCard({
3860
4101
  DepositHistoryPanel.displayName = "DepositHistoryPanel";
3861
4102
 
3862
4103
  // src/components/history/HistoryFallbackFlow.tsx
3863
- import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5, useState as useState5 } from "react";
4104
+ import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef5, useState as useState5 } from "react";
3864
4105
 
3865
4106
  // src/components/ui/ListRow.tsx
3866
4107
  import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -4044,10 +4285,10 @@ function RecoveryLookupScreen({
4044
4285
  RecoveryLookupScreen.displayName = "RecoveryLookupScreen";
4045
4286
 
4046
4287
  // src/components/recovery/recovery-display.ts
4047
- import { formatUnits as formatUnits2 } from "viem";
4288
+ import { formatUnits as formatUnits3 } from "viem";
4048
4289
 
4049
4290
  // src/core/token-amount.ts
4050
- import { formatUnits } from "viem";
4291
+ import { formatUnits as formatUnits2 } from "viem";
4051
4292
  var STABLECOIN_SYMBOLS = /* @__PURE__ */ new Set([
4052
4293
  "USDC",
4053
4294
  "USDT",
@@ -4111,7 +4352,7 @@ function formatTokenAmount(amount, symbol) {
4111
4352
  function tokenAmountToNumber(rawAmount, decimals) {
4112
4353
  if (decimals === void 0) return void 0;
4113
4354
  try {
4114
- const value = Number(formatUnits(BigInt(rawAmount), decimals));
4355
+ const value = Number(formatUnits2(BigInt(rawAmount), decimals));
4115
4356
  return Number.isFinite(value) ? value : void 0;
4116
4357
  } catch {
4117
4358
  return void 0;
@@ -4198,18 +4439,18 @@ function describeRecoveryDeposit(params) {
4198
4439
  const decimals = getTokenDecimalsByAddress(token, params.chainId);
4199
4440
  const symbol = getTokenSymbol(token, params.chainId);
4200
4441
  const raw = toBigInt(params.amountRaw);
4201
- const human = Number(formatUnits2(raw, decimals));
4442
+ const human = Number(formatUnits3(raw, decimals));
4202
4443
  return {
4203
4444
  symbol,
4204
4445
  decimals,
4205
- amount: formatTokenAmount(human, symbol) ?? formatUnits2(raw, decimals),
4446
+ amount: formatTokenAmount(human, symbol) ?? formatUnits3(raw, decimals),
4206
4447
  chainName: getChainName(params.chainId),
4207
4448
  chainIcon: getChainIcon(params.chainId)
4208
4449
  };
4209
4450
  }
4210
4451
  function formatRecoveryAmount(amountRaw, decimals, symbol) {
4211
4452
  const raw = toBigInt(amountRaw);
4212
- return formatTokenAmount(Number(formatUnits2(raw, decimals)), symbol) ?? formatUnits2(raw, decimals);
4453
+ return formatTokenAmount(Number(formatUnits3(raw, decimals)), symbol) ?? formatUnits3(raw, decimals);
4213
4454
  }
4214
4455
  function toBigInt(value) {
4215
4456
  try {
@@ -4697,7 +4938,7 @@ function HistoryFallbackFlow({
4697
4938
  epochRef.current += 1;
4698
4939
  };
4699
4940
  }, [recipient]);
4700
- const reportSelection = useCallback4(
4941
+ const reportSelection = useCallback5(
4701
4942
  (match, autoSelected) => {
4702
4943
  analytics.selectDeposit({
4703
4944
  depositId: match.depositId,
@@ -4707,7 +4948,7 @@ function HistoryFallbackFlow({
4707
4948
  },
4708
4949
  [analytics]
4709
4950
  );
4710
- const lookup = useCallback4(
4951
+ const lookup2 = useCallback5(
4711
4952
  async (txHash) => {
4712
4953
  const epoch = epochRef.current;
4713
4954
  const isCurrentLookup = () => epochRef.current === epoch;
@@ -4750,7 +4991,7 @@ function HistoryFallbackFlow({
4750
4991
  },
4751
4992
  [analytics, service, recipient, onFound, reportSelection]
4752
4993
  );
4753
- const selectMatch = useCallback4(
4994
+ const selectMatch = useCallback5(
4754
4995
  (match) => {
4755
4996
  analytics.completeStep("select");
4756
4997
  reportSelection(match, false);
@@ -4758,7 +4999,7 @@ function HistoryFallbackFlow({
4758
4999
  },
4759
5000
  [analytics, onFound, reportSelection]
4760
5001
  );
4761
- const handleKeyDown = useCallback4(
5002
+ const handleKeyDown = useCallback5(
4762
5003
  (e) => {
4763
5004
  if (e.key === "Escape") {
4764
5005
  e.stopPropagation();
@@ -4837,7 +5078,7 @@ function HistoryFallbackFlow({
4837
5078
  hint: "The transaction hash from the wallet or exchange you sent from.",
4838
5079
  loading,
4839
5080
  error,
4840
- onSubmit: lookup,
5081
+ onSubmit: lookup2,
4841
5082
  onInvalid: () => analytics.friction("lookup_transaction_hash_invalid", "lookup")
4842
5083
  }
4843
5084
  )
@@ -4853,7 +5094,7 @@ function newestFirst(rows) {
4853
5094
  HistoryFallbackFlow.displayName = "HistoryFallbackFlow";
4854
5095
 
4855
5096
  // src/components/history/HistoryRecoveryFlow.tsx
4856
- import { useCallback as useCallback5, useEffect as useEffect9, useRef as useRef6, useState as useState7 } from "react";
5097
+ import { useCallback as useCallback6, useEffect as useEffect9, useRef as useRef6, useState as useState7 } from "react";
4857
5098
 
4858
5099
  // src/components/recovery/RecoveryOutcomeScreen.tsx
4859
5100
  import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
@@ -5057,7 +5298,7 @@ function HistoryRecoveryFlow({
5057
5298
  token: deposit.token,
5058
5299
  amountRaw: deposit.amountRaw
5059
5300
  });
5060
- const submit = useCallback5(
5301
+ const submit = useCallback6(
5061
5302
  async (destination) => {
5062
5303
  setPhase("submitting");
5063
5304
  onSubmittingChange(true);
@@ -5129,7 +5370,7 @@ function HistoryRecoveryFlow({
5129
5370
  symbol
5130
5371
  ]
5131
5372
  );
5132
- const retry = useCallback5(() => {
5373
+ const retry = useCallback6(() => {
5133
5374
  const reason = lastFailureRef.current;
5134
5375
  if (reason) {
5135
5376
  lastFailureRef.current = null;
@@ -5226,7 +5467,7 @@ function HistoryOverlay({
5226
5467
  const [fallbackOpen, setFallbackOpen] = useState8(false);
5227
5468
  const [attempt, setAttempt] = useState8(null);
5228
5469
  const claimControllerRef = useRef7(null);
5229
- const beginAttempt = useCallback6(
5470
+ const beginAttempt = useCallback7(
5230
5471
  (entrySource) => {
5231
5472
  const id = randomUuid();
5232
5473
  const started = {
@@ -5244,7 +5485,7 @@ function HistoryOverlay({
5244
5485
  },
5245
5486
  [analytics]
5246
5487
  );
5247
- const endAttempt = useCallback6(
5488
+ const endAttempt = useCallback7(
5248
5489
  (source, reason) => {
5249
5490
  const claim = claimControllerRef.current;
5250
5491
  claim?.abandon(reason);
@@ -5254,12 +5495,12 @@ function HistoryOverlay({
5254
5495
  },
5255
5496
  []
5256
5497
  );
5257
- const openFallback = useCallback6(() => {
5498
+ const openFallback = useCallback7(() => {
5258
5499
  const started = beginAttempt("history_fallback");
5259
5500
  analytics.fallbackOpened(started.id);
5260
5501
  setFallbackOpen(true);
5261
5502
  }, [analytics, beginAttempt]);
5262
- const closeFallback = useCallback6(
5503
+ const closeFallback = useCallback7(
5263
5504
  (via) => {
5264
5505
  endAttempt(via === "escape" ? "escape" : "history_back", "back");
5265
5506
  setFallbackOpen(false);
@@ -5272,7 +5513,7 @@ function HistoryOverlay({
5272
5513
  setFallbackOpen(false);
5273
5514
  if (fallbackOpen && !recovering) endAttempt("host_controlled", "back");
5274
5515
  }, [canLookup, fallbackOpen, recovering, endAttempt]);
5275
- const startRecoveryFromRow = useCallback6(
5516
+ const startRecoveryFromRow = useCallback7(
5276
5517
  (deposit) => {
5277
5518
  const started = beginAttempt("history_row");
5278
5519
  analytics.recoveryStarted(started.id, "history_row", deposit.depositId);
@@ -5280,7 +5521,7 @@ function HistoryOverlay({
5280
5521
  },
5281
5522
  [analytics, beginAttempt, controller]
5282
5523
  );
5283
- const startRecoveryFromLookup = useCallback6(
5524
+ const startRecoveryFromLookup = useCallback7(
5284
5525
  (deposit) => {
5285
5526
  if (attempt) {
5286
5527
  analytics.recoveryStarted(
@@ -5294,11 +5535,11 @@ function HistoryOverlay({
5294
5535
  },
5295
5536
  [analytics, attempt, controller]
5296
5537
  );
5297
- const cancelRecovery = useCallback6(() => {
5538
+ const cancelRecovery = useCallback7(() => {
5298
5539
  endAttempt("history_back", "back");
5299
5540
  controller.cancelRecovery();
5300
5541
  }, [controller, endAttempt]);
5301
- const closeModal = useCallback6(() => {
5542
+ const closeModal = useCallback7(() => {
5302
5543
  endAttempt("header_button", "modal_close");
5303
5544
  onCloseModal();
5304
5545
  }, [endAttempt, onCloseModal]);
@@ -5389,7 +5630,8 @@ function HistoryOverlay({
5389
5630
  }
5390
5631
 
5391
5632
  // src/components/history/useHistoryPanel.ts
5392
- import { useCallback as useCallback7, useEffect as useEffect11, useMemo as useMemo7, useRef as useRef8, useState as useState9 } from "react";
5633
+ import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo7, useRef as useRef8, useState as useState9 } from "react";
5634
+ var MAX_TIMEOUT_MS = 2147483647;
5393
5635
  function sortByCreatedAtDesc(items) {
5394
5636
  return [...items].sort((a, b) => {
5395
5637
  const at = a.createdAt ? Date.parse(a.createdAt) : 0;
@@ -5417,7 +5659,7 @@ function useHistoryPanel({
5417
5659
  const requestRef = useRef8(0);
5418
5660
  const [recovering, setRecovering] = useState9(null);
5419
5661
  const [recoverySubmitting, setRecoverySubmitting] = useState9(false);
5420
- const fetchHistory = useCallback7(
5662
+ const fetchHistory = useCallback8(
5421
5663
  async (mode = "initial") => {
5422
5664
  if (!recipient) return;
5423
5665
  const isInitial = mode === "initial";
@@ -5467,27 +5709,40 @@ function useHistoryPanel({
5467
5709
  },
5468
5710
  [recipient, service]
5469
5711
  );
5470
- const badge = useMemo7(
5471
- () => deriveHistoryBadge(deposits, { recipient, canRecover }),
5472
- [deposits, recipient, canRecover]
5473
- );
5474
- const openPanel = useCallback7(() => {
5712
+ const badgeState2 = deriveHistoryBadgeState(deposits, {
5713
+ recipient,
5714
+ canRecover
5715
+ });
5716
+ const badge = badgeState2.badge;
5717
+ useEffect11(() => {
5718
+ if (!isOpen || badgeState2.nextExpiryAt === null) return;
5719
+ const delay = Math.min(
5720
+ badgeState2.nextExpiryAt - Date.now(),
5721
+ MAX_TIMEOUT_MS
5722
+ );
5723
+ const timeout = setTimeout(
5724
+ () => setDeposits((current) => current.slice()),
5725
+ delay
5726
+ );
5727
+ return () => clearTimeout(timeout);
5728
+ }, [isOpen, badgeState2.nextExpiryAt]);
5729
+ const openPanel = useCallback8(() => {
5475
5730
  setIsPanelOpen(true);
5476
5731
  analytics?.visitOpened(badge);
5477
5732
  if (!loadedRef.current || staleRef.current) {
5478
5733
  fetchHistory("initial");
5479
5734
  }
5480
5735
  }, [analytics, badge, fetchHistory]);
5481
- const closePanel = useCallback7(() => {
5736
+ const closePanel = useCallback8(() => {
5482
5737
  if (recovering) return;
5483
5738
  setIsPanelOpen(false);
5484
5739
  analytics?.visitEnded();
5485
5740
  }, [analytics, recovering]);
5486
- const loadMore = useCallback7(() => {
5741
+ const loadMore = useCallback8(() => {
5487
5742
  analytics?.loadMore(filterVisibleDeposits(deposits).length);
5488
5743
  fetchHistory("more");
5489
5744
  }, [analytics, deposits, fetchHistory]);
5490
- const markStale = useCallback7(() => {
5745
+ const markStale = useCallback8(() => {
5491
5746
  staleRef.current = true;
5492
5747
  if (isOpen) {
5493
5748
  fetchHistory("initial");
@@ -5523,10 +5778,10 @@ function useHistoryPanel({
5523
5778
  setRecovering(null);
5524
5779
  setRecoverySubmitting(false);
5525
5780
  }, [isOpen, analytics]);
5526
- const invalidate = useCallback7(() => {
5781
+ const invalidate = useCallback8(() => {
5527
5782
  staleRef.current = true;
5528
5783
  }, []);
5529
- const cancelRecovery = useCallback7(() => setRecovering(null), []);
5784
+ const cancelRecovery = useCallback8(() => setRecovering(null), []);
5530
5785
  return useMemo7(
5531
5786
  () => ({
5532
5787
  deposits,
@@ -5960,14 +6215,14 @@ function isSubmissionUncertain(error) {
5960
6215
  }
5961
6216
 
5962
6217
  // src/components/steps/ProcessingStep.tsx
5963
- import { useCallback as useCallback9, useEffect as useEffect14, useRef as useRef10, useState as useState13 } from "react";
6218
+ import { useCallback as useCallback10, useEffect as useEffect14, useRef as useRef10, useState as useState13 } from "react";
5964
6219
 
5965
6220
  // src/components/ui/Tooltip.tsx
5966
6221
  import {
5967
6222
  useState as useState10,
5968
6223
  useRef as useRef9,
5969
6224
  useEffect as useEffect12,
5970
- useCallback as useCallback8
6225
+ useCallback as useCallback9
5971
6226
  } from "react";
5972
6227
  import { createPortal as createPortal2 } from "react-dom";
5973
6228
  import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
@@ -5976,7 +6231,7 @@ function Tooltip({ content, children, className }) {
5976
6231
  const [position, setPosition] = useState10(null);
5977
6232
  const triggerRef = useRef9(null);
5978
6233
  const bubbleRef = useRef9(null);
5979
- const updatePosition = useCallback8(() => {
6234
+ const updatePosition = useCallback9(() => {
5980
6235
  const trigger = triggerRef.current;
5981
6236
  if (!trigger) return;
5982
6237
  const rect = trigger.getBoundingClientRect();
@@ -6776,7 +7031,7 @@ function ProcessingStep({
6776
7031
  }
6777
7032
  const timingStateRef = useRef10("expected");
6778
7033
  const servedTimingLatchRef = useRef10(false);
6779
- const updatePhaseTimings = useCallback9(
7034
+ const updatePhaseTimings = useCallback10(
6780
7035
  (updater) => {
6781
7036
  setPhaseTimings((previous) => {
6782
7037
  const next = updater(previous);
@@ -7709,8 +7964,11 @@ export {
7709
7964
  SwappedConnectFeeChangedError,
7710
7965
  SUPPORTED_CONNECT_EXCHANGE_COUNT,
7711
7966
  resolveAppFeeBps,
7967
+ isDepositPolicyRefusal,
7712
7968
  PermitDepositSubmitError,
7713
7969
  createDepositService,
7970
+ depositBoundsViolation,
7971
+ describePolicyRefusal,
7714
7972
  rpcUrlFor,
7715
7973
  RpcUrlsProvider,
7716
7974
  useRpcUrls,
@@ -7723,6 +7981,9 @@ export {
7723
7981
  useDepositChainIds,
7724
7982
  useDestinationChains,
7725
7983
  useTargetTokens,
7984
+ useDepositOffer,
7985
+ useDepositBounds,
7986
+ useRestrictedListExclusion,
7726
7987
  useIsDepositChain,
7727
7988
  useChainName,
7728
7989
  useReadClient,