@rhinestone/deposit-modal 0.17.0 → 0.19.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 (62) hide show
  1. package/README.md +109 -0
  2. package/dist/{DepositModalReown-6CXXGQ7X.cjs → DepositModalReown-UITYXJ6W.cjs} +17 -17
  3. package/dist/{DepositModalReown-ELDSTIME.mjs → DepositModalReown-XBNY6QCI.mjs} +4 -4
  4. package/dist/{WithdrawModal-QRMDR5T4.mjs → WithdrawModal-DY6A55OA.mjs} +3 -3
  5. package/dist/WithdrawModal-V42OK7L3.cjs +11 -0
  6. package/dist/bearer-transport-CVHNBgoV.d.cts +14 -0
  7. package/dist/bearer-transport-CVHNBgoV.d.ts +14 -0
  8. package/dist/{chunk-QMGEK623.mjs → chunk-5NWLS5BQ.mjs} +536 -225
  9. package/dist/chunk-ADEWTBSN.cjs +19 -0
  10. package/dist/{chunk-ZHX4QROC.mjs → chunk-BIL4ENC3.mjs} +1 -1
  11. package/dist/{chunk-2W4BJ5I3.cjs → chunk-EG4H7OMC.cjs} +71 -71
  12. package/dist/{chunk-QGNJFDEO.cjs → chunk-HMUGY66O.cjs} +690 -379
  13. package/dist/chunk-ISLVD5HQ.cjs +41 -0
  14. package/dist/{chunk-H66WM5AO.mjs → chunk-NITKDXHR.mjs} +388 -95
  15. package/dist/{chunk-XJMDCBHS.cjs → chunk-OCWSMLOP.cjs} +862 -569
  16. package/dist/chunk-PP4QE32S.mjs +19 -0
  17. package/dist/{chunk-JCWJ6HW7.cjs → chunk-PYOC6K24.cjs} +1 -1
  18. package/dist/{chunk-XCBDJPBD.mjs → chunk-X6N2XPTO.mjs} +1 -1
  19. package/dist/chunk-ZGHVJ2XM.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 +40 -0
  45. package/dist/onramp.d.cts +55 -0
  46. package/dist/onramp.d.ts +55 -0
  47. package/dist/onramp.mjs +40 -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/styles.css +25 -0
  55. package/dist/{types-B9SbZQIG.d.ts → types-BOxa1DE-.d.ts} +40 -4
  56. package/dist/{types-SwGGf0eH.d.cts → types-BShYNpvO.d.cts} +40 -4
  57. package/dist/withdraw.cjs +4 -4
  58. package/dist/withdraw.d.cts +2 -2
  59. package/dist/withdraw.d.ts +2 -2
  60. package/dist/withdraw.mjs +3 -3
  61. package/package.json +35 -5
  62. 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-X6N2XPTO.mjs";
39
39
 
40
40
  // src/components/ui/Modal.tsx
41
41
  import {
@@ -625,6 +625,8 @@ function parseQuotePreview(raw) {
625
625
  }
626
626
  const output = parseQuoteOutput(obj.output);
627
627
  if (!output) return null;
628
+ const input = obj.input === void 0 ? void 0 : parseQuoteOutput(obj.input);
629
+ if (obj.input !== void 0 && !input) return null;
628
630
  const fees = obj.fees;
629
631
  if (typeof fees !== "object" || fees === null) return null;
630
632
  const total = fees.total;
@@ -657,6 +659,7 @@ function parseQuotePreview(raw) {
657
659
  estimatedFillTimeSeconds: obj.estimatedFillTimeSeconds,
658
660
  fees: { total: { usd: totalUsd }, breakdown },
659
661
  output,
662
+ ...input ? { input } : {},
660
663
  ...timing ? { timing } : {}
661
664
  };
662
665
  }
@@ -736,6 +739,31 @@ function resolveAppFeeBps(appFees, sourceChainCaip2, sourceToken) {
736
739
  const bps = override ?? appFees?.feeBps ?? 0;
737
740
  return bps > 0 ? bps : 0;
738
741
  }
