@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,3 +1,15 @@
1
+ import {
2
+ MULTICALL3_ADDRESS,
3
+ POLYMARKET_COLLATERAL_OFFRAMP_ADDRESS,
4
+ POLYMARKET_ICON_URL,
5
+ POLYMARKET_POLYGON_CHAIN_ID,
6
+ POLYMARKET_PUSD_ADDRESS,
7
+ POLYMARKET_USDCE_ADDRESS,
8
+ SAFE_ABI,
9
+ executePolymarketSafeTransfer,
10
+ fetchPolymarketProxyWallet,
11
+ readPolymarketBalances
12
+ } from "./chunk-FTWKAJGN.mjs";
1
13
  import {
2
14
  AnalyticsSession,
3
15
  AppleIcon,
@@ -47,6 +59,8 @@ import {
47
59
  currencyFormatter,
48
60
  debugError,
49
61
  debugLog,
62
+ depositBoundsViolation,
63
+ describePolicyRefusal,
50
64
  failureMessageForEvent,
51
65
  fetchTokenPriceUsd,
52
66
  formatExpectedDuration,
@@ -61,6 +75,7 @@ import {
61
75
  getExchangeLogoSrc,
62
76
  getPublicClient,
63
77
  isDepositEvent,
78
+ isDepositPolicyRefusal,
64
79
  isDisplayStablecoinSymbol,
65
80
  isFailedEvent,
66
81
  isHistoryEntryVisible,
@@ -79,27 +94,18 @@ import {
79
94
  useChainCatalog,
80
95
  useChainName,
81
96
  useDepositAnalyticsEmitter,
97
+ useDepositBounds,
82
98
  useDepositChainIds,
99
+ useDepositOffer,
83
100
  useExplorerTxUrl,
84
101
  useHistoryPanel,
85
102
  useIsDepositChain,
86
103
  useLatestRef,
104
+ useRestrictedListExclusion,
87
105
  useRpcUrls,
88
106
  useStableRpcUrls,
89
107
  warnRemovedProps
90
- } from "./chunk-QMGEK623.mjs";
91
- import {
92
- MULTICALL3_ADDRESS,
93
- POLYMARKET_COLLATERAL_OFFRAMP_ADDRESS,
94
- POLYMARKET_ICON_URL,
95
- POLYMARKET_POLYGON_CHAIN_ID,
96
- POLYMARKET_PUSD_ADDRESS,
97
- POLYMARKET_USDCE_ADDRESS,
98
- SAFE_ABI,
99
- executePolymarketSafeTransfer,
100
- fetchPolymarketProxyWallet,
101
- readPolymarketBalances
102
- } from "./chunk-FTWKAJGN.mjs";
108
+ } from "./chunk-5NWLS5BQ.mjs";
103
109
  import {
104
110
  HYPERCORE_CHAIN_ID,
105
111
  HYPERCORE_MIN_DEPOSIT_USD,
@@ -123,7 +129,6 @@ import {
123
129
  isStablecoinSymbol,
124
130
  isVirtualDestination,
125
131
  parseEvmChainId,
126
- publishShortlistTokens,
127
132
  targetChainToChainId,
128
133
  toEvmCaip2
129
134
  } from "./chunk-EAGMY3QE.mjs";
@@ -723,6 +728,7 @@ function AssetSelectStep({
723
728
  const [loading, setLoading] = useState2(true);
724
729
  const [error, setError] = useState2(null);
725
730
  const isDepositChain = useIsDepositChain();
731
+ const isExcludedByRestrictedList = useRestrictedListExclusion();
726
732
  const { catalog } = useChainCatalog();
727
733
  const chainName = useChainName();
728
734
  const defaultAssetId = useMemo(() => {
@@ -804,6 +810,9 @@ function AssetSelectStep({
804
810
  return false;
805
811
  }
806
812
  const isNativeToken = a.token.toLowerCase() === NATIVE_TOKEN_ADDRESS;
813
+ if (isNativeToken && isExcludedByRestrictedList(a.chainId, a.token)) {
814
+ return false;
815
+ }
807
816
  if (!isNativeToken && !(typeof a.balanceUsd === "number" && a.balanceUsd >= 0.01))
808
817
  return false;
809
818
  return true;
@@ -817,7 +826,7 @@ function AssetSelectStep({
817
826
  if (balB < balA) return -1;
818
827
  return 0;
819
828
  });
820
- }, [assets, isDepositChain]);
829
+ }, [assets, isDepositChain, isExcludedByRestrictedList]);
821
830
  useEffect2(() => {
822
831
  if (!defaultAssetId || selectedAssetId) return;
823
832
  if (rows.some((asset) => asset.id === defaultAssetId)) {
@@ -1074,6 +1083,16 @@ function AnimatedAmountInput({
1074
1083
 
1075
1084
  // src/components/steps/AmountStep.tsx
1076
1085
  import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
1086
+ function requiredSourceAmount(quote, decimals) {
1087
+ const raw = quote?.input?.amount;
1088
+ if (raw === void 0) return null;
1089
+ try {
1090
+ return formatUnits2(BigInt(raw), decimals);
1091
+ } catch {
1092
+ return null;
1093
+ }
1094
+ }
1095
+ var PAYMENT_QUOTE_REFRESH_MS = 3e4;
1077
1096
  var PRESETS = [
1078
1097
  { value: 25, label: "25%" },
1079
1098
  { value: 50, label: "50%" },
@@ -1104,6 +1123,8 @@ function AmountStep({
1104
1123
  sourcePriceUnavailable = false,
1105
1124
  appBalanceUsd,
1106
1125
  title = "Wallet deposit",
1126
+ payment,
1127
+ account,
1107
1128
  onContinue,
1108
1129
  onCtaClick,
1109
1130
  service
@@ -1111,6 +1132,7 @@ function AmountStep({
1111
1132
  const [amount, setAmount] = useState3("");
1112
1133
  const [balance, setBalance] = useState3(null);
1113
1134
  const [error, setError] = useState3(null);
1135
+ const [paymentQuote, setPaymentQuote] = useState3("loading");
1114
1136
  const hasAttemptedSwitch = useRef2(false);
1115
1137
  const hasAppliedDefaultRef = useRef2(false);
1116
1138
  const isMaxDefault = defaultAmount?.trim().toLowerCase() === "max";
@@ -1232,7 +1254,15 @@ function AmountStep({
1232
1254
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
1233
1255
  }, [amount]);
1234
1256
  const amountUsd = numericAmount;
1257
+ const lockedAmount = payment && typeof paymentQuote !== "string" ? requiredSourceAmount(paymentQuote, asset.decimals) : null;
1235
1258
  const sourceAmountUnits = useMemo2(() => {
1259
+ if (lockedAmount !== null) {
1260
+ try {
1261
+ return parseUnits(lockedAmount, asset.decimals);
1262
+ } catch {
1263
+ return null;
1264
+ }
1265
+ }
1236
1266
  if (numericAmount <= 0) return null;
1237
1267
  const raw = isSourceStablecoin || !hasPricing ? numericAmount : numericAmount / (tokenPriceUsd ?? 1);
1238
1268
  try {
@@ -1240,7 +1270,14 @@ function AmountStep({
1240
1270
  } catch {
1241
1271
  return null;
1242
1272
  }
1243
- }, [numericAmount, isSourceStablecoin, hasPricing, tokenPriceUsd, asset.decimals]);
1273
+ }, [
1274
+ lockedAmount,
1275
+ numericAmount,
1276
+ isSourceStablecoin,
1277
+ hasPricing,
1278
+ tokenPriceUsd,
1279
+ asset.decimals
1280
+ ]);
1244
1281
  const minDepositUsd = isHyperCoreDestination(targetChain) ? Math.max(HYPERCORE_MIN_DEPOSIT_USD, uiConfig?.minDepositUsd ?? 0) : uiConfig?.minDepositUsd ?? null;
1245
1282
  const maxDepositUsd = uiConfig?.maxDepositUsd ?? null;
1246
1283
  const isBelowMin = minDepositUsd !== null && numericAmount > 0 && amountUsd < minDepositUsd;
@@ -1248,7 +1285,42 @@ function AmountStep({
1248
1285
  const exceedsBalance = Boolean(
1249
1286
  sourceAmountUnits !== null && balance !== null && sourceAmountUnits > balance
1250
1287
  );
1288
+ const boundsFor = useDepositBounds();
1289
+ const boundViolation = depositBoundsViolation(
1290
+ boundsFor(asset.chainId, asset.token),
1291
+ sourceAmountUnits
1292
+ );
1251
1293
  const balanceAfterUsd = appBalanceUsd !== void 0 ? appBalanceUsd + numericAmount : null;
1294
+ useEffect3(() => {
1295
+ if (!payment || !account) return;
1296
+ let active = true;
1297
+ setPaymentQuote("loading");
1298
+ const load = async () => {
1299
+ const result = await service.getQuotePreview({
1300
+ account,
1301
+ sourceChainId: asset.chainId,
1302
+ sourceToken: asset.token,
1303
+ direction: "exactOut",
1304
+ amountOut: payment.amount
1305
+ });
1306
+ if (!active) return;
1307
+ setPaymentQuote(
1308
+ result && "output" in result && result.input ? result : "unavailable"
1309
+ );
1310
+ };
1311
+ void load();
1312
+ const timer = setInterval(load, PAYMENT_QUOTE_REFRESH_MS);
1313
+ return () => {
1314
+ active = false;
1315
+ clearInterval(timer);
1316
+ };
1317
+ }, [
1318
+ payment,
1319
+ account,
1320
+ asset.chainId,
1321
+ asset.token,
1322
+ service
1323
+ ]);
1252
1324
  const handlePresetClick = (percentage) => {
1253
1325
  if (balance === null) return;
1254
1326
  onCtaClick?.(percentage === 100 ? "Max" : `${percentage}%`);
@@ -1271,7 +1343,20 @@ function AmountStep({
1271
1343
  if (error) setError(null);
1272
1344
  };
1273
1345
  const handleContinue = async () => {
1274
- if (chainMismatch) return;
1346
+ if (chainMismatch || boundViolation) return;
1347
+ if (payment) {
1348
+ if (!lockedAmount) return;
1349
+ if (balance !== null && parseUnits(lockedAmount, asset.decimals) > balance) {
1350
+ setError("Insufficient balance");
1351
+ return;
1352
+ }
1353
+ setError(null);
1354
+ onCtaClick?.("continue");
1355
+ const lockedUnits = Number(lockedAmount);
1356
+ const usd = isSourceStablecoin || !hasPricing ? lockedUnits : lockedUnits * (tokenPriceUsd ?? 1);
1357
+ onContinue(lockedAmount, usd.toString(), balance ?? void 0);
1358
+ return;
1359
+ }
1275
1360
  let effectiveAmount = numericAmount;
1276
1361
  if (isBelowMin && minDepositUsd !== null) {
1277
1362
  effectiveAmount = minDepositUsd;
@@ -1305,11 +1390,17 @@ function AmountStep({
1305
1390
  balance ?? void 0
1306
1391
  );
1307
1392
  };
1308
- const continueLabel = exceedsBalance ? "Insufficient balance" : isAboveMax ? "Continue with max allowed deposit" : isBelowMin ? "Continue with minimum deposit" : "Continue";
1393
+ const continueLabel = exceedsBalance ? "Insufficient balance" : boundViolation ?? (isAboveMax ? "Continue with max allowed deposit" : isBelowMin ? "Continue with minimum deposit" : "Continue");
1309
1394
  const knowsBalance = address !== void 0;
1310
1395
  const needsPricing = !knowsBalance && !isSourceStablecoin;
1311
1396
  const blockedOnPricing = needsPricing && !hasPricing;
1312
- const continueDisabled = chainMismatch || exceedsBalance || blockedOnPricing || numericAmount === 0 && !isBelowMin || knowsBalance && balance === null;
1397
+ const continueDisabled = payment ? (
1398
+ // No input to validate — only whether the quote resolved and whether the
1399
+ // payer can cover it. Bounds still apply: they are the project's ingest
1400
+ // limits, so an invoice under a chain's minimum genuinely cannot be paid
1401
+ // that way and the payer has to be told, not left on a dead button.
1402
+ chainMismatch || lockedAmount === null || exceedsBalance || boundViolation !== null || knowsBalance && balance === null
1403
+ ) : chainMismatch || exceedsBalance || boundViolation !== null || blockedOnPricing || numericAmount === 0 && !isBelowMin || knowsBalance && balance === null;
1313
1404
  const chainName = useChainName();
1314
1405
  const sourceChainName = chainName(asset.chainId);
1315
1406
  const targetChainName = chainName(targetChainId);
@@ -1318,7 +1409,7 @@ function AmountStep({
1318
1409
  /* @__PURE__ */ jsx8(BodyHeader, { icon: /* @__PURE__ */ jsx8(WalletIcon, {}), title }),
1319
1410
  /* @__PURE__ */ jsxs6("div", { className: "rs-amount-section", children: [
1320
1411
  /* @__PURE__ */ jsxs6("div", { className: "rs-amount-display", children: [
1321
- /* @__PURE__ */ jsx8(
1412
+ payment ? /* @__PURE__ */ jsx8("div", { className: "rs-amount-fixed", "aria-label": "Amount to pay", children: lockedAmount ? `${tokenFormatter.format(Number(lockedAmount))} ${asset.symbol}` : "\u2014" }) : /* @__PURE__ */ jsx8(
1322
1413
  AnimatedAmountInput,
1323
1414
  {
1324
1415
  value: amount,
@@ -1345,7 +1436,7 @@ function AmountStep({
1345
1436
  ] })
1346
1437
  ] })
1347
1438
  ] }),
1348
- knowsBalance && /* @__PURE__ */ jsx8("div", { className: "rs-amount-presets", children: PRESETS.map((preset) => /* @__PURE__ */ jsx8(
1439
+ knowsBalance && !payment && /* @__PURE__ */ jsx8("div", { className: "rs-amount-presets", children: PRESETS.map((preset) => /* @__PURE__ */ jsx8(
1349
1440
  "button",
1350
1441
  {
1351
1442
  type: "button",
@@ -1385,6 +1476,14 @@ function AmountStep({
1385
1476
  /* @__PURE__ */ jsx8("span", { className: "rs-amount-detail-value", children: currencyFormatter.format(balanceAfterUsd) })
1386
1477
  ] })
1387
1478
  ] }),
1479
+ payment && paymentQuote === "unavailable" && /* @__PURE__ */ jsxs6(Callout, { variant: "error", children: [
1480
+ "This payment can't be priced from ",
1481
+ asset.symbol,
1482
+ " on",
1483
+ " ",
1484
+ sourceChainName,
1485
+ " right now. Try another asset."
1486
+ ] }),
1388
1487
  blockedOnPricing && sourcePriceUnavailable && /* @__PURE__ */ jsxs6(Callout, { variant: "error", children: [
1389
1488
  asset.symbol,
1390
1489
  " can't be priced right now, so this deposit can't be estimated. You can still send any amount to the deposit address."
@@ -1642,7 +1741,9 @@ function ConfirmStep({
1642
1741
  onError,
1643
1742
  onDepositSubmitted,
1644
1743
  onSubmittingChange,
1645
- executeTransfer
1744
+ executeTransfer,
1745
+ title = "Review deposit",
1746
+ routeThroughAccount = false
1646
1747
  }) {
1647
1748
  const [isSubmitting, setIsSubmitting] = useState4(false);
1648
1749
  const [error, setError] = useState4(null);
@@ -1651,6 +1752,14 @@ function ConfirmStep({
1651
1752
  const [permitUnavailable, setPermitUnavailable] = useState4(null);
1652
1753
  const [permitDisabled, setPermitDisabled] = useState4(false);
1653
1754
  const [submissionUncertain, setSubmissionUncertain] = useState4(false);
1755
+ const [previewRefusal, setPreviewRefusal] = useState4(null);
1756
+ const [permitRefusal, setPermitRefusal] = useState4(null);
1757
+ const [previewSettledKey, setPreviewSettledKey] = useState4(
1758
+ null
1759
+ );
1760
+ const [permitSettledKey, setPermitSettledKey] = useState4(
1761
+ null
1762
+ );
1654
1763
  const hasAttemptedSwitch = useRef3(false);
1655
1764
  const onSubmittingChangeRef = useRef3(onSubmittingChange);
1656
1765
  onSubmittingChangeRef.current = onSubmittingChange;
@@ -1690,7 +1799,7 @@ function ConfirmStep({
1690
1799
  const { display: feeDisplay, struck: feesFullySponsored } = quoteFeeRow(
1691
1800
  quote?.fees
1692
1801
  );
1693
- const directTransfer = sameRoute && !mustHaveQuote && !executeTransfer;
1802
+ const directTransfer = sameRoute && !mustHaveQuote && !executeTransfer && !routeThroughAccount;
1694
1803
  const permitEligible = enableGaslessDeposit && !isNativeAsset(asset) && !isDappImportAsset(asset) && !executeTransfer && !directTransfer && asset.chainId !== HYPERCORE_CHAIN_ID;
1695
1804
  const permitRequestKey = JSON.stringify([
1696
1805
  smartAccount.toLowerCase(),
@@ -1701,6 +1810,21 @@ function ConfirmStep({
1701
1810
  address.toLowerCase()
1702
1811
  ]);
1703
1812
  const permit = permitEligible && !permitDisabled && permitResult?.requestKey === permitRequestKey ? permitResult.prepared : null;
1813
+ const boundsFor = useDepositBounds();
1814
+ const activeRefusal = (previewRefusal?.requestKey === quoteRequestKey ? previewRefusal.refusal : null) ?? (permitRefusal?.requestKey === permitRequestKey ? permitRefusal.refusal : null);
1815
+ const blockedReason = activeRefusal ? describePolicyRefusal(
1816
+ activeRefusal,
1817
+ boundsFor(quoteSource.chainId, quoteSource.token)
1818
+ ) : null;
1819
+ const requestsAmount = (() => {
1820
+ try {
1821
+ return parseUnits2(amount, asset.decimals) > 0n;
1822
+ } catch {
1823
+ return false;
1824
+ }
1825
+ })();
1826
+ const previewPending = setupReady && requestsAmount && previewSettledKey !== quoteRequestKey;
1827
+ const permitPending = permitEligible && !permitDisabled && requestsAmount && permitSettledKey !== permitRequestKey;
1704
1828
  const receiveDisplay = (() => {
1705
1829
  if (directTransfer) return `${formattedSendAmount} ${asset.symbol}`;
1706
1830
  if (quote) {
@@ -1757,11 +1881,22 @@ function ConfirmStep({
1757
1881
  amount: units.toString()
1758
1882
  }).then((result) => {
1759
1883
  if (cancelled) return;
1884
+ setPreviewSettledKey(quoteRequestKey);
1885
+ if (isDepositPolicyRefusal(result)) {
1886
+ setQuoteResult(null);
1887
+ setPreviewRefusal({
1888
+ requestKey: quoteRequestKey,
1889
+ refusal: result.policyRefusal
1890
+ });
1891
+ return;
1892
+ }
1893
+ setPreviewRefusal(null);
1760
1894
  setQuoteResult(
1761
1895
  result === null ? null : { requestKey: quoteRequestKey, quote: result }
1762
1896
  );
1763
1897
  }).catch(() => {
1764
1898
  if (cancelled) return;
1899
+ setPreviewSettledKey(quoteRequestKey);
1765
1900
  setQuoteResult(null);
1766
1901
  });
1767
1902
  return () => {
@@ -1794,7 +1929,16 @@ function ConfirmStep({
1794
1929
  amount: units.toString(),
1795
1930
  owner: address
1796
1931
  }).then((prepared) => {
1797
- if (cancelled || prepared === null) return;
1932
+ if (cancelled) return;
1933
+ setPermitSettledKey(permitRequestKey);
1934
+ if (prepared === null) return;
1935
+ if (isDepositPolicyRefusal(prepared)) {
1936
+ setPermitRefusal({
1937
+ requestKey: permitRequestKey,
1938
+ refusal: prepared.policyRefusal
1939
+ });
1940
+ return;
1941
+ }
1798
1942
  if (!prepared.available) {
1799
1943
  setPermitUnavailable(prepared.reason);
1800
1944
  return;
@@ -1819,6 +1963,7 @@ function ConfirmStep({
1819
1963
  prepared: authorization
1820
1964
  });
1821
1965
  }).catch(() => {
1966
+ if (!cancelled) setPermitSettledKey(permitRequestKey);
1822
1967
  });
1823
1968
  return () => {
1824
1969
  cancelled = true;
@@ -1860,6 +2005,11 @@ function ConfirmStep({
1860
2005
  setError("Insufficient balance");
1861
2006
  return;
1862
2007
  }
2008
+ if (previewPending || permitPending) return;
2009
+ if (blockedReason) {
2010
+ setError(blockedReason);
2011
+ return;
2012
+ }
1863
2013
  if (!quote && mustHaveQuote) {
1864
2014
  setError("Quote unavailable \u2014 please try again");
1865
2015
  return;
@@ -1982,7 +2132,7 @@ function ConfirmStep({
1982
2132
  };
1983
2133
  return /* @__PURE__ */ jsxs8("div", { className: "rs-screen", children: [
1984
2134
  /* @__PURE__ */ jsxs8("div", { className: "rs-screen-body rs-screen-body--gap-32", children: [
1985
- /* @__PURE__ */ jsx10(BodyHeader, { icon: /* @__PURE__ */ jsx10(WalletIcon, {}), title: "Review deposit" }),
2135
+ /* @__PURE__ */ jsx10(BodyHeader, { icon: /* @__PURE__ */ jsx10(WalletIcon, {}), title }),
1986
2136
  /* @__PURE__ */ jsx10(
1987
2137
  ReviewDetails,
1988
2138
  {
@@ -1998,7 +2148,7 @@ function ConfirmStep({
1998
2148
  fee: { display: feeDisplay, struck: feesFullySponsored }
1999
2149
  }
2000
2150
  ),
2001
- error && /* @__PURE__ */ jsx10(Callout, { variant: "error", children: error }),
2151
+ (error ?? blockedReason) && /* @__PURE__ */ jsx10(Callout, { variant: "error", children: error ?? blockedReason }),
2002
2152
  /* @__PURE__ */ jsx10(
2003
2153
  Button,
2004
2154
  {
@@ -2007,7 +2157,9 @@ function ConfirmStep({
2007
2157
  loadingText: permit ? "Waiting for signature..." : "Submitting transaction...",
2008
2158
  disabled: (
2009
2159
  // An earlier send whose outcome is unknown. Never re-offered.
2010
- submissionUncertain || // Signing is off-chain, so a chain mismatch only blocks the
2160
+ submissionUncertain || // The deposit on screen would be rejected at ingest.
2161
+ blockedReason !== null || // A refusal for what is on screen may still be in flight.
2162
+ previewPending || permitPending || // Signing is off-chain, so a chain mismatch only blocks the
2011
2163
  // transfer path.
2012
2164
  chainMismatch && !permit || !amount || Number(amount) <= 0 || // Fee-charging route: block until the authoritative quote resolves
2013
2165
  // (covers both loading and an unavailable preview). Fee-free routes
@@ -2049,6 +2201,7 @@ function QrReviewStep({
2049
2201
  const [quote, setQuote] = useState5({ state: "loading", value: null });
2050
2202
  const targetChainId = targetChainToChainId(targetChain);
2051
2203
  const targetSymbol = getTargetTokenSymbol(targetToken, targetChainId);
2204
+ const boundsFor = useDepositBounds();
2052
2205
  useEffect5(() => {
2053
2206
  let cancelled = false;
2054
2207
  let units;
@@ -2070,6 +2223,14 @@ function QrReviewStep({
2070
2223
  amount: units.toString()
2071
2224
  }).then((result) => {
2072
2225
  if (cancelled) return;
2226
+ if (isDepositPolicyRefusal(result)) {
2227
+ setQuote({
2228
+ state: "refused",
2229
+ value: null,
2230
+ refusal: result.policyRefusal
2231
+ });
2232
+ return;
2233
+ }
2073
2234
  setQuote(
2074
2235
  result ? { state: "ready", value: result } : { state: "unavailable", value: null }
2075
2236
  );
@@ -2100,7 +2261,7 @@ function QrReviewStep({
2100
2261
  const expectedSeconds = quote.value?.timing?.expectedSeconds ?? (quote.value && Number.isFinite(quote.value.estimatedFillTimeSeconds) ? quote.value.estimatedFillTimeSeconds : void 0);
2101
2262
  const estimatedTime = expectedSeconds !== void 0 ? formatExpectedDuration(expectedSeconds) : quote.state === "loading" ? "\u2026" : "\u2014";
2102
2263
  const { display: quotedFee, struck } = quoteFeeRow(quote.value?.fees);
2103
- const feeDisplay = quote.state === "unavailable" ? "\u2014" : quotedFee;
2264
+ const feeDisplay = quote.state === "unavailable" || quote.state === "refused" ? "\u2014" : quotedFee;
2104
2265
  return /* @__PURE__ */ jsxs9("div", { className: "rs-screen", children: [
2105
2266
  /* @__PURE__ */ jsxs9("div", { className: "rs-screen-body rs-screen-body--gap-32", children: [
2106
2267
  /* @__PURE__ */ jsx11(BodyHeader, { icon: /* @__PURE__ */ jsx11(TransferCryptoIcon, {}), title: "Fee estimate" }),
@@ -2119,6 +2280,10 @@ function QrReviewStep({
2119
2280
  fee: { display: feeDisplay, struck }
2120
2281
  }
2121
2282
  ),
2283
+ quote.state === "refused" && quote.refusal && /* @__PURE__ */ jsx11(Callout, { variant: "error", children: describePolicyRefusal(
2284
+ quote.refusal,
2285
+ boundsFor(asset.chainId, asset.token)
2286
+ ) }),
2122
2287
  /* @__PURE__ */ jsx11(Button, { onClick: onClose, fullWidth: true, children: "Close" })
2123
2288
  ] }),
2124
2289
  /* @__PURE__ */ jsx11(PoweredBy, {})
@@ -2136,6 +2301,7 @@ import {
2136
2301
  lazy,
2137
2302
  Suspense
2138
2303
  } from "react";
2304
+ import { formatUnits as formatUnits3 } from "viem";
2139
2305
 
2140
2306
  // src/components/steps/DepositAddressSkeleton.tsx
2141
2307
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
@@ -2333,7 +2499,7 @@ function DepositNotification({
2333
2499
  const onCompleteRef = useLatestRef(onComplete);
2334
2500
  const onFailedRef = useLatestRef(onFailed);
2335
2501
  const handleComplete = useCallback(
2336
- (destTxHash) => {
2502
+ (destTxHash, destAmount) => {
2337
2503
  if (completedRef.current) return;
2338
2504
  completedRef.current = true;
2339
2505
  setDestinationTxHash(destTxHash ?? null);
@@ -2346,7 +2512,8 @@ function DepositNotification({
2346
2512
  sourceToken: context.sourceToken,
2347
2513
  sourceDecimals: context.sourceDecimals,
2348
2514
  targetChain: context.targetChain,
2349
- targetToken: context.targetToken
2515
+ targetToken: context.targetToken,
2516
+ destinationAmount: destAmount
2350
2517
  });
2351
2518
  },
2352
2519
  [depositContextRef, onCompleteRef, txHash]
@@ -2375,8 +2542,11 @@ function DepositNotification({
2375
2542
  const lastEvent = data.lastEvent;
2376
2543
  const eventForTx = isEventForTx(lastEvent, txHash) ? lastEvent : void 0;
2377
2544
  if (eventForTx?.type === "bridge-complete") {
2378
- const destTx = eventForTx.data?.destination?.transactionHash;
2379
- handleComplete(destTx);
2545
+ const destination = eventForTx.data?.destination;
2546
+ handleComplete(
2547
+ destination?.transactionHash,
2548
+ typeof destination?.amount === "string" ? destination.amount : void 0
2549
+ );
2380
2550
  return;
2381
2551
  }
2382
2552
  if (eventForTx?.type === "bridge-failed") {
@@ -2544,6 +2714,15 @@ function DepositNotification({
2544
2714
  );
2545
2715
  }
2546
2716
 
2717
+ // src/core/payment-uri.ts
2718
+ function paymentRequestUri(input) {
2719
+ const { to, chainId, token, amount } = input;
2720
+ if (token.toLowerCase() === NATIVE_TOKEN_ADDRESS.toLowerCase() || token.toLowerCase() === "0x0") {
2721
+ return `ethereum:${to}@${chainId}?value=${amount}`;
2722
+ }
2723
+ return `ethereum:${token}@${chainId}/transfer?address=${to}&uint256=${amount}`;
2724
+ }
2725
+
2547
2726
  // src/core/deposit-source-identity.ts
2548
2727
  var SOLANA_NATIVE_MINT = "11111111111111111111111111111111";
2549
2728
  var EVM_ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
@@ -2565,6 +2744,7 @@ function resolveSourceTokenIdentity(input) {
2565
2744
  const isNative = raw.toLowerCase() === "native" || raw.toLowerCase() === EVM_ZERO_ADDRESS;
2566
2745
  return isNative ? SOLANA_NATIVE_MINT : raw;
2567
2746
  }
2747
+ if (raw.toLowerCase() === "0x0") return EVM_ZERO_ADDRESS;
2568
2748
  return raw.toLowerCase();
2569
2749
  }
2570
2750
 
@@ -2574,6 +2754,7 @@ var QRCode = lazy(
2574
2754
  () => import("./QRCode-5RGIZZHL.mjs").then((m) => ({ default: m.QRCode }))
2575
2755
  );
2576
2756
  var POLL_INTERVAL_MS = 4e3;
2757
+ var PAYMENT_QUOTE_REFRESH_MS2 = 3e4;
2577
2758
  function isRecord(value) {
2578
2759
  return typeof value === "object" && value !== null;
2579
2760
  }
@@ -2653,17 +2834,15 @@ function chainLabel(chainId, catalogChain) {
2653
2834
  const name = catalogChain?.name ?? getChainName(chainId);
2654
2835
  return name.replace(/\s+(One|Mainnet|Smart Chain|Chain)$/i, "");
2655
2836
  }
2656
- function computeTokensForChain(chainId, topTokens, catalogChain) {
2657
- const fromBackend = topTokens?.[String(chainId)]?.map((t) => t.symbol);
2658
- if (fromBackend) return fromBackend;
2837
+ function computeTokensForChain(chainId, offer, catalogChain) {
2838
+ const listed = offer.tokensFor(chainId)?.map((t) => t.symbol);
2839
+ if (listed) return listed;
2659
2840
  return Array.isArray(catalogChain?.supportedTokens) ? catalogChain.supportedTokens.map((t) => t.symbol) : [];
2660
2841
  }
2661
- function selectionAsAsset(chainId, symbol, topTokens, catalogChain) {
2842
+ function selectionAsAsset(chainId, symbol, offer, catalogChain) {
2662
2843
  if (typeof chainId !== "number" || !symbol) return void 0;
2663
- const fromShortlist = topTokens?.[String(chainId)]?.find(
2664
- (t) => t.symbol === symbol
2665
- );
2666
- const fromCatalog = Array.isArray(catalogChain?.supportedTokens) ? catalogChain.supportedTokens.find((t) => t.symbol === symbol) : void 0;
2844
+ const fromShortlist = offer.tokensFor(chainId)?.find((t) => t.symbol === symbol);
2845
+ const fromCatalog = !offer.restricted && Array.isArray(catalogChain?.supportedTokens) ? catalogChain.supportedTokens.find((t) => t.symbol === symbol) : void 0;
2667
2846
  const address = fromShortlist?.address ?? fromCatalog?.address;
2668
2847
  const decimals = fromShortlist?.decimals ?? fromCatalog?.decimals;
2669
2848
  if (!address || decimals === void 0) return void 0;
@@ -2703,30 +2882,16 @@ function DepositAddressStep({
2703
2882
  onDepositComplete,
2704
2883
  onDepositFailed,
2705
2884
  onAddressShown,
2885
+ payment,
2706
2886
  onCopyAddress,
2707
2887
  onReviewDeposit,
2708
2888
  onError
2709
2889
  }) {
2710
2890
  const hasSolana = Boolean(solanaDepositAddress);
2711
- const [topTokens, setTopTokens] = useState7(null);
2712
- const [topTokensSettled, setTopTokensSettled] = useState7(false);
2713
- useEffect7(() => {
2714
- let active = true;
2715
- setTopTokens(null);
2716
- setTopTokensSettled(false);
2717
- service.getTopTokens().then((t) => {
2718
- if (!active) return;
2719
- setTopTokens(t);
2720
- setTopTokensSettled(true);
2721
- if (t) publishShortlistTokens(t);
2722
- });
2723
- return () => {
2724
- active = false;
2725
- };
2726
- }, [service]);
2891
+ const offer = useDepositOffer();
2727
2892
  const BASE_CHAIN_ID2 = 8453;
2728
2893
  const evmChainIds = useDepositChainIds();
2729
- const { catalog, isLoading: catalogLoading } = useChainCatalog();
2894
+ const { catalog, isLoading: catalogLoading, shortlist } = useChainCatalog();
2730
2895
  const catalogFor = useCallback2(
2731
2896
  (chain) => {
2732
2897
  if (!catalog) return void 0;
@@ -2743,9 +2908,9 @@ function DepositAddressStep({
2743
2908
  if (acceptsDeposits("hypercore")) extras.push("hypercore");
2744
2909
  options.splice(1, 0, ...extras);
2745
2910
  return options.filter(
2746
- (chain) => topTokens?.[String(chain)]?.length !== 0 && computeTokensForChain(chain, topTokens, catalogFor(chain)).length > 0
2911
+ (chain) => offer.isChainOffered(chain) && computeTokensForChain(chain, offer, catalogFor(chain)).length > 0
2747
2912
  );
2748
- }, [evmChainIds, hasSolana, topTokens, catalogFor]);
2913
+ }, [evmChainIds, hasSolana, offer, catalogFor]);
2749
2914
  const preferredChainId = (options) => options.includes(BASE_CHAIN_ID2) ? BASE_CHAIN_ID2 : options[0] ?? BASE_CHAIN_ID2;
2750
2915
  const [sourceChainId, setSourceChainId] = useState7(() => initialSource?.chainId ?? preferredChainId(chainOptions));
2751
2916
  const chainChosenByUser = useRef5(initialSource !== void 0);
@@ -2759,17 +2924,17 @@ function DepositAddressStep({
2759
2924
  setSourceChainId(pendingChain2);
2760
2925
  return;
2761
2926
  }
2762
- if (catalogLoading || topTokens === null) return;
2927
+ if (catalogLoading || offer.status !== "ready") return;
2763
2928
  pendingChainRef.current = void 0;
2764
2929
  }
2765
2930
  const stale = chainChosenByUser.current ? !chainOptions.includes(sourceChainId) : sourceChainId !== preferredChainId(chainOptions);
2766
2931
  if (stale) setSourceChainId(preferredChainId(chainOptions));
2767
- }, [chainOptions, sourceChainId, catalogLoading, topTokens]);
2932
+ }, [chainOptions, sourceChainId, catalogLoading, offer.status]);
2768
2933
  const isSolana = sourceChainId === "solana";
2769
2934
  const isHyperCore = sourceChainId === "hypercore";
2770
2935
  const tokensForChain = useMemo3(
2771
- () => computeTokensForChain(sourceChainId, topTokens, catalogFor(sourceChainId)),
2772
- [sourceChainId, topTokens, catalogFor]
2936
+ () => computeTokensForChain(sourceChainId, offer, catalogFor(sourceChainId)),
2937
+ [sourceChainId, offer, catalogFor]
2773
2938
  );
2774
2939
  const defaultToken = pickFallbackToken(
2775
2940
  tokensForChain,
@@ -2803,17 +2968,56 @@ function DepositAddressStep({
2803
2968
  const baselineTxHashRef = useRef5(void 0);
2804
2969
  const notificationIdRef = useRef5(0);
2805
2970
  const displayAddress = isSolana && solanaDepositAddress ? solanaDepositAddress : smartAccount;
2971
+ const [paymentUnits, setPaymentUnits] = useState7("loading");
2806
2972
  const sourceTokenIdentity = useMemo3(
2807
2973
  () => resolveSourceTokenIdentity({
2808
2974
  chain: sourceChainId,
2809
2975
  symbol: sourceTokenSymbol,
2810
- shortlist: topTokens,
2811
- catalogTokens: catalogFor(sourceChainId)?.supportedTokens
2976
+ shortlist,
2977
+ catalogTokens: offer.restricted ? void 0 : catalogFor(sourceChainId)?.supportedTokens
2812
2978
  }),
2813
- [sourceChainId, sourceTokenSymbol, topTokens, catalogFor]
2979
+ [sourceChainId, sourceTokenSymbol, shortlist, offer.restricted, catalogFor]
2814
2980
  );
2981
+ useEffect7(() => {
2982
+ if (!payment) return;
2983
+ if (typeof sourceChainId !== "number" || !sourceTokenIdentity) {
2984
+ setPaymentUnits("unavailable");
2985
+ return;
2986
+ }
2987
+ setPaymentUnits("loading");
2988
+ let active = true;
2989
+ const load = async () => {
2990
+ const quote = await service.getQuotePreview({
2991
+ account: smartAccount,
2992
+ sourceChainId,
2993
+ sourceToken: sourceTokenIdentity,
2994
+ direction: "exactOut",
2995
+ amountOut: payment.amount
2996
+ });
2997
+ if (!active) return;
2998
+ const answered = quote && "output" in quote ? quote.input?.amount : void 0;
2999
+ setPaymentUnits(answered ?? "unavailable");
3000
+ };
3001
+ void load();
3002
+ const timer = setInterval(load, PAYMENT_QUOTE_REFRESH_MS2);
3003
+ return () => {
3004
+ active = false;
3005
+ clearInterval(timer);
3006
+ };
3007
+ }, [payment, sourceChainId, sourceTokenIdentity, smartAccount, service]);
3008
+ const paymentDecimals = typeof sourceChainId === "number" && sourceTokenIdentity ? findTokenDecimals(sourceTokenIdentity, sourceChainId) : void 0;
3009
+ const paymentAmountUnits = payment && paymentUnits !== "loading" && paymentUnits !== "unavailable" ? paymentUnits : null;
3010
+ const paymentDisplay = paymentAmountUnits !== null && paymentDecimals !== void 0 ? formatUnits3(BigInt(paymentAmountUnits), paymentDecimals) : null;
3011
+ const qrValue = paymentAmountUnits !== null && typeof sourceChainId === "number" && sourceTokenIdentity ? paymentRequestUri({
3012
+ to: displayAddress,
3013
+ chainId: sourceChainId,
3014
+ token: sourceTokenIdentity,
3015
+ amount: paymentAmountUnits
3016
+ }) : displayAddress;
3017
+ const withholdPaymentQr = Boolean(payment) && paymentAmountUnits === null;
3018
+ const paymentUnquotable = Boolean(payment) && paymentUnits === "unavailable";
2815
3019
  const pendingChain = pendingChainRef.current;
2816
- const sourceSelectionSettled = !catalogLoading && topTokensSettled && chainOptions.length > 0 && (pendingChain === void 0 || pendingChain === sourceChainId) && (chainChosenByUser.current ? chainOptions.includes(sourceChainId) : sourceChainId === preferredChainId(chainOptions));
3020
+ const sourceSelectionSettled = !catalogLoading && offer.status === "ready" && chainOptions.length > 0 && (pendingChain === void 0 || pendingChain === sourceChainId) && (chainChosenByUser.current ? chainOptions.includes(sourceChainId) : sourceChainId === preferredChainId(chainOptions));
2817
3021
  useEffect7(() => {
2818
3022
  if (!sourceTokenIdentity) return;
2819
3023
  if (!sourceSelectionSettled) return;
@@ -2873,7 +3077,7 @@ function DepositAddressStep({
2873
3077
  (nextChain) => {
2874
3078
  const nextTokens = computeTokensForChain(
2875
3079
  nextChain,
2876
- topTokens,
3080
+ offer,
2877
3081
  catalogFor(nextChain)
2878
3082
  );
2879
3083
  chainChosenByUser.current = true;
@@ -2883,7 +3087,7 @@ function DepositAddressStep({
2883
3087
  setTokenDropdownOpen(false);
2884
3088
  setCopied(false);
2885
3089
  },
2886
- [sourceTokenSymbol, topTokens, catalogFor]
3090
+ [sourceTokenSymbol, offer, catalogFor]
2887
3091
  );
2888
3092
  const onDepositSubmittedRef = useRef5(onDepositSubmitted);
2889
3093
  onDepositSubmittedRef.current = onDepositSubmitted;
@@ -3016,14 +3220,23 @@ function DepositAddressStep({
3016
3220
  const reviewAsset = selectionAsAsset(
3017
3221
  sourceChainId,
3018
3222
  sourceTokenSymbol,
3019
- topTokens,
3223
+ offer,
3020
3224
  catalogFor(sourceChainId)
3021
3225
  );
3226
+ if (offer.status === "error") {
3227
+ return /* @__PURE__ */ jsx14("div", { className: "rs-screen", children: /* @__PURE__ */ jsxs12("div", { className: "rs-screen-body", children: [
3228
+ /* @__PURE__ */ jsx14(BodyHeader, { icon: /* @__PURE__ */ jsx14(TransferCryptoIcon, {}), title: "Transfer crypto" }),
3229
+ /* @__PURE__ */ jsx14(Callout, { variant: "error", children: "The tokens this app accepts could not be loaded, so there is no deposit address to show." }),
3230
+ /* @__PURE__ */ jsx14(Button, { onClick: offer.retry, fullWidth: true, children: "Try again" })
3231
+ ] }) });
3232
+ }
3022
3233
  if (chainOptions.length === 0) {
3023
- if (catalogLoading) return /* @__PURE__ */ jsx14(DepositAddressSkeleton, { uiConfig });
3234
+ if (catalogLoading || offer.status === "loading") {
3235
+ return /* @__PURE__ */ jsx14(DepositAddressSkeleton, { uiConfig });
3236
+ }
3024
3237
  return /* @__PURE__ */ jsx14("div", { className: "rs-screen", children: /* @__PURE__ */ jsxs12("div", { className: "rs-screen-body", children: [
3025
3238
  /* @__PURE__ */ jsx14(BodyHeader, { icon: /* @__PURE__ */ jsx14(TransferCryptoIcon, {}), title: "Transfer crypto" }),
3026
- /* @__PURE__ */ jsx14(Callout, { variant: "error", children: "Supported chains are unavailable right now, so there is no deposit address to show. Close and try again in a moment." })
3239
+ /* @__PURE__ */ jsx14(Callout, { variant: "error", children: catalog ? "This app does not accept deposits from any chain available here." : "Supported chains are unavailable right now, so there is no deposit address to show. Close and try again in a moment." })
3027
3240
  ] }) });
3028
3241
  }
3029
3242
  return /* @__PURE__ */ jsxs12("div", { className: "rs-screen rs-step--with-notifications", children: [
@@ -3131,14 +3344,34 @@ function DepositAddressStep({
3131
3344
  style: { display: "flex", flexDirection: "column", gap: 4, width: "100%" },
3132
3345
  children: [
3133
3346
  /* @__PURE__ */ jsxs12("div", { className: "rs-deposit-address-well", children: [
3134
- /* @__PURE__ */ jsx14("div", { className: "rs-deposit-address-qr", children: /* @__PURE__ */ jsx14(
3347
+ /* @__PURE__ */ jsx14("div", { className: "rs-deposit-address-qr", children: withholdPaymentQr ? /* @__PURE__ */ jsx14("div", { className: "rs-skeleton rs-skeleton-qr", "aria-hidden": "true" }) : /* @__PURE__ */ jsx14(
3135
3348
  Suspense,
3136
3349
  {
3137
- fallback: /* @__PURE__ */ jsx14("div", { className: "rs-skeleton rs-skeleton-qr", "aria-hidden": "true" }),
3138
- children: /* @__PURE__ */ jsx14(QRCode, { value: displayAddress, size: 190, iconSrc: qrIconSrc })
3350
+ fallback: /* @__PURE__ */ jsx14(
3351
+ "div",
3352
+ {
3353
+ className: "rs-skeleton rs-skeleton-qr",
3354
+ "aria-hidden": "true"
3355
+ }
3356
+ ),
3357
+ children: /* @__PURE__ */ jsx14(QRCode, { value: qrValue, size: 190, iconSrc: qrIconSrc })
3139
3358
  }
3140
3359
  ) }),
3141
- /* @__PURE__ */ jsx14("div", { className: "rs-deposit-address-value", children: displayAddress })
3360
+ payment && paymentDisplay && /* @__PURE__ */ jsxs12("div", { className: "rs-deposit-address-amount", children: [
3361
+ "Send exactly ",
3362
+ paymentDisplay,
3363
+ " ",
3364
+ sourceTokenSymbol
3365
+ ] }),
3366
+ !withholdPaymentQr && /* @__PURE__ */ jsx14("div", { className: "rs-deposit-address-value", children: displayAddress })
3367
+ ] }),
3368
+ paymentUnquotable && /* @__PURE__ */ jsxs12(Callout, { variant: "error", children: [
3369
+ "This payment can't be priced from ",
3370
+ sourceTokenSymbol,
3371
+ " on",
3372
+ " ",
3373
+ chainLabel(sourceChainId, catalogFor(sourceChainId)),
3374
+ " right now. Pick another chain or token."
3142
3375
  ] }),
3143
3376
  /* @__PURE__ */ jsxs12("div", { className: "rs-deposit-address-actions", children: [
3144
3377
  /* @__PURE__ */ jsxs12(
@@ -3147,6 +3380,7 @@ function DepositAddressStep({
3147
3380
  type: "button",
3148
3381
  className: "rs-deposit-address-copy",
3149
3382
  onClick: handleCopy,
3383
+ disabled: withholdPaymentQr,
3150
3384
  children: [
3151
3385
  copied ? /* @__PURE__ */ jsx14(CheckIcon, {}) : /* @__PURE__ */ jsx14(CopyIcon, {}),
3152
3386
  copied ? "Copied!" : "Copy address"
@@ -4212,7 +4446,7 @@ ExchangeSelectStep.displayName = "ExchangeSelectStep";
4212
4446
 
4213
4447
  // src/components/steps/SolanaTokenSelectStep.tsx
4214
4448
  import { useState as useState10, useEffect as useEffect10, useMemo as useMemo5 } from "react";
4215
- import { formatUnits as formatUnits3 } from "viem";
4449
+ import { formatUnits as formatUnits4 } from "viem";
4216
4450
  import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
4217
4451
  function SolanaTokenSelectStep({
4218
4452
  solanaAddress,
@@ -4309,7 +4543,7 @@ function SolanaTokenSelectStep({
4309
4543
  const selectedEntry = selectedSymbol ? rows.find((r) => r.token.symbol === selectedSymbol) : null;
4310
4544
  const formatBalance = (entry) => {
4311
4545
  try {
4312
- const raw = formatUnits3(entry.balance, entry.token.decimals);
4546
+ const raw = formatUnits4(entry.balance, entry.token.decimals);
4313
4547
  const numeric = Number(raw);
4314
4548
  if (!Number.isFinite(numeric)) return raw;
4315
4549
  return tokenFormatter.format(numeric);
@@ -4415,7 +4649,7 @@ function SolanaTokenSelectStep({
4415
4649
 
4416
4650
  // src/components/steps/SolanaAmountStep.tsx
4417
4651
  import { useCallback as useCallback6, useEffect as useEffect11, useMemo as useMemo6, useRef as useRef7, useState as useState11 } from "react";
4418
- import { formatUnits as formatUnits4, parseUnits as parseUnits4 } from "viem";
4652
+ import { formatUnits as formatUnits5, parseUnits as parseUnits4 } from "viem";
4419
4653
  import { Fragment as Fragment5, jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
4420
4654
  var SOL_FEE_RESERVE_LAMPORTS = 1000000n;
4421
4655
  var PRESETS2 = [
@@ -4445,7 +4679,7 @@ function SolanaAmountStep({
4445
4679
  const tokenPriceUsd = useMemo6(() => {
4446
4680
  if (isSourceStablecoin) return 1;
4447
4681
  try {
4448
- const balanceUnits = Number(formatUnits4(balance, token.decimals));
4682
+ const balanceUnits = Number(formatUnits5(balance, token.decimals));
4449
4683
  if (!Number.isFinite(balanceUnits) || balanceUnits <= 0 || balanceUsd <= 0) {
4450
4684
  return null;
4451
4685
  }
@@ -4458,7 +4692,7 @@ function SolanaAmountStep({
4458
4692
  const hasPricing = tokenPriceUsd !== null;
4459
4693
  const formattedBalance = useMemo6(() => {
4460
4694
  try {
4461
- const raw = formatUnits4(balance, token.decimals);
4695
+ const raw = formatUnits5(balance, token.decimals);
4462
4696
  const numeric = Number(raw);
4463
4697
  if (!Number.isFinite(numeric)) return raw;
4464
4698
  return tokenFormatter.format(numeric);
@@ -4468,7 +4702,7 @@ function SolanaAmountStep({
4468
4702
  }, [balance, token.decimals]);
4469
4703
  const computedBalanceUsd = useMemo6(() => {
4470
4704
  try {
4471
- const balanceUnits = Number(formatUnits4(balance, token.decimals));
4705
+ const balanceUnits = Number(formatUnits5(balance, token.decimals));
4472
4706
  if (!Number.isFinite(balanceUnits) || balanceUnits < 0) return null;
4473
4707
  if (tokenPriceUsd !== null) return balanceUnits * tokenPriceUsd;
4474
4708
  if (Number.isFinite(balanceUsd) && balanceUsd > 0) return balanceUsd;
@@ -4483,7 +4717,7 @@ function SolanaAmountStep({
4483
4717
  }, [balance, token]);
4484
4718
  const spendableBalanceUsd = useMemo6(() => {
4485
4719
  try {
4486
- const spendableUnits = Number(formatUnits4(spendableBalance, token.decimals));
4720
+ const spendableUnits = Number(formatUnits5(spendableBalance, token.decimals));
4487
4721
  if (!Number.isFinite(spendableUnits) || spendableUnits < 0) return null;
4488
4722
  if (tokenPriceUsd !== null) return spendableUnits * tokenPriceUsd;
4489
4723
  return null;
@@ -4495,7 +4729,7 @@ function SolanaAmountStep({
4495
4729
  (percentage) => {
4496
4730
  try {
4497
4731
  const spendableUnits = Number(
4498
- formatUnits4(spendableBalance, token.decimals)
4732
+ formatUnits5(spendableBalance, token.decimals)
4499
4733
  );
4500
4734
  if (!Number.isFinite(spendableUnits) || spendableUnits <= 0) return "";
4501
4735
  const value = isSourceStablecoin || !hasPricing ? spendableUnits * percentage / 100 : (spendableBalanceUsd ?? 0) * percentage / 100;
@@ -4550,6 +4784,11 @@ function SolanaAmountStep({
4550
4784
  const exceedsBalance = Boolean(
4551
4785
  sourceAmountUnits !== null && sourceAmountUnits > spendableBalance
4552
4786
  );
4787
+ const boundsFor = useDepositBounds();
4788
+ const boundViolation = depositBoundsViolation(
4789
+ boundsFor("solana", token.mint),
4790
+ sourceAmountUnits
4791
+ );
4553
4792
  const balanceAfterUsd = appBalanceUsd !== void 0 ? appBalanceUsd + numericAmount : null;
4554
4793
  const handlePresetClick = (percentage) => {
4555
4794
  const formatted = computePresetAmount(percentage);
@@ -4570,6 +4809,7 @@ function SolanaAmountStep({
4570
4809
  if (error) setError(null);
4571
4810
  };
4572
4811
  const handleContinue = () => {
4812
+ if (boundViolation) return;
4573
4813
  let effective = numericAmount;
4574
4814
  if (isBelowMin && minDepositUsd !== null) {
4575
4815
  effective = minDepositUsd;
@@ -4613,8 +4853,8 @@ function SolanaAmountStep({
4613
4853
  setError(null);
4614
4854
  onContinue(token, sourceAmountStr, effective.toString());
4615
4855
  };
4616
- const continueLabel = exceedsBalance ? "Insufficient balance" : isAboveMax ? "Continue with max allowed deposit" : isBelowMin ? "Continue with minimum deposit" : "Continue";
4617
- const continueDisabled = exceedsBalance || numericAmount === 0 && !isBelowMin;
4856
+ const continueLabel = exceedsBalance ? "Insufficient balance" : boundViolation ?? (isAboveMax ? "Continue with max allowed deposit" : isBelowMin ? "Continue with minimum deposit" : "Continue");
4857
+ const continueDisabled = exceedsBalance || boundViolation !== null || numericAmount === 0 && !isBelowMin;
4618
4858
  return /* @__PURE__ */ jsxs18("div", { className: "rs-screen", children: [
4619
4859
  /* @__PURE__ */ jsxs18("div", { className: "rs-screen-body rs-screen-body--gap-32", children: [
4620
4860
  /* @__PURE__ */ jsx22(BodyHeader, { icon: /* @__PURE__ */ jsx22(WalletIcon, {}), title: "Wallet deposit" }),
@@ -5454,7 +5694,7 @@ function withdrawableUsd(position) {
5454
5694
  }
5455
5695
 
5456
5696
  // src/core/dapp-imports/polymarket/index.ts
5457
- import { formatUnits as formatUnits5 } from "viem";
5697
+ import { formatUnits as formatUnits6 } from "viem";
5458
5698
 
5459
5699
  // src/core/dapp-imports/polymarket/deposit-wallet.ts
5460
5700
  import {
@@ -5713,7 +5953,7 @@ function buildAsset2(params) {
5713
5953
  const isPusd = tokenKind === "pusd";
5714
5954
  const tokenAddress = isPusd ? POLYMARKET_PUSD_ADDRESS : POLYMARKET_USDCE_ADDRESS;
5715
5955
  const symbol = isPusd ? "pUSD" : "USDC.e";
5716
- const balanceUsd = Number(formatUnits5(balance, 6));
5956
+ const balanceUsd = Number(formatUnits6(balance, 6));
5717
5957
  const metadata = { proxyWallet, tokenKind, walletKind };
5718
5958
  return {
5719
5959
  id: `polymarket:${tokenKind}`,
@@ -5753,6 +5993,33 @@ function getEnabledProviders(config) {
5753
5993
  }
5754
5994
 
5755
5995
  // src/core/dapp-imports/status.ts
5996
+ function narrowDappImportAvailability(availability, isExcluded) {
5997
+ let changed = false;
5998
+ const narrowed = {};
5999
+ for (const [providerId, entry] of Object.entries(availability)) {
6000
+ if (!entry || entry === "loading" || entry === "error") {
6001
+ narrowed[providerId] = entry;
6002
+ continue;
6003
+ }
6004
+ const assets = entry.assets.filter(
6005
+ (asset) => !isExcluded(asset.depositChainId, asset.depositToken)
6006
+ );
6007
+ if (assets.length === entry.assets.length) {
6008
+ narrowed[providerId] = entry;
6009
+ continue;
6010
+ }
6011
+ changed = true;
6012
+ narrowed[providerId] = assets.length === 0 ? null : {
6013
+ ...entry,
6014
+ assets,
6015
+ totalUsd: assets.reduce(
6016
+ (sum, asset) => sum + (asset.balanceUsd ?? 0),
6017
+ 0
6018
+ )
6019
+ };
6020
+ }
6021
+ return changed ? narrowed : availability;
6022
+ }
5756
6023
  function computeDappImportStatus(providerId, inputs) {
5757
6024
  const { owner, availability, availabilityOwner, setup } = inputs;
5758
6025
  if (!owner) return "needs-connect";
@@ -7180,7 +7447,7 @@ async function fetchPinnedQuote(params) {
7180
7447
  timeoutId = setTimeout(resolve, SWAPPED_QUOTE_FEE_TIMEOUT_MS, null);
7181
7448
  })
7182
7449
  ]);
7183
- return quote ?? void 0;
7450
+ return quote && !isDepositPolicyRefusal(quote) ? quote : void 0;
7184
7451
  } catch {
7185
7452
  return void 0;
7186
7453
  } finally {
@@ -7393,6 +7660,7 @@ function DepositFlow({
7393
7660
  sourceToken: defaultSourceToken,
7394
7661
  amount: defaultAmount,
7395
7662
  recipient,
7663
+ payment,
7396
7664
  appBalanceUsd,
7397
7665
  outputTokenRules,
7398
7666
  rejectUnmapped,
@@ -8090,7 +8358,17 @@ function DepositFlow({
8090
8358
  providerId: initialAssetMigrationProvider.id
8091
8359
  });
8092
8360
  }, [analytics, initialAssetMigrationProvider, flowSlice, storeApi]);
8093
- const dappImportAvailability = useDepositStore((s) => s.dappImport.availability);
8361
+ const storedDappImportAvailability = useDepositStore(
8362
+ (s) => s.dappImport.availability
8363
+ );
8364
+ const isExcludedByRestrictedList = useRestrictedListExclusion();
8365
+ const dappImportAvailability = useMemo8(
8366
+ () => narrowDappImportAvailability(
8367
+ storedDappImportAvailability,
8368
+ isExcludedByRestrictedList
8369
+ ),
8370
+ [storedDappImportAvailability, isExcludedByRestrictedList]
8371
+ );
8094
8372
  const dappImportAvailabilityOwner = useDepositStore(
8095
8373
  (s) => s.dappImport.availabilityOwner
8096
8374
  );
@@ -8535,7 +8813,8 @@ function DepositFlow({
8535
8813
  recipient,
8536
8814
  outputTokenRules,
8537
8815
  rejectUnmapped,
8538
- forceRegister
8816
+ forceRegister,
8817
+ ...payment ? { salt: payment.salt } : {}
8539
8818
  }),
8540
8819
  [
8541
8820
  targetChain,
@@ -8543,7 +8822,8 @@ function DepositFlow({
8543
8822
  recipient,
8544
8823
  outputTokenRules,
8545
8824
  rejectUnmapped,
8546
- forceRegister
8825
+ forceRegister,
8826
+ payment
8547
8827
  ]
8548
8828
  );
8549
8829
  const desiredSetupRequestKey = computeRequestKey(setupInput);
@@ -9024,6 +9304,7 @@ function DepositFlow({
9024
9304
  return /* @__PURE__ */ jsx28("div", { className: "rs-modal-body", children: /* @__PURE__ */ jsx28(
9025
9305
  ConnectStep,
9026
9306
  {
9307
+ title: payment ? "Pay" : void 0,
9027
9308
  walletRows,
9028
9309
  defaultMethodTab: flowSlice.methodTab,
9029
9310
  onMethodTabChange: (tab) => storeApi.dispatch({ type: "connect/method-tab-changed", tab }),
@@ -9093,6 +9374,7 @@ function DepositFlow({
9093
9374
  effectiveStep.type === "deposit-address" && /* @__PURE__ */ jsx28(
9094
9375
  DepositAddressStep,
9095
9376
  {
9377
+ payment,
9096
9378
  smartAccount: effectiveStep.smartAccount,
9097
9379
  solanaDepositAddress: effectiveStep.solanaDepositAddress,
9098
9380
  service,
@@ -9207,6 +9489,7 @@ function DepositFlow({
9207
9489
  step.type === "processing" && /* @__PURE__ */ jsx28(
9208
9490
  ProcessingStep,
9209
9491
  {
9492
+ flowLabel: payment ? "payment" : void 0,
9210
9493
  smartAccount: step.smartAccount,
9211
9494
  solanaDepositAddress: step.solanaDepositAddress,
9212
9495
  txHash: step.txHash,
@@ -9287,6 +9570,7 @@ function DepositFlow({
9287
9570
  step.type === "processing" && /* @__PURE__ */ jsx28(
9288
9571
  ProcessingStep,
9289
9572
  {
9573
+ flowLabel: payment ? "payment" : void 0,
9290
9574
  smartAccount: step.smartAccount,
9291
9575
  solanaDepositAddress: step.solanaDepositAddress,
9292
9576
  txHash: step.txHash,
@@ -9416,6 +9700,7 @@ function DepositFlow({
9416
9700
  effectiveStep.type === "processing" && /* @__PURE__ */ jsx28(
9417
9701
  ProcessingStep,
9418
9702
  {
9703
+ flowLabel: payment ? "payment" : void 0,
9419
9704
  smartAccount: effectiveStep.smartAccount,
9420
9705
  solanaDepositAddress: effectiveStep.solanaDepositAddress,
9421
9706
  txHash: effectiveStep.txHash,
@@ -9550,6 +9835,9 @@ function DepositFlow({
9550
9835
  uiConfig,
9551
9836
  appBalanceUsd,
9552
9837
  service,
9838
+ payment,
9839
+ account: effectiveStep.smartAccount,
9840
+ title: payment ? "Amount to pay" : void 0,
9553
9841
  onContinue: handleAmountContinue,
9554
9842
  onCtaClick: (ctaName) => {
9555
9843
  if (ctaName === "continue") analytics.completeStep();
@@ -9565,6 +9853,8 @@ function DepositFlow({
9565
9853
  recipient,
9566
9854
  asset: effectiveStep.asset,
9567
9855
  amount: effectiveStep.amount,
9856
+ title: payment ? "Review payment" : void 0,
9857
+ routeThroughAccount: Boolean(payment),
9568
9858
  targetAmount: effectiveStep.targetAmount,
9569
9859
  targetTokenPriceUsd: effectiveStep.targetTokenPriceUsd,
9570
9860
  balance: effectiveStep.balance,
@@ -9608,6 +9898,7 @@ function DepositFlow({
9608
9898
  effectiveStep.type === "processing" && /* @__PURE__ */ jsx28(
9609
9899
  ProcessingStep,
9610
9900
  {
9901
+ flowLabel: payment ? "payment" : void 0,
9611
9902
  smartAccount: effectiveStep.smartAccount,
9612
9903
  solanaDepositAddress: effectiveStep.solanaDepositAddress,
9613
9904
  txHash: effectiveStep.txHash,
@@ -9646,7 +9937,7 @@ function DepositFlow({
9646
9937
  import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
9647
9938
  var ReownDepositInner = lazy2(async () => {
9648
9939
  await loadReownDependencies();
9649
- const module = await import("./DepositModalReown-ELDSTIME.mjs");
9940
+ const module = await import("./DepositModalReown-XBNY6QCI.mjs");
9650
9941
  return { default: module.DepositModalReown };
9651
9942
  });
9652
9943
  function buildDepositAnalyticsSessionProperties({
@@ -9739,6 +10030,7 @@ function DepositModalInner({
9739
10030
  sourceToken,
9740
10031
  defaultAmount,
9741
10032
  recipient,
10033
+ payment,
9742
10034
  appBalanceUsd,
9743
10035
  backendUrl,
9744
10036
  rpcUrls: rpcUrlsProp,
@@ -9785,7 +10077,7 @@ function DepositModalInner({
9785
10077
  void 0
9786
10078
  );
9787
10079
  const [processingSettled, setProcessingSettled] = useState14(false);
9788
- const historyButtonVisible = isHistoryEntryVisible({
10080
+ const historyButtonVisible = !payment && isHistoryEntryVisible({
9789
10081
  screen: currentScreen,
9790
10082
  submitting,
9791
10083
  settled: processingSettled
@@ -9809,7 +10101,7 @@ function DepositModalInner({
9809
10101
  []
9810
10102
  );
9811
10103
  const history = useHistoryPanel({
9812
- isOpen,
10104
+ isOpen: isOpen && !payment,
9813
10105
  recipient,
9814
10106
  service,
9815
10107
  canRecover: Boolean(signRecovery),
@@ -10022,6 +10314,7 @@ function DepositModalInner({
10022
10314
  sourceToken,
10023
10315
  amount: defaultAmount,
10024
10316
  recipient,
10317
+ payment,
10025
10318
  appBalanceUsd,
10026
10319
  outputTokenRules,
10027
10320
  rejectUnmapped,