742
+ function isRawAmount(value) {
743
+ return typeof value === "string" && /^\d+$/.test(value);
744
+ }
745
+ function isDepositPolicyRefusal(value) {
746
+ return typeof value === "object" && value !== null && "policyRefusal" in value;
747
+ }
748
+ var DEPOSIT_POLICY_REFUSAL_CODES = [
749
+ "TOKEN-3",
750
+ "BALANCE-3",
751
+ "BALANCE-4"
752
+ ];
753
+ var DEPOSIT_PREFLIGHT_TIMEOUT_MS = 1e4;
754
+ async function readPolicyRefusal(response) {
755
+ if (response.status !== 422) return null;
756
+ try {
757
+ const body = await response.json();
758
+ if (!body || typeof body.code !== "string" || !DEPOSIT_POLICY_REFUSAL_CODES.includes(body.code)) {
759
+ return null;
760
+ }
761
+ const message = typeof body.error === "string" && body.error !== "" ? body.error : "This deposit is not accepted.";
762
+ return { policyRefusal: { code: body.code, message } };
763
+ } catch {
764
+ return null;
765
+ }
766
+ }
739
767
  var PermitDepositSubmitError = class extends Error {
740
768
  constructor(message, safeToTransfer) {
741
769
  super(message);
@@ -762,29 +790,60 @@ function createDepositService(baseUrl, options) {
762
790
  if (value.length <= 20) return value;
763
791
  return `${value.slice(0, 10)}...${value.slice(-8)}`;
764
792
  }
793
+ function withPreflightTimeout(label, fallback, run) {
794
+ const controller = new AbortController();
795
+ return new Promise((resolve) => {
796
+ const timer = setTimeout(() => {
797
+ debugLog(debug, scope, `${label}:timeout`, {
798
+ timeoutMs: DEPOSIT_PREFLIGHT_TIMEOUT_MS
799
+ });
800
+ controller.abort();
801
+ resolve(fallback);
802
+ }, DEPOSIT_PREFLIGHT_TIMEOUT_MS);
803
+ run(controller.signal).then(
804
+ (value) => {
805
+ clearTimeout(timer);
806
+ resolve(value);
807
+ },
808
+ () => {
809
+ clearTimeout(timer);
810
+ resolve(fallback);
811
+ }
812
+ );
813
+ });
814
+ }
765
815
  const PORTFOLIO_CACHE_TTL_MS = 3e4;
766
816
  const portfolioCache = /* @__PURE__ */ new Map();
767
817
  let topTokensPromise = null;
768
818
  let chainsPromise = null;
769
- function parseTopTokens(value) {
770
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
819
+ function parseDepositOptions(value) {
820
+ if (typeof value !== "object" || value === null) return null;
821
+ const { restricted, tokens: rawTokens } = value;
822
+ if (typeof restricted !== "boolean") return null;
823
+ if (typeof rawTokens !== "object" || rawTokens === null || Array.isArray(rawTokens)) {
771
824
  return null;
772
825
  }
773
- const parsed = {};
774
- for (const [chainKey, list] of Object.entries(value)) {
826
+ const tokens = {};
827
+ for (const [chainKey, list] of Object.entries(rawTokens)) {
775
828
  if (!Array.isArray(list)) return null;
776
- const tokens = [];
829
+ const parsed = [];
777
830
  for (const entry of list) {
778
831
  if (typeof entry !== "object" || entry === null) return null;
779
- const { symbol, address, decimals } = entry;
832
+ const { symbol, address, decimals, minAmount, maxAmount } = entry;
780
833
  if (typeof symbol !== "string" || typeof address !== "string" || typeof decimals !== "number") {
781
834
  return null;
782
835
  }
783
- tokens.push({ symbol, address, decimals });
836
+ parsed.push({
837
+ symbol,
838
+ address,
839
+ decimals,
840
+ ...isRawAmount(minAmount) && { minAmount },
841
+ ...isRawAmount(maxAmount) && { maxAmount }
842
+ });
784
843
  }
785
- parsed[chainKey] = tokens;
844
+ tokens[chainKey] = parsed;
786
845
  }
787
- return parsed;
846
+ return { restricted, tokens };
788
847
  }
789
848
  async function requestTopTokens() {
790
849
  try {
@@ -796,21 +855,21 @@ function createDepositService(baseUrl, options) {
796
855
  debugLog(debug, scope, "getTopTokens:unavailable", {
797
856
  status: response.status
798
857
  });
799
- return null;
858
+ return { ok: false };
800
859
  }
801
- const data = await response.json();
802
- const tokens = parseTopTokens(data.tokens);
803
- if (!tokens) {
860
+ const options2 = parseDepositOptions(await response.json());
861
+ if (!options2) {
804
862
  debugLog(debug, scope, "getTopTokens:malformed");
805
- return null;
863
+ return { ok: false };
806
864
  }
807
865
  debugLog(debug, scope, "getTopTokens:success", {
808
- chains: Object.keys(tokens).length
866
+ chains: Object.keys(options2.tokens).length,
867
+ restricted: options2.restricted
809
868
  });
810
- return tokens;
869
+ return { ok: true, options: options2 };
811
870
  } catch (error) {
812
871
  debugError(debug, scope, "getTopTokens:error", error);
813
- return null;
872
+ return { ok: false };
814
873
  }
815
874
  }
816
875
  async function requestChains() {
@@ -1295,44 +1354,59 @@ function createDepositService(baseUrl, options) {
1295
1354
  // chain, and a HyperCore source token is a 16-byte Core spot tokenId.
1296
1355
  sourceChainId: params.sourceChainId === "hypercore" ? HYPERCORE_SPOT_CAIP2 : `eip155:${params.sourceChainId}`,
1297
1356
  sourceToken: params.sourceToken,
1298
- amount: params.amount
1357
+ // Each direction carries its own amount field and omits the other, which
1358
+ // is what the processor's "exactly one of" refinement checks. Sending
1359
+ // both is a 400, so spread rather than always emitting `amount`.
1360
+ ...params.direction === "exactOut" ? { direction: "exactOut", amountOut: params.amountOut } : { amount: params.amount }
1299
1361
  };
1300
1362
  debugLog(debug, scope, "getQuotePreview:request", {
1301
1363
  url,
1302
1364
  sourceChainId: body.sourceChainId,
1303
1365
  sourceToken: params.sourceToken,
1304
- amount: params.amount
1366
+ direction: params.direction ?? "exactIn",
1367
+ amount: params.amount,
1368
+ amountOut: params.amountOut
1305
1369
  });
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
1370
+ return withPreflightTimeout("getQuotePreview", null, async (signal) => {
1371
+ try {
1372
+ const response = await fetch(url, {
1373
+ method: "POST",
1374
+ headers: serviceHeaders(),
1375
+ body: JSON.stringify(body),
1376
+ cache: "no-store",
1377
+ signal
1316
1378
  });
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"
1379
+ if (!response.ok) {
1380
+ const refusal = await readPolicyRefusal(response);
1381
+ if (refusal) {
1382
+ debugLog(debug, scope, "getQuotePreview:refused", {
1383
+ code: refusal.policyRefusal.code
1384
+ });
1385
+ return refusal;
1386
+ }
1387
+ debugLog(debug, scope, "getQuotePreview:unavailable", {
1388
+ status: response.status
1389
+ });
1390
+ return null;
1391
+ }
1392
+ const quote = parseQuotePreview(await response.json());
1393
+ if (!quote) {
1394
+ debugLog(debug, scope, "getQuotePreview:unavailable", {
1395
+ status: response.status,
1396
+ reason: "malformed"
1397
+ });
1398
+ return null;
1399
+ }
1400
+ debugLog(debug, scope, "getQuotePreview:success", {
1401
+ settlementLayer: quote.settlementLayer,
1402
+ totalUsd: quote.fees.total.usd
1324
1403
  });
1404
+ return quote;
1405
+ } catch (error) {
1406
+ debugError(debug, scope, "getQuotePreview:error", error);
1325
1407
  return null;
1326
1408
  }
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
- }
1409
+ });
1336
1410
  },
1337
1411
  async preparePermitDeposit(params) {
1338
1412
  const url = apiUrl("/deposits/permit/prepare");
@@ -1343,88 +1417,98 @@ function createDepositService(baseUrl, options) {
1343
1417
  token: params.token,
1344
1418
  amount: params.amount
1345
1419
  });
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"
1420
+ return withPreflightTimeout("preparePermitDeposit", null, async (signal) => {
1421
+ try {
1422
+ const response = await fetch(url, {
1423
+ method: "POST",
1424
+ headers: serviceHeaders(),
1425
+ body: JSON.stringify({ ...params, kind: "auto" }),
1426
+ cache: "no-store",
1427
+ signal
1364
1428
  });
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") {
1429
+ if (!response.ok) {
1430
+ const refusal = await readPolicyRefusal(response);
1431
+ if (refusal) {
1432
+ debugLog(debug, scope, "preparePermitDeposit:refused", {
1433
+ code: refusal.policyRefusal.code
1434
+ });
1435
+ return refusal;
1436
+ }
1437
+ debugLog(debug, scope, "preparePermitDeposit:unavailable", {
1438
+ status: response.status
1439
+ });
1370
1440
  return null;
1371
1441
  }
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
1442
+ const data = await response.json();
1443
+ if (!data || typeof data.available !== "boolean") {
1444
+ debugLog(debug, scope, "preparePermitDeposit:unavailable", {
1445
+ status: response.status,
1446
+ reason: "malformed"
1447
+ });
1448
+ return null;
1449
+ }
1450
+ if (data.available === false) {
1451
+ const reason = data.reason;
1452
+ if (reason !== "permit2-approval-required" && reason !== "unsupported-token" && reason !== "unsupported-owner") {
1453
+ return null;
1405
1454
  }
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: {
1455
+ debugLog(debug, scope, "preparePermitDeposit:unavailable", {
1456
+ status: response.status,
1457
+ reason
1458
+ });
1459
+ const approval = data.approval;
1460
+ const parsedApproval = approval && typeof approval.token === "string" && typeof approval.spender === "string" && typeof approval.minimumAmount === "string" ? {
1461
+ token: approval.token,
1462
+ spender: approval.spender,
1463
+ minimumAmount: approval.minimumAmount
1464
+ } : void 0;
1465
+ return {
1466
+ available: false,
1467
+ reason,
1468
+ ...parsedApproval && { approval: parsedApproval }
1469
+ };
1470
+ }
1471
+ const authorization = data.authorization;
1472
+ if (!authorization || typeof authorization.typedData !== "object" || authorization.typedData === null || typeof authorization.nonce !== "string") {
1473
+ return null;
1474
+ }
1475
+ if (authorization.kind === "erc3009" && typeof authorization.validAfter === "string" && typeof authorization.validBefore === "string") {
1476
+ debugLog(debug, scope, "preparePermitDeposit:success", {
1416
1477
  kind: authorization.kind,
1417
- typedData: authorization.typedData,
1418
- nonce: authorization.nonce,
1419
- deadline: authorization.deadline
1420
- }
1421
- };
1478
+ nonce: authorization.nonce
1479
+ });
1480
+ return {
1481
+ available: true,
1482
+ authorization: {
1483
+ kind: "erc3009",
1484
+ typedData: authorization.typedData,
1485
+ nonce: authorization.nonce,
1486
+ validAfter: authorization.validAfter,
1487
+ validBefore: authorization.validBefore
1488
+ }
1489
+ };
1490
+ }
1491
+ if ((authorization.kind === "erc2612" || authorization.kind === "permit2") && typeof authorization.deadline === "string") {
1492
+ debugLog(debug, scope, "preparePermitDeposit:success", {
1493
+ kind: authorization.kind,
1494
+ nonce: authorization.nonce
1495
+ });
1496
+ return {
1497
+ available: true,
1498
+ authorization: {
1499
+ kind: authorization.kind,
1500
+ typedData: authorization.typedData,
1501
+ nonce: authorization.nonce,
1502
+ deadline: authorization.deadline
1503
+ }
1504
+ };
1505
+ }
1506
+ return null;
1507
+ } catch (error) {
1508
+ debugError(debug, scope, "preparePermitDeposit:error", error);
1509
+ return null;
1422
1510
  }
1423
- return null;
1424
- } catch (error) {
1425
- debugError(debug, scope, "preparePermitDeposit:error", error);
1426
- return null;
1427
- }
1511
+ });
1428
1512
  },
1429
1513
  async submitPermitDeposit(params) {
1430
1514
  const url = apiUrl("/deposits/permit");
@@ -1733,9 +1817,11 @@ function createDepositService(baseUrl, options) {
1733
1817
  },
1734
1818
  getTopTokens() {
1735
1819
  if (!topTokensPromise) {
1736
- const attempt = requestTopTokens().then((tokens) => {
1737
- if (!tokens && topTokensPromise === attempt) topTokensPromise = null;
1738
- return tokens;
1820
+ const attempt = requestTopTokens().then((result) => {
1821
+ if (!result.ok && topTokensPromise === attempt) {
1822
+ topTokensPromise = null;
1823
+ }
1824
+ return result;
1739
1825
  });
1740
1826
  topTokensPromise = attempt;
1741
1827
  }
@@ -1964,6 +2050,7 @@ function useStableRpcUrls(rpcUrls) {
1964
2050
  // src/core/chain-catalog-context.tsx
1965
2051
  import {
1966
2052
  createContext as createContext2,
2053
+ useCallback as useCallback2,
1967
2054
  useContext as useContext2,
1968
2055
  useEffect as useEffect2,
1969
2056
  useMemo as useMemo2,
@@ -1971,6 +2058,59 @@ import {
1971
2058
  useSyncExternalStore
1972
2059
  } from "react";
1973
2060
 
2061
+ // src/core/deposit-bounds.ts
2062
+ import { formatUnits } from "viem";
2063
+ function findListed(options, chain, token) {
2064
+ const normalize2 = (value) => chain === "solana" ? value : value.toLowerCase();
2065
+ return options.tokens[String(chain)]?.find(
2066
+ (listed) => normalize2(listed.address) === normalize2(token)
2067
+ );
2068
+ }
2069
+ function isExcludedByRestrictedList(options, chain, token) {
2070
+ return Boolean(options?.restricted && !findListed(options, chain, token));
2071
+ }
2072
+ function findDepositBounds(options, chain, token) {
2073
+ if (!options?.restricted) return void 0;
2074
+ const entry = findListed(options, chain, token);
2075
+ if (!entry || entry.minAmount === void 0 && entry.maxAmount === void 0) {
2076
+ return void 0;
2077
+ }
2078
+ return {
2079
+ symbol: entry.symbol,
2080
+ decimals: entry.decimals,
2081
+ ...entry.minAmount !== void 0 && { minAmount: BigInt(entry.minAmount) },
2082
+ ...entry.maxAmount !== void 0 && { maxAmount: BigInt(entry.maxAmount) }
2083
+ };
2084
+ }
2085
+ function boundMessage(bounds, bound) {
2086
+ const raw = bounds?.[bound];
2087
+ if (!bounds || raw === void 0) return null;
2088
+ const amount = new Intl.NumberFormat("en-US", {
2089
+ maximumFractionDigits: Math.min(bounds.decimals, 20)
2090
+ }).format(Number(formatUnits(raw, bounds.decimals)));
2091
+ return `${bound === "minAmount" ? "Minimum" : "Maximum"} deposit is ${amount} ${bounds.symbol}`;
2092
+ }
2093
+ function depositBoundsViolation(bounds, units) {
2094
+ if (!bounds || units === null || units <= 0n) return null;
2095
+ if (bounds.minAmount !== void 0 && units < bounds.minAmount) {
2096
+ return boundMessage(bounds, "minAmount");
2097
+ }
2098
+ if (bounds.maxAmount !== void 0 && units > bounds.maxAmount) {
2099
+ return boundMessage(bounds, "maxAmount");
2100
+ }
2101
+ return null;
2102
+ }
2103
+ function describePolicyRefusal(refusal, bounds) {
2104
+ switch (refusal.code) {
2105
+ case "BALANCE-4":
2106
+ return boundMessage(bounds, "minAmount") ?? "This amount is below the minimum deposit";
2107
+ case "BALANCE-3":
2108
+ return boundMessage(bounds, "maxAmount") ?? "This amount is above the maximum deposit";
2109
+ default:
2110
+ return "This token isn't accepted for deposits";
2111
+ }
2112
+ }
2113
+
1974
2114
  // src/core/public-client.ts
1975
2115
  import { createPublicClient, http } from "viem";
1976
2116
  var clientCache = /* @__PURE__ */ new Map();
@@ -2135,6 +2275,9 @@ var EMPTY = {
2135
2275
  viemChains: null,
2136
2276
  isLoading: true,
2137
2277
  shortlist: null,
2278
+ depositOptions: { status: "loading" },
2279
+ retryDepositOptions: () => {
2280
+ },
2138
2281
  clientCapableChainIds: null
2139
2282
  };
2140
2283
  var ChainCatalogContext = createContext2(EMPTY);
@@ -2292,6 +2435,48 @@ function useTargetTokens() {
2292
2435
  };
2293
2436
  }, [catalog, shortlist]);
2294
2437
  }
2438
+ function createDepositOffer(state, retry) {
2439
+ const options = state.status === "ready" ? state.options : null;
2440
+ const restricted = options?.restricted ?? false;
2441
+ const listed = (chain) => options?.tokens[String(chain)];
2442
+ const isChainOffered = (chain) => {
2443
+ if (!options) return false;
2444
+ const entry = listed(chain);
2445
+ return restricted ? (entry?.length ?? 0) > 0 : entry?.length !== 0;
2446
+ };
2447
+ return {
2448
+ status: state.status,
2449
+ restricted,
2450
+ retry,
2451
+ tokensFor: (chain) => {
2452
+ if (!options) return [];
2453
+ const entry = listed(chain);
2454
+ return restricted ? entry ?? [] : entry;
2455
+ },
2456
+ isChainOffered
2457
+ };
2458
+ }
2459
+ function useDepositOffer() {
2460
+ const { depositOptions, retryDepositOptions } = useChainCatalog();
2461
+ return useMemo2(
2462
+ () => createDepositOffer(depositOptions, retryDepositOptions),
2463
+ [depositOptions, retryDepositOptions]
2464
+ );
2465
+ }
2466
+ function useDepositBounds() {
2467
+ const { depositOptions } = useChainCatalog();
2468
+ return useMemo2(() => {
2469
+ const options = depositOptions.status === "ready" ? depositOptions.options : null;
2470
+ return (chain, token) => findDepositBounds(options, chain, token);
2471
+ }, [depositOptions]);
2472
+ }
2473
+ function useRestrictedListExclusion() {
2474
+ const { depositOptions } = useChainCatalog();
2475
+ return useMemo2(() => {
2476
+ const options = depositOptions.status === "ready" ? depositOptions.options : null;
2477
+ return (chain, token) => isExcludedByRestrictedList(options, chain, token);
2478
+ }, [depositOptions]);
2479
+ }
2295
2480
  function useIsDepositChain() {
2296
2481
  const ids = useDepositChainIds();
2297
2482
  const { clientCapableChainIds } = useChainCatalog();
@@ -2384,19 +2569,36 @@ function ChainCatalogProvider({
2384
2569
  const chains = toViemChains(catalog, overrides, viemChains);
2385
2570
  if (chains.length > 0) setRuntimeChains(chains);
2386
2571
  }, [catalog, viemChains, overrides]);
2387
- const [shortlist, setShortlist] = useState(null);
2572
+ const [depositOptions, setDepositOptions] = useState({
2573
+ status: "loading"
2574
+ });
2575
+ const [depositOptionsAttempt, setDepositOptionsAttempt] = useState(0);
2388
2576
  useEffect2(() => {
2389
2577
  let active = true;
2390
- setShortlist(null);
2391
- service.getTopTokens().then((tokens) => {
2392
- if (!active || !tokens) return;
2393
- publishShortlistTokens(tokens);
2394
- setShortlist(tokens);
2395
- });
2578
+ setDepositOptions({ status: "loading" });
2579
+ service.getTopTokens().then(
2580
+ (result) => {
2581
+ if (!active) return;
2582
+ if (!result?.ok) {
2583
+ setDepositOptions({ status: "error" });
2584
+ return;
2585
+ }
2586
+ publishShortlistTokens(result.options.tokens);
2587
+ setDepositOptions({ status: "ready", options: result.options });
2588
+ },
2589
+ () => {
2590
+ if (active) setDepositOptions({ status: "error" });
2591
+ }
2592
+ );
2396
2593
  return () => {
2397
2594
  active = false;
2398
2595
  };
2399
- }, [service]);
2596
+ }, [service, depositOptionsAttempt]);
2597
+ const retryDepositOptions = useCallback2(
2598
+ () => setDepositOptionsAttempt((attempt) => attempt + 1),
2599
+ []
2600
+ );
2601
+ const shortlist = depositOptions.status === "ready" ? depositOptions.options.tokens : null;
2400
2602
  const clientCapableChainIds = useMemo2(() => {
2401
2603
  if (!catalog) return null;
2402
2604
  return new Set(
@@ -2409,9 +2611,19 @@ function ChainCatalogProvider({
2409
2611
  viemChains,
2410
2612
  isLoading,
2411
2613
  shortlist,
2614
+ depositOptions,
2615
+ retryDepositOptions,
2412
2616
  clientCapableChainIds
2413
2617
  }),
2414
- [catalog, viemChains, isLoading, shortlist, clientCapableChainIds]
2618
+ [
2619
+ catalog,
2620
+ viemChains,
2621
+ isLoading,
2622
+ shortlist,
2623
+ depositOptions,
2624
+ retryDepositOptions,
2625
+ clientCapableChainIds
2626
+ ]
2415
2627
  );
2416
2628
  return /* @__PURE__ */ jsx3(ChainCatalogContext.Provider, { value, children });
2417
2629
  }
@@ -2429,7 +2641,7 @@ function useLatestRef(value) {
2429
2641
  // src/core/analytics.tsx
2430
2642
  import {
2431
2643
  createContext as createContext3,
2432
- useCallback as useCallback2,
2644
+ useCallback as useCallback3,
2433
2645
  useContext as useContext3,
2434
2646
  useEffect as useEffect4,
2435
2647
  useMemo as useMemo3,
@@ -2817,7 +3029,7 @@ function AnalyticsSession(props) {
2817
3029
  nowRef.current = props.now ?? (() => /* @__PURE__ */ new Date());
2818
3030
  const modal = props.modal;
2819
3031
  const reportedFailuresRef = useRef3(/* @__PURE__ */ new Set());
2820
- const emitFailure = useCallback2(
3032
+ const emitFailure = useCallback3(
2821
3033
  ({ reason, status, droppedEvents }) => {
2822
3034
  if (reportedFailuresRef.current.has(reason)) return;
2823
3035
  reportedFailuresRef.current.add(reason);
@@ -2867,7 +3079,7 @@ function AnalyticsSession(props) {
2867
3079
  ingestRef.current = createIngest();
2868
3080
  }
2869
3081
  const pendingDisposalRef = useRef3(null);
2870
- const enqueue = useCallback2(
3082
+ const enqueue = useCallback3(
2871
3083
  (event) => {
2872
3084
  try {
2873
3085
  ingestRef.current?.transport?.enqueue(
@@ -2902,7 +3114,7 @@ function AnalyticsSession(props) {
2902
3114
  }, 0);
2903
3115
  };
2904
3116
  }, [emitFailure]);
2905
- const emitDeposit = useCallback2(
3117
+ const emitDeposit = useCallback3(
2906
3118
  (...[payload, fundingMethod]) => {
2907
3119
  if (modal !== "deposit") return;
2908
3120
  const event = {
@@ -2921,7 +3133,7 @@ function AnalyticsSession(props) {
2921
3133
  },
2922
3134
  [enqueue, modal, nowRef, onEventRef]
2923
3135
  );
2924
- const emitWithdraw = useCallback2(
3136
+ const emitWithdraw = useCallback3(
2925
3137
  (payload) => {
2926
3138
  if (modal !== "withdraw") return;
2927
3139
  const event = {
@@ -2937,7 +3149,7 @@ function AnalyticsSession(props) {
2937
3149
  },
2938
3150
  [enqueue, modal, nowRef, onEventRef]
2939
3151
  );
2940
- const emitClaim = useCallback2(
3152
+ const emitClaim = useCallback3(
2941
3153
  (payload) => {
2942
3154
  if (modal !== "claim") return;
2943
3155
  const event = {
@@ -2999,6 +3211,7 @@ function toRecoverableDeposit(row, options = {}) {
2999
3211
  depositId: row.id,
3000
3212
  signer,
3001
3213
  destinationPrefill: signer,
3214
+ depositAddress: row.depositAddress,
3002
3215
  sourceChainId,
3003
3216
  targetChainId,
3004
3217
  token: row.token,
@@ -3006,22 +3219,49 @@ function toRecoverableDeposit(row, options = {}) {
3006
3219
  sourceTxHash: sourceDepositTxHash(row)
3007
3220
  };
3008
3221
  }
3222
+ function isDepositSelfDestination(row, destination) {
3223
+ const depositAddress = row.depositAddress?.trim();
3224
+ const candidate = destination.trim();
3225
+ if (!depositAddress || !candidate) return false;
3226
+ return depositAddress.toLowerCase() === candidate.toLowerCase();
3227
+ }
3009
3228
  function recoveryFailureCause(sourceChainName) {
3010
3229
  return sourceChainName ? `This deposit couldn't be completed. Your funds are still on ${sourceChainName}.` : "This deposit couldn't be completed and your funds were not moved.";
3011
3230
  }
3012
3231
 
3013
3232
  // src/core/history-status.ts
3014
- function normalizeStatus(raw, isSpam) {
3233
+ var UNKNOWN = ["pending", false, []];
3234
+ var STATUS_FACTS = [
3235
+ // `delayed` is held up but still coming on the corridor's schedule.
3236
+ ["pending", true, ["pending", "delayed"]],
3237
+ // Nothing emits `submitted` or `bridging`; they are here so the badge and
3238
+ // the arrival line cannot diverge should a row ever carry them.
3239
+ ["processing", true, ["processing", "submitted", "bridging"]],
3240
+ ["completed", false, ["completed", "complete", "bridge-complete", "processed"]],
3241
+ // `rejected` is recoverable upstream exactly like `failed`, so it must not
3242
+ // read as Pending — a row offering recovery would otherwise claim to still
3243
+ // be on its way.
3244
+ ["failed", false, ["failed", "error", "bridge-failed", "rejected"]],
3245
+ // Where a recovered deposit lands. Terminal and not recoverable again.
3246
+ ["refunded", false, ["refunded"]],
3247
+ // These read Pending but are not arriving: the deposit is bound for a refund
3248
+ // or for manual reconciliation rather than for the recipient, so an arrival
3249
+ // expectation would be a claim about a corridor it never enters.
3250
+ ["pending", false, ["ignored", "expecting_refund", "reconciliation_required"]]
3251
+ ];
3252
+ function lookup(raw) {
3015
3253
  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";
3254
+ return STATUS_FACTS.find((facts) => facts[2].includes(lower)) ?? UNKNOWN;
3255
+ }
3256
+ function normalizeStatus(raw, isSpam) {
3257
+ const [badge] = lookup(raw);
3258
+ return isSpam && badge === "failed" ? "spam" : badge;
3259
+ }
3260
+ function isInFlight(status) {
3261
+ return status === "pending" || status === "processing";
3262
+ }
3263
+ function expectsArrival(raw) {
3264
+ return lookup(raw)[1];
3025
3265
  }
3026
3266
  var STATUS_LABEL = {
3027
3267
  pending: "Pending",
@@ -3041,20 +3281,37 @@ function filterVisibleDeposits(deposits) {
3041
3281
  }
3042
3282
 
3043
3283
  // src/core/history-badge.ts
3044
- function deriveHistoryBadge(deposits, { recipient, canRecover }) {
3284
+ var RECOVERY_INDICATOR_WINDOW_MS = 30 * 24 * 60 * 60 * 1e3;
3285
+ function deriveHistoryBadgeState(deposits, {
3286
+ recipient,
3287
+ canRecover,
3288
+ evaluatedAt = Date.now()
3289
+ }) {
3045
3290
  const visible = filterVisibleDeposits(deposits);
3046
3291
  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;
3292
+ let count = 0;
3293
+ let nextExpiryAt = null;
3294
+ for (const row of visible) {
3295
+ if (toRecoverableDeposit(row, { recipient }) === null) continue;
3296
+ const expiryAt = Date.parse(row.createdAt ?? "") + RECOVERY_INDICATOR_WINDOW_MS;
3297
+ if (!(evaluatedAt < expiryAt)) continue;
3298
+ count += 1;
3299
+ nextExpiryAt = nextExpiryAt === null ? expiryAt : Math.min(nextExpiryAt, expiryAt);
3300
+ }
3301
+ if (count > 0) {
3302
+ return {
3303
+ badge: { state: "needs-you", count },
3304
+ nextExpiryAt
3305
+ };
3306
+ }
3307
+ }
3308
+ const inProgress = visible.filter(
3309
+ (row) => isInFlight(normalizeStatus(row.status, row.isSpam))
3310
+ ).length;
3311
+ return {
3312
+ badge: inProgress > 0 ? { state: "progress", count: inProgress } : null,
3313
+ nextExpiryAt: null
3314
+ };
3058
3315
  }
3059
3316
  function historyBadgeAriaLabel(badge) {
3060
3317
  if (!badge) return "Deposit history";
@@ -3094,11 +3351,11 @@ function HistoryEntryButton({
3094
3351
  }
3095
3352
 
3096
3353
  // src/components/history/HistoryOverlay.tsx
3097
- import { useCallback as useCallback6, useEffect as useEffect10, useMemo as useMemo6, useRef as useRef7, useState as useState8 } from "react";
3354
+ import { useCallback as useCallback7, useEffect as useEffect10, useMemo as useMemo6, useRef as useRef7, useState as useState8 } from "react";
3098
3355
 
3099
3356
  // src/components/history/DepositHistoryPanel.tsx
3100
3357
  import {
3101
- useCallback as useCallback3,
3358
+ useCallback as useCallback4,
3102
3359
  useEffect as useEffect5,
3103
3360
  useId,
3104
3361
  useMemo as useMemo4,
@@ -3428,14 +3685,13 @@ function advancePendingTimingState(previous, next) {
3428
3685
  }
3429
3686
 
3430
3687
  // src/core/history-timing.ts
3431
- var TIMED_HISTORY_STATUSES = /* @__PURE__ */ new Set(["pending", "processing", "delayed"]);
3432
3688
  function resolveHistoryTiming({
3433
3689
  status,
3434
3690
  timing,
3435
3691
  createdAt,
3436
3692
  nowMs
3437
3693
  }) {
3438
- if (!TIMED_HISTORY_STATUSES.has(status) || !timing || !createdAt) {
3694
+ if (!expectsArrival(status) || !timing || !createdAt) {
3439
3695
  return void 0;
3440
3696
  }
3441
3697
  const createdAtMs = Date.parse(createdAt);
@@ -3559,7 +3815,7 @@ function DepositHistoryPanel({
3559
3815
  useEffect5(() => {
3560
3816
  rootRef.current?.toggleAttribute("inert", Boolean(obscured));
3561
3817
  }, [obscured]);
3562
- const handleKeyDown = useCallback3(
3818
+ const handleKeyDown = useCallback4(
3563
3819
  (e) => {
3564
3820
  if (obscured) return;
3565
3821
  if (e.key === "Escape") {
@@ -3860,7 +4116,7 @@ function HistoryCard({
3860
4116
  DepositHistoryPanel.displayName = "DepositHistoryPanel";
3861
4117
 
3862
4118
  // src/components/history/HistoryFallbackFlow.tsx
3863
- import { useCallback as useCallback4, useEffect as useEffect7, useRef as useRef5, useState as useState5 } from "react";
4119
+ import { useCallback as useCallback5, useEffect as useEffect7, useRef as useRef5, useState as useState5 } from "react";
3864
4120
 
3865
4121
  // src/components/ui/ListRow.tsx
3866
4122
  import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -4044,10 +4300,10 @@ function RecoveryLookupScreen({
4044
4300
  RecoveryLookupScreen.displayName = "RecoveryLookupScreen";
4045
4301
 
4046
4302
  // src/components/recovery/recovery-display.ts
4047
- import { formatUnits as formatUnits2 } from "viem";
4303
+ import { formatUnits as formatUnits3 } from "viem";
4048
4304
 
4049
4305
  // src/core/token-amount.ts
4050
- import { formatUnits } from "viem";
4306
+ import { formatUnits as formatUnits2 } from "viem";
4051
4307
  var STABLECOIN_SYMBOLS = /* @__PURE__ */ new Set([
4052
4308
  "USDC",
4053
4309
  "USDT",
@@ -4111,7 +4367,7 @@ function formatTokenAmount(amount, symbol) {
4111
4367
  function tokenAmountToNumber(rawAmount, decimals) {
4112
4368
  if (decimals === void 0) return void 0;
4113
4369
  try {
4114
- const value = Number(formatUnits(BigInt(rawAmount), decimals));
4370
+ const value = Number(formatUnits2(BigInt(rawAmount), decimals));
4115
4371
  return Number.isFinite(value) ? value : void 0;
4116
4372
  } catch {
4117
4373
  return void 0;
@@ -4198,18 +4454,18 @@ function describeRecoveryDeposit(params) {
4198
4454
  const decimals = getTokenDecimalsByAddress(token, params.chainId);
4199
4455
  const symbol = getTokenSymbol(token, params.chainId);
4200
4456
  const raw = toBigInt(params.amountRaw);
4201
- const human = Number(formatUnits2(raw, decimals));
4457
+ const human = Number(formatUnits3(raw, decimals));
4202
4458
  return {
4203
4459
  symbol,
4204
4460
  decimals,
4205
- amount: formatTokenAmount(human, symbol) ?? formatUnits2(raw, decimals),
4461
+ amount: formatTokenAmount(human, symbol) ?? formatUnits3(raw, decimals),
4206
4462
  chainName: getChainName(params.chainId),
4207
4463
  chainIcon: getChainIcon(params.chainId)
4208
4464
  };
4209
4465
  }
4210
4466
  function formatRecoveryAmount(amountRaw, decimals, symbol) {
4211
4467
  const raw = toBigInt(amountRaw);
4212
- return formatTokenAmount(Number(formatUnits2(raw, decimals)), symbol) ?? formatUnits2(raw, decimals);
4468
+ return formatTokenAmount(Number(formatUnits3(raw, decimals)), symbol) ?? formatUnits3(raw, decimals);
4213
4469
  }
4214
4470
  function toBigInt(value) {
4215
4471
  try {
@@ -4697,7 +4953,7 @@ function HistoryFallbackFlow({
4697
4953
  epochRef.current += 1;
4698
4954
  };
4699
4955
  }, [recipient]);
4700
- const reportSelection = useCallback4(
4956
+ const reportSelection = useCallback5(
4701
4957
  (match, autoSelected) => {
4702
4958
  analytics.selectDeposit({
4703
4959
  depositId: match.depositId,
@@ -4707,7 +4963,7 @@ function HistoryFallbackFlow({
4707
4963
  },
4708
4964
  [analytics]
4709
4965
  );
4710
- const lookup = useCallback4(
4966
+ const lookup2 = useCallback5(
4711
4967
  async (txHash) => {
4712
4968
  const epoch = epochRef.current;
4713
4969
  const isCurrentLookup = () => epochRef.current === epoch;
@@ -4750,7 +5006,7 @@ function HistoryFallbackFlow({
4750
5006
  },
4751
5007
  [analytics, service, recipient, onFound, reportSelection]
4752
5008
  );
4753
- const selectMatch = useCallback4(
5009
+ const selectMatch = useCallback5(
4754
5010
  (match) => {
4755
5011
  analytics.completeStep("select");
4756
5012
  reportSelection(match, false);
@@ -4758,7 +5014,7 @@ function HistoryFallbackFlow({
4758
5014
  },
4759
5015
  [analytics, onFound, reportSelection]
4760
5016
  );
4761
- const handleKeyDown = useCallback4(
5017
+ const handleKeyDown = useCallback5(
4762
5018
  (e) => {
4763
5019
  if (e.key === "Escape") {
4764
5020
  e.stopPropagation();
@@ -4837,7 +5093,7 @@ function HistoryFallbackFlow({
4837
5093
  hint: "The transaction hash from the wallet or exchange you sent from.",
4838
5094
  loading,
4839
5095
  error,
4840
- onSubmit: lookup,
5096
+ onSubmit: lookup2,
4841
5097
  onInvalid: () => analytics.friction("lookup_transaction_hash_invalid", "lookup")
4842
5098
  }
4843
5099
  )
@@ -4853,7 +5109,7 @@ function newestFirst(rows) {
4853
5109
  HistoryFallbackFlow.displayName = "HistoryFallbackFlow";
4854
5110
 
4855
5111
  // src/components/history/HistoryRecoveryFlow.tsx
4856
- import { useCallback as useCallback5, useEffect as useEffect9, useRef as useRef6, useState as useState7 } from "react";
5112
+ import { useCallback as useCallback6, useEffect as useEffect9, useRef as useRef6, useState as useState7 } from "react";
4857
5113
 
4858
5114
  // src/components/recovery/RecoveryOutcomeScreen.tsx
4859
5115
  import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
@@ -4927,9 +5183,11 @@ function RecoveryReviewScreen({
4927
5183
  txHash,
4928
5184
  defaultDestination,
4929
5185
  destinationHint,
5186
+ depositAddress,
4930
5187
  submitting,
4931
5188
  onSubmit,
4932
- onDestinationInvalid
5189
+ onDestinationInvalid,
5190
+ onDestinationIsDepositAddress
4933
5191
  }) {
4934
5192
  const { symbol, amount, chainName, chainIcon } = describeRecoveryDeposit({
4935
5193
  chainId,
@@ -4940,13 +5198,30 @@ function RecoveryReviewScreen({
4940
5198
  const [destination, setDestination] = useState6(
4941
5199
  defaultDestination ?? ""
4942
5200
  );
4943
- const destinationValid = isAddress2(destination);
5201
+ const isSelfDestination = isDepositSelfDestination(
5202
+ { depositAddress },
5203
+ destination
5204
+ );
5205
+ const destinationValid = isAddress2(destination) && !isSelfDestination;
4944
5206
  const onDestinationInvalidRef = useLatestRef(onDestinationInvalid);
5207
+ const onDestinationIsDepositAddressRef = useLatestRef(
5208
+ onDestinationIsDepositAddress
5209
+ );
4945
5210
  useEffect8(() => {
4946
- if (destination.length > 0 && !destinationValid) {
5211
+ if (destination.length === 0) return;
5212
+ if (isSelfDestination) {
5213
+ onDestinationIsDepositAddressRef.current?.();
5214
+ } else if (!destinationValid) {
4947
5215
  onDestinationInvalidRef.current?.();
4948
5216
  }
4949
- }, [destination, destinationValid, onDestinationInvalidRef]);
5217
+ }, [
5218
+ destination,
5219
+ destinationValid,
5220
+ isSelfDestination,
5221
+ onDestinationInvalidRef,
5222
+ onDestinationIsDepositAddressRef
5223
+ ]);
5224
+ const selfDestinationError = `${shortenAddress(destination)} is this deposit's own address. Enter a different address.`;
4950
5225
  const txUrl = txHash ? explorerTxUrl(chainId, txHash) : null;
4951
5226
  const txShort = txHash ? shortenHash2(txHash) : "\u2014";
4952
5227
  return /* @__PURE__ */ jsxs14("div", { className: "rs-screen", children: [
@@ -5003,7 +5278,7 @@ function RecoveryReviewScreen({
5003
5278
  value: destination,
5004
5279
  onChange: (e) => setDestination(e.target.value),
5005
5280
  hint: destinationHint,
5006
- error: destination.length > 0 && !destinationValid ? "Enter a valid address" : void 0
5281
+ error: isSelfDestination ? selfDestinationError : destination.length > 0 && !destinationValid ? "Enter a valid address" : void 0
5007
5282
  }
5008
5283
  ),
5009
5284
  /* @__PURE__ */ jsx20(
@@ -5057,7 +5332,7 @@ function HistoryRecoveryFlow({
5057
5332
  token: deposit.token,
5058
5333
  amountRaw: deposit.amountRaw
5059
5334
  });
5060
- const submit = useCallback5(
5335
+ const submit = useCallback6(
5061
5336
  async (destination) => {
5062
5337
  setPhase("submitting");
5063
5338
  onSubmittingChange(true);
@@ -5129,7 +5404,7 @@ function HistoryRecoveryFlow({
5129
5404
  symbol
5130
5405
  ]
5131
5406
  );
5132
- const retry = useCallback5(() => {
5407
+ const retry = useCallback6(() => {
5133
5408
  const reason = lastFailureRef.current;
5134
5409
  if (reason) {
5135
5410
  lastFailureRef.current = null;
@@ -5181,9 +5456,14 @@ function HistoryRecoveryFlow({
5181
5456
  txHash: deposit.sourceTxHash,
5182
5457
  defaultDestination: deposit.destinationPrefill,
5183
5458
  destinationHint: "Prefilled with the account this deposit was for. Editable.",
5459
+ depositAddress: deposit.depositAddress,
5184
5460
  submitting,
5185
5461
  onSubmit: submit,
5186
- onDestinationInvalid: () => analytics.friction("refund_destination_invalid", "review")
5462
+ onDestinationInvalid: () => analytics.friction("refund_destination_invalid", "review"),
5463
+ onDestinationIsDepositAddress: () => analytics.friction(
5464
+ "refund_destination_is_deposit_address",
5465
+ "review"
5466
+ )
5187
5467
  }
5188
5468
  ),
5189
5469
  phase === "success" && outcome && /* @__PURE__ */ jsx21(
@@ -5226,7 +5506,7 @@ function HistoryOverlay({
5226
5506
  const [fallbackOpen, setFallbackOpen] = useState8(false);
5227
5507
  const [attempt, setAttempt] = useState8(null);
5228
5508
  const claimControllerRef = useRef7(null);
5229
- const beginAttempt = useCallback6(
5509
+ const beginAttempt = useCallback7(
5230
5510
  (entrySource) => {
5231
5511
  const id = randomUuid();
5232
5512
  const started = {
@@ -5244,7 +5524,7 @@ function HistoryOverlay({
5244
5524
  },
5245
5525
  [analytics]
5246
5526
  );
5247
- const endAttempt = useCallback6(
5527
+ const endAttempt = useCallback7(
5248
5528
  (source, reason) => {
5249
5529
  const claim = claimControllerRef.current;
5250
5530
  claim?.abandon(reason);
@@ -5254,12 +5534,12 @@ function HistoryOverlay({
5254
5534
  },
5255
5535
  []
5256
5536
  );
5257
- const openFallback = useCallback6(() => {
5537
+ const openFallback = useCallback7(() => {
5258
5538
  const started = beginAttempt("history_fallback");
5259
5539
  analytics.fallbackOpened(started.id);
5260
5540
  setFallbackOpen(true);
5261
5541
  }, [analytics, beginAttempt]);
5262
- const closeFallback = useCallback6(
5542
+ const closeFallback = useCallback7(
5263
5543
  (via) => {
5264
5544
  endAttempt(via === "escape" ? "escape" : "history_back", "back");
5265
5545
  setFallbackOpen(false);
@@ -5272,7 +5552,7 @@ function HistoryOverlay({
5272
5552
  setFallbackOpen(false);
5273
5553
  if (fallbackOpen && !recovering) endAttempt("host_controlled", "back");
5274
5554
  }, [canLookup, fallbackOpen, recovering, endAttempt]);
5275
- const startRecoveryFromRow = useCallback6(
5555
+ const startRecoveryFromRow = useCallback7(
5276
5556
  (deposit) => {
5277
5557
  const started = beginAttempt("history_row");
5278
5558
  analytics.recoveryStarted(started.id, "history_row", deposit.depositId);
@@ -5280,7 +5560,7 @@ function HistoryOverlay({
5280
5560
  },
5281
5561
  [analytics, beginAttempt, controller]
5282
5562
  );
5283
- const startRecoveryFromLookup = useCallback6(
5563
+ const startRecoveryFromLookup = useCallback7(
5284
5564
  (deposit) => {
5285
5565
  if (attempt) {
5286
5566
  analytics.recoveryStarted(
@@ -5294,11 +5574,11 @@ function HistoryOverlay({
5294
5574
  },
5295
5575
  [analytics, attempt, controller]
5296
5576
  );
5297
- const cancelRecovery = useCallback6(() => {
5577
+ const cancelRecovery = useCallback7(() => {
5298
5578
  endAttempt("history_back", "back");
5299
5579
  controller.cancelRecovery();
5300
5580
  }, [controller, endAttempt]);
5301
- const closeModal = useCallback6(() => {
5581
+ const closeModal = useCallback7(() => {
5302
5582
  endAttempt("header_button", "modal_close");
5303
5583
  onCloseModal();
5304
5584
  }, [endAttempt, onCloseModal]);
@@ -5389,7 +5669,8 @@ function HistoryOverlay({
5389
5669
  }
5390
5670
 
5391
5671
  // src/components/history/useHistoryPanel.ts
5392
- import { useCallback as useCallback7, useEffect as useEffect11, useMemo as useMemo7, useRef as useRef8, useState as useState9 } from "react";
5672
+ import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo7, useRef as useRef8, useState as useState9 } from "react";
5673
+ var MAX_TIMEOUT_MS = 2147483647;
5393
5674
  function sortByCreatedAtDesc(items) {
5394
5675
  return [...items].sort((a, b) => {
5395
5676
  const at = a.createdAt ? Date.parse(a.createdAt) : 0;
@@ -5417,7 +5698,7 @@ function useHistoryPanel({
5417
5698
  const requestRef = useRef8(0);
5418
5699
  const [recovering, setRecovering] = useState9(null);
5419
5700
  const [recoverySubmitting, setRecoverySubmitting] = useState9(false);
5420
- const fetchHistory = useCallback7(
5701
+ const fetchHistory = useCallback8(
5421
5702
  async (mode = "initial") => {
5422
5703
  if (!recipient) return;
5423
5704
  const isInitial = mode === "initial";
@@ -5467,27 +5748,40 @@ function useHistoryPanel({
5467
5748
  },
5468
5749
  [recipient, service]
5469
5750
  );
5470
- const badge = useMemo7(
5471
- () => deriveHistoryBadge(deposits, { recipient, canRecover }),
5472
- [deposits, recipient, canRecover]
5473
- );
5474
- const openPanel = useCallback7(() => {
5751
+ const badgeState2 = deriveHistoryBadgeState(deposits, {
5752
+ recipient,
5753
+ canRecover
5754
+ });
5755
+ const badge = badgeState2.badge;
5756
+ useEffect11(() => {
5757
+ if (!isOpen || badgeState2.nextExpiryAt === null) return;
5758
+ const delay = Math.min(
5759
+ badgeState2.nextExpiryAt - Date.now(),
5760
+ MAX_TIMEOUT_MS
5761
+ );
5762
+ const timeout = setTimeout(
5763
+ () => setDeposits((current) => current.slice()),
5764
+ delay
5765
+ );
5766
+ return () => clearTimeout(timeout);
5767
+ }, [isOpen, badgeState2.nextExpiryAt]);
5768
+ const openPanel = useCallback8(() => {
5475
5769
  setIsPanelOpen(true);
5476
5770
  analytics?.visitOpened(badge);
5477
5771
  if (!loadedRef.current || staleRef.current) {
5478
5772
  fetchHistory("initial");
5479
5773
  }
5480
5774
  }, [analytics, badge, fetchHistory]);
5481
- const closePanel = useCallback7(() => {
5775
+ const closePanel = useCallback8(() => {
5482
5776
  if (recovering) return;
5483
5777
  setIsPanelOpen(false);
5484
5778
  analytics?.visitEnded();
5485
5779
  }, [analytics, recovering]);
5486
- const loadMore = useCallback7(() => {
5780
+ const loadMore = useCallback8(() => {
5487
5781
  analytics?.loadMore(filterVisibleDeposits(deposits).length);
5488
5782
  fetchHistory("more");
5489
5783
  }, [analytics, deposits, fetchHistory]);
5490
- const markStale = useCallback7(() => {
5784
+ const markStale = useCallback8(() => {
5491
5785
  staleRef.current = true;
5492
5786
  if (isOpen) {
5493
5787
  fetchHistory("initial");
@@ -5523,10 +5817,10 @@ function useHistoryPanel({
5523
5817
  setRecovering(null);
5524
5818
  setRecoverySubmitting(false);
5525
5819
  }, [isOpen, analytics]);
5526
- const invalidate = useCallback7(() => {
5820
+ const invalidate = useCallback8(() => {
5527
5821
  staleRef.current = true;
5528
5822
  }, []);
5529
- const cancelRecovery = useCallback7(() => setRecovering(null), []);
5823
+ const cancelRecovery = useCallback8(() => setRecovering(null), []);
5530
5824
  return useMemo7(
5531
5825
  () => ({
5532
5826
  deposits,
@@ -5960,14 +6254,14 @@ function isSubmissionUncertain(error) {
5960
6254
  }
5961
6255
 
5962
6256
  // src/components/steps/ProcessingStep.tsx
5963
- import { useCallback as useCallback9, useEffect as useEffect14, useRef as useRef10, useState as useState13 } from "react";
6257
+ import { useCallback as useCallback10, useEffect as useEffect14, useRef as useRef10, useState as useState13 } from "react";
5964
6258
 
5965
6259
  // src/components/ui/Tooltip.tsx
5966
6260
  import {
5967
6261
  useState as useState10,
5968
6262
  useRef as useRef9,
5969
6263
  useEffect as useEffect12,
5970
- useCallback as useCallback8
6264
+ useCallback as useCallback9
5971
6265
  } from "react";
5972
6266
  import { createPortal as createPortal2 } from "react-dom";
5973
6267
  import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
@@ -5976,7 +6270,7 @@ function Tooltip({ content, children, className }) {
5976
6270
  const [position, setPosition] = useState10(null);
5977
6271
  const triggerRef = useRef9(null);
5978
6272
  const bubbleRef = useRef9(null);
5979
- const updatePosition = useCallback8(() => {
6273
+ const updatePosition = useCallback9(() => {
5980
6274
  const trigger = triggerRef.current;
5981
6275
  if (!trigger) return;
5982
6276
  const rect = trigger.getBoundingClientRect();
@@ -6636,6 +6930,10 @@ function getCurrentPhaseId(state, phaseTimings) {
6636
6930
  if (state.lastEvent?.type === "deposit-received") return "received";
6637
6931
  return "confirming";
6638
6932
  }
6933
+ function getCompletionDestinationAmount(event) {
6934
+ const amount = event?.data?.destination?.amount;
6935
+ return typeof amount === "string" ? amount : void 0;
6936
+ }
6639
6937
  function getCompletionDestinationTxHash(event) {
6640
6938
  const eventData = event?.data;
6641
6939
  const swapTxHash = eventData?.swap?.transactionHash;
@@ -6646,7 +6944,8 @@ function getTerminalProcessingOutcome(event) {
6646
6944
  if (event?.type === "post-bridge-swap-complete") {
6647
6945
  return {
6648
6946
  type: "complete",
6649
- destinationTxHash: getCompletionDestinationTxHash(event)
6947
+ destinationTxHash: getCompletionDestinationTxHash(event),
6948
+ destinationAmount: getCompletionDestinationAmount(event)
6650
6949
  };
6651
6950
  }
6652
6951
  if (event?.type === "post-bridge-swap-failed") {
@@ -6658,7 +6957,8 @@ function getTerminalProcessingOutcome(event) {
6658
6957
  if (event?.type === "bridge-complete") {
6659
6958
  return {
6660
6959
  type: "complete",
6661
- destinationTxHash: getCompletionDestinationTxHash(event)
6960
+ destinationTxHash: getCompletionDestinationTxHash(event),
6961
+ destinationAmount: getCompletionDestinationAmount(event)
6662
6962
  };
6663
6963
  }
6664
6964
  if (event?.type === "bridge-failed") {
@@ -6776,7 +7076,7 @@ function ProcessingStep({
6776
7076
  }
6777
7077
  const timingStateRef = useRef10("expected");
6778
7078
  const servedTimingLatchRef = useRef10(false);
6779
- const updatePhaseTimings = useCallback9(
7079
+ const updatePhaseTimings = useCallback10(
6780
7080
  (updater) => {
6781
7081
  setPhaseTimings((previous) => {
6782
7082
  const next = updater(previous);
@@ -6845,7 +7145,8 @@ function ProcessingStep({
6845
7145
  sourceDecimals: context.sourceDecimals,
6846
7146
  amountUsd: context.amountUsd,
6847
7147
  targetChain: context.targetChain,
6848
- targetToken: context.targetToken
7148
+ targetToken: context.targetToken,
7149
+ destinationAmount: outcome.destinationAmount
6849
7150
  }
6850
7151
  );
6851
7152
  return;
@@ -6986,7 +7287,8 @@ function ProcessingStep({
6986
7287
  eventForCurrentTx,
6987
7288
  txHash
6988
7289
  );
6989
- const destinationTxHash2 = eventForCurrentTx.data?.destination?.transactionHash;
7290
+ const destination = eventForCurrentTx.data?.destination;
7291
+ const destinationTxHash2 = destination?.transactionHash;
6990
7292
  debugLog(debug, "processing", "state:complete", {
6991
7293
  txHash,
6992
7294
  sourceTxHash: sourceTxHash2,
@@ -7001,7 +7303,8 @@ function ProcessingStep({
7001
7303
  sourceDecimals: context.sourceDecimals,
7002
7304
  amountUsd: context.amountUsd,
7003
7305
  targetChain: context.targetChain,
7004
- targetToken: context.targetToken
7306
+ targetToken: context.targetToken,
7307
+ destinationAmount: typeof destination?.amount === "string" ? destination.amount : void 0
7005
7308
  });
7006
7309
  return;
7007
7310
  }
@@ -7087,8 +7390,8 @@ function ProcessingStep({
7087
7390
  const lastEvent = state.lastEvent;
7088
7391
  const failureMessage = state.type === "failed" ? state.message : void 0;
7089
7392
  const timelineNowMs = phaseTimings.endedAt ?? Date.now();
7090
- const flowNoun = flowLabel === "withdraw" ? "withdrawal" : "deposit";
7091
- const flowCapitalized = flowLabel === "withdraw" ? "Withdrawal" : "Deposit";
7393
+ const flowNoun = flowLabel === "withdraw" ? "withdrawal" : flowLabel === "payment" ? "payment" : "deposit";
7394
+ const flowCapitalized = flowLabel === "withdraw" ? "Withdrawal" : flowLabel === "payment" ? "Payment" : "Deposit";
7092
7395
  const destinationTxHash = lastEvent?.data?.destination?.transactionHash || null;
7093
7396
  const sourceTxHash = getSettledSourceTxHash(lastEvent, txHash);
7094
7397
  const sourceDetails = getEventSourceDetails(lastEvent);
@@ -7542,7 +7845,8 @@ function computeRequestKey(input) {
7542
7845
  recipient: normalizeKeyToken(input.recipient),
7543
7846
  outputTokenRules: input.outputTokenRules ?? null,
7544
7847
  rejectUnmapped: input.rejectUnmapped ?? null,
7545
- forceRegister: input.forceRegister ?? false
7848
+ forceRegister: input.forceRegister ?? false,
7849
+ salt: input.salt ?? null
7546
7850
  });
7547
7851
  }
7548
7852
  function computeCacheKey(input) {
@@ -7551,7 +7855,8 @@ function computeCacheKey(input) {
7551
7855
  targetToken: normalizeKeyToken(input.targetToken),
7552
7856
  recipient: normalizeKeyToken(input.recipient),
7553
7857
  outputTokenRules: input.outputTokenRules ?? null,
7554
- rejectUnmapped: input.rejectUnmapped ?? null
7858
+ rejectUnmapped: input.rejectUnmapped ?? null,
7859
+ salt: input.salt ?? null
7555
7860
  });
7556
7861
  }
7557
7862
  function computeIsCacheable(input) {
@@ -7583,7 +7888,7 @@ async function runAccountSetup(input, deps) {
7583
7888
  );
7584
7889
  }
7585
7890
  try {
7586
- const salt = deriveAccountSalt(input);
7891
+ const salt = input.salt ?? deriveAccountSalt(input);
7587
7892
  debugLog(debug, "account-setup", "setup:start", {
7588
7893
  salt,
7589
7894
  recipient: input.recipient,
@@ -7709,8 +8014,11 @@ export {
7709
8014
  SwappedConnectFeeChangedError,
7710
8015
  SUPPORTED_CONNECT_EXCHANGE_COUNT,
7711
8016
  resolveAppFeeBps,
8017
+ isDepositPolicyRefusal,
7712
8018
  PermitDepositSubmitError,
7713
8019
  createDepositService,
8020
+ depositBoundsViolation,
8021
+ describePolicyRefusal,
7714
8022
  rpcUrlFor,
7715
8023
  RpcUrlsProvider,
7716
8024
  useRpcUrls,
@@ -7723,6 +8031,9 @@ export {
7723
8031
  useDepositChainIds,
7724
8032
  useDestinationChains,
7725
8033
  useTargetTokens,
8034
+ useDepositOffer,
8035
+ useDepositBounds,
8036
+ useRestrictedListExclusion,
7726
8037
  useIsDepositChain,
7727
8038
  useChainName,
7728
8039
  useReadClient,