@unifold/connect-react 0.1.21 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1181,7 +1181,7 @@ __export(index_exports, {
1181
1181
  module.exports = __toCommonJS(index_exports);
1182
1182
 
1183
1183
  // src/provider.tsx
1184
- var import_react20 = __toESM(require("react"));
1184
+ var import_react21 = __toESM(require("react"));
1185
1185
 
1186
1186
  // ../react-provider/dist/index.mjs
1187
1187
  var import_react = require("react");
@@ -6320,12 +6320,14 @@ async function getTokenChains() {
6320
6320
  return response.json();
6321
6321
  }
6322
6322
  function getChainName(chains, chainType, chainId) {
6323
+ if (!chainType && !chainId) return "Unknown";
6323
6324
  const target = chains.find((c) => c.chain_id === chainId && c.chain_type === chainType);
6324
6325
  if (target) return target.chain_name;
6325
6326
  const byId = chains.find((c) => c.chain_id === chainId);
6326
6327
  if (byId) return byId.chain_name;
6327
6328
  const byType = chains.find((c) => c.chain_type === chainType);
6328
6329
  if (byType) return byType.chain_name;
6330
+ if (!chainType) return chainId || "Unknown";
6329
6331
  return chainType.charAt(0).toUpperCase() + chainType.slice(1);
6330
6332
  }
6331
6333
  async function getProjectConfig(publishableKey) {
@@ -6359,6 +6361,49 @@ async function getIpAddress() {
6359
6361
  }
6360
6362
  return response.json();
6361
6363
  }
6364
+ async function getAddressBalance(address, chainType, chainId, tokenAddress, publishableKey) {
6365
+ const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6366
+ validatePublishableKey(pk);
6367
+ const url = `${API_BASE_URL}/v1/public/addresses/balance`;
6368
+ const response = await fetch(url, {
6369
+ method: "POST",
6370
+ headers: {
6371
+ "Content-Type": "application/json",
6372
+ accept: "application/json",
6373
+ "x-publishable-key": pk
6374
+ },
6375
+ body: JSON.stringify({
6376
+ address,
6377
+ chain_type: chainType,
6378
+ chain_id: chainId,
6379
+ token_address: tokenAddress
6380
+ })
6381
+ });
6382
+ if (!response.ok) {
6383
+ throw new Error(`Failed to fetch address balance: ${response.statusText}`);
6384
+ }
6385
+ const data = await response.json();
6386
+ return data;
6387
+ }
6388
+ async function verifyRecipientAddress(request, publishableKey) {
6389
+ const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6390
+ validatePublishableKey(pk);
6391
+ const response = await fetch(`${API_BASE_URL}/v1/public/addresses/verify`, {
6392
+ method: "POST",
6393
+ headers: {
6394
+ accept: "application/json",
6395
+ "x-publishable-key": pk,
6396
+ "Content-Type": "application/json"
6397
+ },
6398
+ body: JSON.stringify(request)
6399
+ });
6400
+ if (!response.ok) {
6401
+ throw new Error(
6402
+ `Failed to verify recipient address: ${response.statusText}`
6403
+ );
6404
+ }
6405
+ return response.json();
6406
+ }
6362
6407
  function useUserIp() {
6363
6408
  const {
6364
6409
  data: userIpInfo,
@@ -6459,21 +6504,22 @@ var i18n = en_default;
6459
6504
 
6460
6505
  // ../ui-react/dist/index.mjs
6461
6506
  var import_react10 = require("react");
6507
+ var import_react11 = require("react");
6462
6508
  var import_jsx_runtime20 = require("react/jsx-runtime");
6463
6509
  var React37 = __toESM(require("react"), 1);
6464
6510
  var import_jsx_runtime21 = require("react/jsx-runtime");
6465
6511
  var import_jsx_runtime22 = require("react/jsx-runtime");
6466
6512
  var import_jsx_runtime23 = require("react/jsx-runtime");
6467
6513
  var import_react_query3 = require("@tanstack/react-query");
6468
- var import_react11 = require("react");
6469
6514
  var import_react12 = require("react");
6470
6515
  var import_react13 = require("react");
6471
6516
  var import_react14 = require("react");
6517
+ var import_react15 = require("react");
6472
6518
  var import_jsx_runtime24 = require("react/jsx-runtime");
6473
6519
  var import_jsx_runtime25 = require("react/jsx-runtime");
6474
6520
  var import_jsx_runtime26 = require("react/jsx-runtime");
6475
6521
  var import_jsx_runtime27 = require("react/jsx-runtime");
6476
- var import_react15 = require("react");
6522
+ var import_react16 = require("react");
6477
6523
  var import_jsx_runtime28 = require("react/jsx-runtime");
6478
6524
  var React42 = __toESM(require("react"), 1);
6479
6525
  var import_jsx_runtime29 = require("react/jsx-runtime");
@@ -6485,11 +6531,12 @@ var import_jsx_runtime32 = require("react/jsx-runtime");
6485
6531
  var React72 = __toESM(require("react"), 1);
6486
6532
  var import_jsx_runtime33 = require("react/jsx-runtime");
6487
6533
  var import_react_query4 = require("@tanstack/react-query");
6488
- var import_react16 = require("react");
6534
+ var import_react_query5 = require("@tanstack/react-query");
6489
6535
  var import_react17 = require("react");
6536
+ var import_react18 = require("react");
6490
6537
  var import_qr_code_styling = __toESM(require_qr_code_styling(), 1);
6491
6538
  var import_jsx_runtime34 = require("react/jsx-runtime");
6492
- var import_react18 = require("react");
6539
+ var import_react19 = require("react");
6493
6540
  var import_jsx_runtime35 = require("react/jsx-runtime");
6494
6541
  var React82 = __toESM(require("react"), 1);
6495
6542
 
@@ -9231,7 +9278,7 @@ var Content22 = TooltipContent;
9231
9278
  // ../ui-react/dist/index.mjs
9232
9279
  var import_jsx_runtime36 = require("react/jsx-runtime");
9233
9280
  var import_jsx_runtime37 = require("react/jsx-runtime");
9234
- var import_react19 = require("react");
9281
+ var import_react20 = require("react");
9235
9282
  var React92 = __toESM(require("react"), 1);
9236
9283
 
9237
9284
  // ../../node_modules/.pnpm/@radix-ui+react-select@2.2.6_@types+react-dom@19.2.3_@types+react@19.2.9__@types+react@19.2.9_5n4syhs66qrtkkphe44qzgdimq/node_modules/@radix-ui/react-select/dist/index.mjs
@@ -10620,6 +10667,35 @@ var import_jsx_runtime41 = require("react/jsx-runtime");
10620
10667
  function cn(...inputs) {
10621
10668
  return twMerge(clsx(inputs));
10622
10669
  }
10670
+ function truncateAddress(address, startChars = 10, endChars = 8) {
10671
+ if (!address) return "";
10672
+ const totalChars = startChars + endChars + 3;
10673
+ if (address.length <= totalChars) return address;
10674
+ return `${address.slice(0, startChars)}...${address.slice(-endChars)}`;
10675
+ }
10676
+ function formatEstimatedTime(seconds) {
10677
+ if (seconds == null) {
10678
+ return "< 1 min";
10679
+ }
10680
+ if (seconds < 60) {
10681
+ return `< ${seconds} sec${seconds > 1 ? "s" : ""}`;
10682
+ } else if (seconds < 3600) {
10683
+ const mins = Math.ceil(seconds / 60);
10684
+ return `< ${mins} min${mins > 1 ? "s" : ""}`;
10685
+ } else {
10686
+ let hrs = Math.floor(seconds / 3600);
10687
+ let mins = Math.ceil(seconds % 3600 / 60);
10688
+ if (mins === 60) {
10689
+ hrs += 1;
10690
+ mins = 0;
10691
+ }
10692
+ const hrLabel = hrs > 1 ? "hrs" : "hr";
10693
+ if (mins === 0) {
10694
+ return `< ${hrs} ${hrLabel}`;
10695
+ }
10696
+ return `< ${hrs} ${hrLabel} ${mins} min${mins > 1 ? "s" : ""}`;
10697
+ }
10698
+ }
10623
10699
  var defaultColors = {
10624
10700
  light: {
10625
10701
  // Background colors
@@ -11061,10 +11137,83 @@ function DepositHeader({
11061
11137
  showClose = true,
11062
11138
  onBack,
11063
11139
  onClose,
11064
- badge
11140
+ badge,
11141
+ showBalance = false,
11142
+ balanceAddress,
11143
+ balanceChainType,
11144
+ balanceChainId,
11145
+ balanceTokenAddress,
11146
+ publishableKey
11065
11147
  }) {
11066
11148
  const { colors: colors2, fonts, components } = useTheme();
11067
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "uf-flex uf-items-center uf-justify-between uf-pb-6", children: [
11149
+ const [balance, setBalance] = (0, import_react11.useState)(null);
11150
+ const [isLoadingBalance, setIsLoadingBalance] = (0, import_react11.useState)(false);
11151
+ (0, import_react11.useEffect)(() => {
11152
+ if (!showBalance || !balanceAddress || !balanceChainType || !balanceChainId || !balanceTokenAddress || !publishableKey) {
11153
+ setBalance(null);
11154
+ setIsLoadingBalance(false);
11155
+ return;
11156
+ }
11157
+ let cancelled = false;
11158
+ setIsLoadingBalance(true);
11159
+ getAddressBalance(
11160
+ balanceAddress,
11161
+ balanceChainType,
11162
+ balanceChainId,
11163
+ balanceTokenAddress,
11164
+ publishableKey
11165
+ ).then((response) => {
11166
+ if (cancelled) return;
11167
+ if (response.balance && response.balance.amount !== "0") {
11168
+ const value = Number(response.balance.amount) / 10 ** response.balance.token.decimals;
11169
+ let formatted;
11170
+ let maxDecimals = 4;
11171
+ const symbol = response.balance.token.symbol?.toUpperCase() || "";
11172
+ if (symbol === "BTC" || symbol === "WBTC") {
11173
+ maxDecimals = 8;
11174
+ } else if (symbol === "ETH" || symbol === "WETH") {
11175
+ maxDecimals = 6;
11176
+ }
11177
+ if (value >= 1) {
11178
+ formatted = value.toLocaleString(void 0, {
11179
+ minimumFractionDigits: 2,
11180
+ maximumFractionDigits: maxDecimals
11181
+ });
11182
+ } else if (value > 0) {
11183
+ formatted = value.toLocaleString(void 0, {
11184
+ minimumFractionDigits: 2,
11185
+ maximumFractionDigits: maxDecimals,
11186
+ minimumSignificantDigits: 2,
11187
+ maximumSignificantDigits: 6
11188
+ });
11189
+ } else {
11190
+ formatted = value.toExponential(2);
11191
+ }
11192
+ const balanceText = response.balance.amount_usd ? `Balance: $${response.balance.amount_usd} (${formatted} ${response.balance.token.symbol})` : `Balance: ${formatted} ${response.balance.token.symbol}`;
11193
+ setBalance(balanceText);
11194
+ } else {
11195
+ setBalance(null);
11196
+ }
11197
+ }).catch((error) => {
11198
+ if (cancelled) return;
11199
+ console.error("Error fetching balance:", error);
11200
+ setBalance(null);
11201
+ }).finally(() => {
11202
+ if (cancelled) return;
11203
+ setIsLoadingBalance(false);
11204
+ });
11205
+ return () => {
11206
+ cancelled = true;
11207
+ };
11208
+ }, [
11209
+ showBalance,
11210
+ balanceAddress,
11211
+ balanceChainType,
11212
+ balanceChainId,
11213
+ balanceTokenAddress,
11214
+ publishableKey
11215
+ ]);
11216
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "uf-flex uf-items-center uf-justify-between uf-pb-6", children: [
11068
11217
  showBack ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
11069
11218
  "button",
11070
11219
  {
@@ -11074,8 +11223,32 @@ function DepositHeader({
11074
11223
  children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ArrowLeft, { className: "uf-w-5 uf-h-5" })
11075
11224
  }
11076
11225
  ) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "uf-w-5 uf-h-5 uf-invisible" }),
11077
- badge ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
11078
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
11226
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "uf-flex uf-flex-col uf-items-center", children: [
11227
+ badge ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
11228
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
11229
+ DialogTitle2,
11230
+ {
11231
+ className: "uf-text-center uf-text-base",
11232
+ style: {
11233
+ color: components.header.titleColor,
11234
+ fontFamily: fonts.medium
11235
+ },
11236
+ children: title
11237
+ }
11238
+ ),
11239
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
11240
+ "div",
11241
+ {
11242
+ className: "uf-px-2 uf-py-0.5 uf-rounded-full uf-text-[10px]",
11243
+ style: {
11244
+ backgroundColor: colors2.card,
11245
+ color: colors2.foregroundMuted,
11246
+ fontFamily: fonts.regular
11247
+ },
11248
+ children: badge.count
11249
+ }
11250
+ )
11251
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
11079
11252
  DialogTitle2,
11080
11253
  {
11081
11254
  className: "uf-text-center uf-text-base",
@@ -11086,29 +11259,19 @@ function DepositHeader({
11086
11259
  children: title
11087
11260
  }
11088
11261
  ),
11089
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
11262
+ showBalance && (isLoadingBalance ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "uf-h-3 uf-w-32 uf-bg-muted uf-rounded uf-animate-pulse uf-mt-2" }) : balance ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
11090
11263
  "div",
11091
11264
  {
11092
- className: "uf-px-2 uf-py-0.5 uf-rounded-full uf-text-[10px]",
11265
+ className: "uf-text-xs uf-mt-2",
11093
11266
  style: {
11094
- backgroundColor: colors2.card,
11095
- color: colors2.foregroundMuted,
11096
- fontFamily: fonts.regular
11267
+ color: colors2.foreground,
11268
+ fontFamily: fonts.regular,
11269
+ opacity: 0.7
11097
11270
  },
11098
- children: badge.count
11271
+ children: balance
11099
11272
  }
11100
- )
11101
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
11102
- DialogTitle2,
11103
- {
11104
- className: "uf-text-center uf-text-base",
11105
- style: {
11106
- color: components.header.titleColor,
11107
- fontFamily: fonts.medium
11108
- },
11109
- children: title
11110
- }
11111
- ),
11273
+ ) : null)
11274
+ ] }),
11112
11275
  showClose ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
11113
11276
  "button",
11114
11277
  {
@@ -11118,7 +11281,7 @@ function DepositHeader({
11118
11281
  children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(X, { className: "uf-w-5 uf-h-5" })
11119
11282
  }
11120
11283
  ) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "uf-w-5 uf-h-5 uf-invisible" })
11121
- ] });
11284
+ ] }) });
11122
11285
  }
11123
11286
  function CurrencyListItem({
11124
11287
  currency,
@@ -11374,8 +11537,8 @@ var en_default2 = {
11374
11537
  seeTerms: "See terms",
11375
11538
  termsApply: "Terms apply"
11376
11539
  },
11377
- supportedToken: "Supported token",
11378
- supportedChain: "Supported chain",
11540
+ selectedToken: "Selected token",
11541
+ selectedChain: "Selected chain",
11379
11542
  depositAddress: {
11380
11543
  label: "Your deposit address",
11381
11544
  tooltip: "Send any supported token to this address, and it will be automatically converted to {{token}} in your account."
@@ -11389,7 +11552,19 @@ var en_default2 = {
11389
11552
  minimumDeposit: "Minimum: {{amount}}",
11390
11553
  minimumDepositTooltip: "The minimum amount you can deposit on the selected network.",
11391
11554
  selectTokenDeposit: "Your deposit token",
11392
- selectTokenDepositTooltip: "Select the token you want to deposit with in order to begin the deposit process."
11555
+ selectTokenDepositTooltip: "Select the token you want to deposit with in order to begin the deposit process.",
11556
+ addressValidation: {
11557
+ validating: "Verifying recipient address...",
11558
+ unableToReceiveFunds: "Unable to Receive Funds",
11559
+ errors: {
11560
+ token_not_supported: "The destination token is not supported",
11561
+ not_opted_in: "Please make sure you opt-in {{token_symbol}}({{chain_name}}) before receiving funds",
11562
+ insufficient_balance: "Recipient account does not meet the minimum balance requirement",
11563
+ account_not_found: "Recipient account does not exist on {{chain_name}}",
11564
+ validation_error: "Unable to verify recipient address on {{chain_name}}"
11565
+ },
11566
+ defaultError: "The recipient address cannot receive funds for the selected token"
11567
+ }
11393
11568
  },
11394
11569
  depositModal: {
11395
11570
  transferCrypto: {
@@ -11398,7 +11573,7 @@ var en_default2 = {
11398
11573
  },
11399
11574
  depositWithCard: {
11400
11575
  title: "Deposit with Card",
11401
- subtitle: "$50,000 limit \u2022 2 min"
11576
+ subtitle: "$50,000 limit"
11402
11577
  },
11403
11578
  quotes: "Quotes"
11404
11579
  },
@@ -11414,6 +11589,13 @@ var en_default2 = {
11414
11589
  }
11415
11590
  };
11416
11591
  var i18n2 = en_default2;
11592
+ function interpolate(template, params) {
11593
+ if (!params) return template;
11594
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
11595
+ const value = params[key];
11596
+ return value !== void 0 ? String(value) : `{{${key}}}`;
11597
+ });
11598
+ }
11417
11599
  function useDepositPolling({
11418
11600
  userId,
11419
11601
  publishableKey,
@@ -11421,14 +11603,14 @@ function useDepositPolling({
11421
11603
  onDepositSuccess,
11422
11604
  onDepositError
11423
11605
  }) {
11424
- const [executions, setExecutions] = (0, import_react11.useState)([]);
11425
- const [isPolling, setIsPolling] = (0, import_react11.useState)(false);
11426
- const pollingIntervalRef = (0, import_react11.useRef)(
11606
+ const [executions, setExecutions] = (0, import_react12.useState)([]);
11607
+ const [isPolling, setIsPolling] = (0, import_react12.useState)(false);
11608
+ const pollingIntervalRef = (0, import_react12.useRef)(
11427
11609
  null
11428
11610
  );
11429
- const [modalOpenedAt] = (0, import_react11.useState)(/* @__PURE__ */ new Date());
11430
- const [trackedExecutions, setTrackedExecutions] = (0, import_react11.useState)(/* @__PURE__ */ new Map());
11431
- (0, import_react11.useEffect)(() => {
11611
+ const [modalOpenedAt] = (0, import_react12.useState)(/* @__PURE__ */ new Date());
11612
+ const [trackedExecutions, setTrackedExecutions] = (0, import_react12.useState)(/* @__PURE__ */ new Map());
11613
+ (0, import_react12.useEffect)(() => {
11432
11614
  if (!userId || !modalOpenedAt || !enabled) return;
11433
11615
  const pollInterval = setInterval(async () => {
11434
11616
  try {
@@ -11539,12 +11721,12 @@ function formatCurrency(currency) {
11539
11721
  }
11540
11722
  function DepositDetailContent({ execution }) {
11541
11723
  const { colors: colors2, fonts, components } = useTheme();
11542
- const [chains, setChains] = (0, import_react14.useState)([]);
11543
- const [showNetworkDetails, setShowNetworkDetails] = (0, import_react14.useState)(false);
11544
- (0, import_react14.useEffect)(() => {
11724
+ const [chains, setChains] = (0, import_react15.useState)([]);
11725
+ const [showNetworkDetails, setShowNetworkDetails] = (0, import_react15.useState)(false);
11726
+ (0, import_react15.useEffect)(() => {
11545
11727
  getTokenChains().then((response) => setChains(response.data)).catch((err) => console.error("Failed to fetch chains:", err));
11546
11728
  }, []);
11547
- (0, import_react14.useEffect)(() => {
11729
+ (0, import_react15.useEffect)(() => {
11548
11730
  setShowNetworkDetails(false);
11549
11731
  }, [execution?.id]);
11550
11732
  const isPending = execution.status === ExecutionStatus.PENDING || execution.status === ExecutionStatus.WAITING || execution.status === ExecutionStatus.DELAYED;
@@ -11839,6 +12021,37 @@ function DepositDetailContent({ execution }) {
11839
12021
  ]
11840
12022
  }
11841
12023
  ),
12024
+ isPending && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
12025
+ "div",
12026
+ {
12027
+ className: "uf-flex uf-justify-between uf-items-center uf-px-4 uf-py-3 uf-border-b",
12028
+ style: { borderColor: colors2.border },
12029
+ children: [
12030
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12031
+ "span",
12032
+ {
12033
+ className: "uf-text-sm",
12034
+ style: {
12035
+ color: components.card.labelColor,
12036
+ fontFamily: fonts.regular
12037
+ },
12038
+ children: "Estimated delivery time"
12039
+ }
12040
+ ),
12041
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
12042
+ "span",
12043
+ {
12044
+ style: {
12045
+ color: components.card.titleColor,
12046
+ fontFamily: fonts.regular,
12047
+ fontSize: "14px"
12048
+ },
12049
+ children: formatEstimatedTime(execution?.estimated_processing_time)
12050
+ }
12051
+ )
12052
+ ]
12053
+ }
12054
+ ),
11842
12055
  /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
11843
12056
  "div",
11844
12057
  {
@@ -12022,7 +12235,7 @@ function DepositSuccessToast({
12022
12235
  onClose,
12023
12236
  execution
12024
12237
  }) {
12025
- const [detailModalOpen, setDetailModalOpen] = (0, import_react13.useState)(false);
12238
+ const [detailModalOpen, setDetailModalOpen] = (0, import_react14.useState)(false);
12026
12239
  const { themeClass, colors: colors2, fonts, components } = useTheme();
12027
12240
  const isPending = status === ExecutionStatus.PENDING || status === ExecutionStatus.WAITING || status === ExecutionStatus.DELAYED;
12028
12241
  const formatDateTime = (timestamp) => {
@@ -12156,14 +12369,28 @@ function DepositSuccessToast({
12156
12369
  }
12157
12370
  )
12158
12371
  ] }),
12159
- /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
12160
- "div",
12161
- {
12162
- className: "uf-font-medium uf-text-sm uf-flex-shrink-0",
12163
- style: { color: colors2.background },
12164
- children: formatUsdAmount(sourceAmountUsd)
12165
- }
12166
- ),
12372
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "uf-flex-shrink-0 uf-text-right", children: [
12373
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
12374
+ "div",
12375
+ {
12376
+ className: "uf-font-medium uf-text-sm",
12377
+ style: { color: colors2.background },
12378
+ children: formatUsdAmount(sourceAmountUsd)
12379
+ }
12380
+ ),
12381
+ isPending && execution?.estimated_processing_time && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
12382
+ "p",
12383
+ {
12384
+ className: "uf-text-xs",
12385
+ style: { color: colors2.foregroundMuted },
12386
+ children: [
12387
+ "Est.",
12388
+ " ",
12389
+ formatEstimatedTime(execution.estimated_processing_time)
12390
+ ]
12391
+ }
12392
+ )
12393
+ ] }),
12167
12394
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
12168
12395
  "button",
12169
12396
  {
@@ -12209,7 +12436,7 @@ function DepositPollingToasts({
12209
12436
  executions,
12210
12437
  horizontalPadding = "24px"
12211
12438
  }) {
12212
- const [closedExecutionIds, setClosedExecutionIds] = (0, import_react12.useState)(
12439
+ const [closedExecutionIds, setClosedExecutionIds] = (0, import_react13.useState)(
12213
12440
  /* @__PURE__ */ new Set()
12214
12441
  );
12215
12442
  const handleClose = (executionId) => {
@@ -12962,6 +13189,21 @@ function BuyWithCard({
12962
13189
  },
12963
13190
  children: quotesError
12964
13191
  }
13192
+ ),
13193
+ defaultToken?.estimated_processing_time && !quotesLoading && selectedProvider && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
13194
+ "div",
13195
+ {
13196
+ className: "uf-text-xs uf-mt-2 uf-px-1",
13197
+ style: {
13198
+ color: components.card.subtitleColor,
13199
+ fontFamily: fonts.regular
13200
+ },
13201
+ children: [
13202
+ "Estimated delivery time:",
13203
+ " ",
13204
+ formatEstimatedTime(defaultToken.estimated_processing_time)
13205
+ ]
13206
+ }
12965
13207
  )
12966
13208
  ] }),
12967
13209
  /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
@@ -13405,9 +13647,9 @@ function DepositsModal({
13405
13647
  themeClass = ""
13406
13648
  }) {
13407
13649
  const { colors: colors2 } = useTheme();
13408
- const [allExecutions, setAllExecutions] = (0, import_react15.useState)(sessionExecutions);
13409
- const [selectedExecution, setSelectedExecution] = (0, import_react15.useState)(null);
13410
- (0, import_react15.useEffect)(() => {
13650
+ const [allExecutions, setAllExecutions] = (0, import_react16.useState)(sessionExecutions);
13651
+ const [selectedExecution, setSelectedExecution] = (0, import_react16.useState)(null);
13652
+ (0, import_react16.useEffect)(() => {
13411
13653
  if (!open || !userId) return;
13412
13654
  const fetchExecutions = async () => {
13413
13655
  try {
@@ -13429,7 +13671,7 @@ function DepositsModal({
13429
13671
  clearInterval(pollInterval);
13430
13672
  };
13431
13673
  }, [open, userId, publishableKey, sessionExecutions]);
13432
- (0, import_react15.useEffect)(() => {
13674
+ (0, import_react16.useEffect)(() => {
13433
13675
  if (!open) {
13434
13676
  setSelectedExecution(null);
13435
13677
  }
@@ -13788,6 +14030,60 @@ function useAllowedCountry(publishableKey) {
13788
14030
  error
13789
14031
  };
13790
14032
  }
14033
+ function useAddressValidation({
14034
+ recipientAddress,
14035
+ destinationChainType,
14036
+ destinationChainId,
14037
+ destinationTokenAddress,
14038
+ publishableKey,
14039
+ enabled = true,
14040
+ refetchOnMount = false
14041
+ }) {
14042
+ const shouldValidate = enabled && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
14043
+ const { data, isLoading, error } = (0, import_react_query5.useQuery)({
14044
+ queryKey: [
14045
+ "unifold",
14046
+ "addressValidation",
14047
+ recipientAddress,
14048
+ destinationChainType,
14049
+ destinationChainId,
14050
+ destinationTokenAddress
14051
+ ],
14052
+ queryFn: () => verifyRecipientAddress(
14053
+ {
14054
+ chain_type: destinationChainType,
14055
+ chain_id: destinationChainId,
14056
+ token_address: destinationTokenAddress,
14057
+ recipient_address: recipientAddress
14058
+ },
14059
+ publishableKey
14060
+ ),
14061
+ enabled: shouldValidate,
14062
+ refetchOnMount,
14063
+ refetchOnReconnect: false,
14064
+ refetchOnWindowFocus: false,
14065
+ staleTime: 1e3 * 60 * 5,
14066
+ // 5 minutes - address state can change
14067
+ gcTime: 1e3 * 60 * 30
14068
+ // 30 minutes
14069
+ });
14070
+ if (!shouldValidate) {
14071
+ return {
14072
+ isValid: null,
14073
+ failureCode: null,
14074
+ metadata: null,
14075
+ isLoading: false,
14076
+ error: null
14077
+ };
14078
+ }
14079
+ return {
14080
+ isValid: data?.valid ?? null,
14081
+ failureCode: data?.failure_code ?? null,
14082
+ metadata: data?.metadata ?? null,
14083
+ isLoading,
14084
+ error: error ?? null
14085
+ };
14086
+ }
13791
14087
  function StyledQRCode({
13792
14088
  value,
13793
14089
  size: size4 = 200,
@@ -13795,9 +14091,9 @@ function StyledQRCode({
13795
14091
  imageSize = 50,
13796
14092
  darkMode = false
13797
14093
  }) {
13798
- const ref = (0, import_react17.useRef)(null);
13799
- const qrCodeRef = (0, import_react17.useRef)(null);
13800
- (0, import_react17.useEffect)(() => {
14094
+ const ref = (0, import_react18.useRef)(null);
14095
+ const qrCodeRef = (0, import_react18.useRef)(null);
14096
+ (0, import_react18.useEffect)(() => {
13801
14097
  if (!ref.current) return;
13802
14098
  if (!qrCodeRef.current) {
13803
14099
  qrCodeRef.current = new import_qr_code_styling.default({
@@ -13837,7 +14133,7 @@ function StyledQRCode({
13837
14133
  qrCodeRef.current.append(ref.current);
13838
14134
  }
13839
14135
  }, []);
13840
- (0, import_react17.useEffect)(() => {
14136
+ (0, import_react18.useEffect)(() => {
13841
14137
  if (qrCodeRef.current) {
13842
14138
  qrCodeRef.current.update({
13843
14139
  data: value,
@@ -13931,13 +14227,13 @@ function TokenSelectorSheet({
13931
14227
  }) {
13932
14228
  const { themeClass, colors: colors2, fonts, components } = useTheme();
13933
14229
  const isDarkMode = themeClass.includes("uf-dark");
13934
- const [searchQuery, setSearchQuery] = (0, import_react18.useState)("");
13935
- const [recentTokens, setRecentTokens] = (0, import_react18.useState)([]);
13936
- const [hoveredTokenKey, setHoveredTokenKey] = (0, import_react18.useState)(null);
13937
- (0, import_react18.useEffect)(() => {
14230
+ const [searchQuery, setSearchQuery] = (0, import_react19.useState)("");
14231
+ const [recentTokens, setRecentTokens] = (0, import_react19.useState)([]);
14232
+ const [hoveredTokenKey, setHoveredTokenKey] = (0, import_react19.useState)(null);
14233
+ (0, import_react19.useEffect)(() => {
13938
14234
  setRecentTokens(getRecentTokens());
13939
14235
  }, []);
13940
- const allOptions = (0, import_react18.useMemo)(() => {
14236
+ const allOptions = (0, import_react19.useMemo)(() => {
13941
14237
  const options = [];
13942
14238
  tokens.forEach((token) => {
13943
14239
  token.chains.forEach((chain) => {
@@ -13946,7 +14242,7 @@ function TokenSelectorSheet({
13946
14242
  });
13947
14243
  return options;
13948
14244
  }, [tokens]);
13949
- const quickSelectOptions = (0, import_react18.useMemo)(() => {
14245
+ const quickSelectOptions = (0, import_react19.useMemo)(() => {
13950
14246
  const result = [];
13951
14247
  const seen = /* @__PURE__ */ new Set();
13952
14248
  const addOption = (symbol, chainType, chainId, isRecent) => {
@@ -13978,7 +14274,7 @@ function TokenSelectorSheet({
13978
14274
  });
13979
14275
  setRecentTokens(updated);
13980
14276
  };
13981
- const filteredOptions = (0, import_react18.useMemo)(() => {
14277
+ const filteredOptions = (0, import_react19.useMemo)(() => {
13982
14278
  if (!searchQuery.trim()) return allOptions;
13983
14279
  const query = searchQuery.toLowerCase();
13984
14280
  return allOptions.filter(
@@ -14363,13 +14659,13 @@ function TransferCryptoSingleInput({
14363
14659
  }) {
14364
14660
  const { themeClass, colors: colors2, fonts, components } = useTheme();
14365
14661
  const isDarkMode = themeClass.includes("uf-dark");
14366
- const [token, setToken] = (0, import_react16.useState)("USDC");
14367
- const [chain, setChain] = (0, import_react16.useState)("solana:mainnet");
14368
- const [copied, setCopied] = (0, import_react16.useState)(false);
14369
- const [internalWallets, setInternalWallets] = (0, import_react16.useState)([]);
14370
- const [loading, setLoading] = (0, import_react16.useState)(!externalWallets?.length);
14662
+ const [token, setToken] = (0, import_react17.useState)("USDC");
14663
+ const [chain, setChain] = (0, import_react17.useState)("solana:mainnet");
14664
+ const [copied, setCopied] = (0, import_react17.useState)(false);
14665
+ const [internalWallets, setInternalWallets] = (0, import_react17.useState)([]);
14666
+ const [loading, setLoading] = (0, import_react17.useState)(!externalWallets?.length);
14371
14667
  const wallets = externalWallets?.length ? externalWallets : internalWallets;
14372
- const [error, setError] = (0, import_react16.useState)(null);
14668
+ const [error, setError] = (0, import_react17.useState)(null);
14373
14669
  const { executions: depositExecutions, isPolling } = useDepositPolling({
14374
14670
  userId,
14375
14671
  publishableKey,
@@ -14377,11 +14673,11 @@ function TransferCryptoSingleInput({
14377
14673
  onDepositSuccess,
14378
14674
  onDepositError
14379
14675
  });
14380
- const [supportedTokens, setSupportedTokens] = (0, import_react16.useState)([]);
14381
- const [tokensLoading, setTokensLoading] = (0, import_react16.useState)(true);
14382
- const [detailsExpanded, setDetailsExpanded] = (0, import_react16.useState)(false);
14383
- const [depositsModalOpen, setDepositsModalOpen] = (0, import_react16.useState)(false);
14384
- const [tokenSelectorOpen, setTokenSelectorOpen] = (0, import_react16.useState)(false);
14676
+ const [supportedTokens, setSupportedTokens] = (0, import_react17.useState)([]);
14677
+ const [tokensLoading, setTokensLoading] = (0, import_react17.useState)(true);
14678
+ const [detailsExpanded, setDetailsExpanded] = (0, import_react17.useState)(false);
14679
+ const [depositsModalOpen, setDepositsModalOpen] = (0, import_react17.useState)(false);
14680
+ const [tokenSelectorOpen, setTokenSelectorOpen] = (0, import_react17.useState)(false);
14385
14681
  const allChainsMap = /* @__PURE__ */ new Map();
14386
14682
  supportedTokens.forEach((t5) => {
14387
14683
  t5.chains.forEach((c) => {
@@ -14399,7 +14695,7 @@ function TransferCryptoSingleInput({
14399
14695
  const currentChainType = currentChainData?.chain_type || "ethereum";
14400
14696
  const currentWallet = getWalletByChainType(wallets, currentChainType);
14401
14697
  const depositAddress = currentWallet?.address || "";
14402
- (0, import_react16.useEffect)(() => {
14698
+ (0, import_react17.useEffect)(() => {
14403
14699
  async function fetchSupportedTokens() {
14404
14700
  try {
14405
14701
  setTokensLoading(true);
@@ -14471,12 +14767,12 @@ function TransferCryptoSingleInput({
14471
14767
  destinationChainId,
14472
14768
  destinationChainType
14473
14769
  ]);
14474
- (0, import_react16.useEffect)(() => {
14770
+ (0, import_react17.useEffect)(() => {
14475
14771
  if (onExecutionsChange) {
14476
14772
  onExecutionsChange(depositExecutions);
14477
14773
  }
14478
14774
  }, [depositExecutions, onExecutionsChange]);
14479
- (0, import_react16.useEffect)(() => {
14775
+ (0, import_react17.useEffect)(() => {
14480
14776
  if (externalWallets?.length) {
14481
14777
  setLoading(false);
14482
14778
  return;
@@ -14533,7 +14829,7 @@ function TransferCryptoSingleInput({
14533
14829
  publishableKey,
14534
14830
  externalWallets
14535
14831
  ]);
14536
- (0, import_react16.useEffect)(() => {
14832
+ (0, import_react17.useEffect)(() => {
14537
14833
  if (!supportedTokens.length) return;
14538
14834
  const currentToken = supportedTokens.find((t5) => t5.symbol === token);
14539
14835
  if (!currentToken || currentToken.chains.length === 0) return;
@@ -14725,24 +15021,24 @@ function TransferCryptoSingleInput({
14725
15021
  )
14726
15022
  ] })
14727
15023
  ] }),
14728
- loading ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-muted-foreground uf-animate-pulse", children: t2.loading }) : error ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-red-400", children: error }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "uf-relative", children: [
14729
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
14730
- "button",
14731
- {
14732
- onClick: handleCopyAddress,
14733
- disabled: !depositAddress,
14734
- className: "uf-w-full uf-bg-secondary hover:uf-bg-accent uf-rounded-lg uf-px-2.5 uf-py-2.5 uf-text-xs uf-font-mono uf-break-all uf-text-left uf-transition-colors uf-cursor-pointer disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
14735
- children: depositAddress || t2.noAddressAvailable
14736
- }
14737
- ),
14738
- depositAddress && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
14739
- "span",
14740
- {
14741
- className: `uf-absolute uf-inset-y-0 uf-right-3 uf-flex uf-items-center uf-pointer-events-none uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
14742
- children: copied ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Check, { className: "uf-w-3 uf-h-3" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Copy, { className: "uf-w-3 uf-h-3" })
14743
- }
14744
- )
14745
- ] })
15024
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-muted-foreground uf-animate-pulse", children: t2.loading }) : error ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-red-400", children: error }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
15025
+ "button",
15026
+ {
15027
+ onClick: handleCopyAddress,
15028
+ disabled: !depositAddress,
15029
+ className: "uf-w-full uf-bg-secondary hover:uf-bg-accent uf-rounded-lg uf-px-3 uf-py-2.5 uf-flex uf-items-center uf-justify-between uf-gap-2 uf-transition-colors uf-cursor-pointer disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
15030
+ children: [
15031
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "uf-text-xs uf-font-mono uf-truncate uf-min-w-0", children: depositAddress ? truncateAddress(depositAddress, 18, 12) : t2.noAddressAvailable }),
15032
+ depositAddress && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
15033
+ "span",
15034
+ {
15035
+ className: `uf-flex-shrink-0 uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
15036
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Check, { className: "uf-w-3.5 uf-h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Copy, { className: "uf-w-3.5 uf-h-3.5" })
15037
+ }
15038
+ )
15039
+ ]
15040
+ }
15041
+ )
14746
15042
  ] }),
14747
15043
  /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "uf-bg-secondary uf-rounded-xl uf-px-2.5", children: [
14748
15044
  /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
@@ -15045,13 +15341,13 @@ function TransferCryptoDoubleInput({
15045
15341
  }) {
15046
15342
  const { themeClass, colors: colors2, fonts, components } = useTheme();
15047
15343
  const isDarkMode = themeClass.includes("uf-dark");
15048
- const [token, setToken] = (0, import_react19.useState)("USDC");
15049
- const [chain, setChain] = (0, import_react19.useState)("solana:mainnet");
15050
- const [copied, setCopied] = (0, import_react19.useState)(false);
15051
- const [internalWallets, setInternalWallets] = (0, import_react19.useState)([]);
15052
- const [loading, setLoading] = (0, import_react19.useState)(!externalWallets?.length);
15344
+ const [token, setToken] = (0, import_react20.useState)("USDC");
15345
+ const [chain, setChain] = (0, import_react20.useState)("solana:mainnet");
15346
+ const [copied, setCopied] = (0, import_react20.useState)(false);
15347
+ const [internalWallets, setInternalWallets] = (0, import_react20.useState)([]);
15348
+ const [loading, setLoading] = (0, import_react20.useState)(!externalWallets?.length);
15053
15349
  const wallets = externalWallets?.length ? externalWallets : internalWallets;
15054
- const [error, setError] = (0, import_react19.useState)(null);
15350
+ const [error, setError] = (0, import_react20.useState)(null);
15055
15351
  const { executions: depositExecutions, isPolling } = useDepositPolling({
15056
15352
  userId,
15057
15353
  publishableKey,
@@ -15059,10 +15355,10 @@ function TransferCryptoDoubleInput({
15059
15355
  onDepositSuccess,
15060
15356
  onDepositError
15061
15357
  });
15062
- const [supportedTokens, setSupportedTokens] = (0, import_react19.useState)([]);
15063
- const [tokensLoading, setTokensLoading] = (0, import_react19.useState)(true);
15064
- const [detailsExpanded, setDetailsExpanded] = (0, import_react19.useState)(false);
15065
- const [depositsModalOpen, setDepositsModalOpen] = (0, import_react19.useState)(false);
15358
+ const [supportedTokens, setSupportedTokens] = (0, import_react20.useState)([]);
15359
+ const [tokensLoading, setTokensLoading] = (0, import_react20.useState)(true);
15360
+ const [detailsExpanded, setDetailsExpanded] = (0, import_react20.useState)(false);
15361
+ const [depositsModalOpen, setDepositsModalOpen] = (0, import_react20.useState)(false);
15066
15362
  const allChainsMap = /* @__PURE__ */ new Map();
15067
15363
  supportedTokens.forEach((t5) => {
15068
15364
  t5.chains.forEach((c) => {
@@ -15080,7 +15376,7 @@ function TransferCryptoDoubleInput({
15080
15376
  const currentChainType = currentChainData?.chain_type || "ethereum";
15081
15377
  const currentWallet = getWalletByChainType(wallets, currentChainType);
15082
15378
  const depositAddress = currentWallet?.address || "";
15083
- (0, import_react19.useEffect)(() => {
15379
+ (0, import_react20.useEffect)(() => {
15084
15380
  async function fetchSupportedTokens() {
15085
15381
  try {
15086
15382
  setTokensLoading(true);
@@ -15123,12 +15419,12 @@ function TransferCryptoDoubleInput({
15123
15419
  destinationChainId,
15124
15420
  destinationChainType
15125
15421
  ]);
15126
- (0, import_react19.useEffect)(() => {
15422
+ (0, import_react20.useEffect)(() => {
15127
15423
  if (onExecutionsChange) {
15128
15424
  onExecutionsChange(depositExecutions);
15129
15425
  }
15130
15426
  }, [depositExecutions, onExecutionsChange]);
15131
- (0, import_react19.useEffect)(() => {
15427
+ (0, import_react20.useEffect)(() => {
15132
15428
  if (externalWallets?.length) {
15133
15429
  setLoading(false);
15134
15430
  return;
@@ -15185,7 +15481,7 @@ function TransferCryptoDoubleInput({
15185
15481
  publishableKey,
15186
15482
  externalWallets
15187
15483
  ]);
15188
- (0, import_react19.useEffect)(() => {
15484
+ (0, import_react20.useEffect)(() => {
15189
15485
  if (!supportedTokens.length) return;
15190
15486
  const currentToken = supportedTokens.find((t5) => t5.symbol === token);
15191
15487
  if (!currentToken || currentToken.chains.length === 0) return;
@@ -15275,7 +15571,7 @@ function TransferCryptoDoubleInput({
15275
15571
  children: [
15276
15572
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "uf-grid uf-grid-cols-2 uf-gap-2.5", children: [
15277
15573
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { children: [
15278
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uf-text-xs uf-text-muted-foreground uf-mb-2 uf-flex uf-items-center uf-gap-1", children: t3.supportedToken }),
15574
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uf-text-xs uf-text-muted-foreground uf-mb-2 uf-flex uf-items-center uf-gap-1", children: t3.selectedToken }),
15279
15575
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
15280
15576
  Select2,
15281
15577
  {
@@ -15299,7 +15595,7 @@ function TransferCryptoDoubleInput({
15299
15595
  ] }),
15300
15596
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { children: [
15301
15597
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "uf-text-xs uf-text-muted-foreground uf-mb-2 uf-flex uf-items-center uf-gap-1", children: [
15302
- t3.supportedChain,
15598
+ t3.selectedChain,
15303
15599
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "uf-text-amber-400 uf-font-medium", children: [
15304
15600
  "$",
15305
15601
  minDepositUsd,
@@ -15413,24 +15709,24 @@ function TransferCryptoDoubleInput({
15413
15709
  )
15414
15710
  ] })
15415
15711
  ] }),
15416
- loading ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-muted-foreground uf-animate-pulse", children: t3.loading }) : error ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-red-400", children: error }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "uf-relative", children: [
15417
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
15418
- "button",
15419
- {
15420
- onClick: handleCopyAddress,
15421
- disabled: !depositAddress,
15422
- className: "uf-w-full uf-bg-secondary hover:uf-bg-accent uf-rounded-lg uf-px-2.5 uf-py-2.5 uf-text-xs uf-font-mono uf-break-all uf-text-left uf-transition-colors uf-cursor-pointer disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
15423
- children: depositAddress || t3.noAddressAvailable
15424
- }
15425
- ),
15426
- depositAddress && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
15427
- "span",
15428
- {
15429
- className: `uf-absolute uf-inset-y-0 uf-right-3 uf-flex uf-items-center uf-pointer-events-none uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
15430
- children: copied ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Check, { className: "uf-w-3 uf-h-3" }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Copy, { className: "uf-w-3 uf-h-3" })
15431
- }
15432
- )
15433
- ] })
15712
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-muted-foreground uf-animate-pulse", children: t3.loading }) : error ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "uf-bg-secondary uf-rounded-lg uf-px-3 uf-py-2.5 uf-text-xs uf-text-red-400", children: error }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
15713
+ "button",
15714
+ {
15715
+ onClick: handleCopyAddress,
15716
+ disabled: !depositAddress,
15717
+ className: "uf-w-full uf-bg-secondary hover:uf-bg-accent uf-rounded-lg uf-px-3 uf-py-2.5 uf-flex uf-items-center uf-justify-between uf-gap-2 uf-transition-colors uf-cursor-pointer disabled:uf-opacity-50 disabled:uf-cursor-not-allowed",
15718
+ children: [
15719
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "uf-text-xs uf-font-mono uf-truncate uf-min-w-0", children: depositAddress ? truncateAddress(depositAddress, 18, 12) : t3.noAddressAvailable }),
15720
+ depositAddress && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
15721
+ "span",
15722
+ {
15723
+ className: `uf-flex-shrink-0 uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
15724
+ children: copied ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Check, { className: "uf-w-3.5 uf-h-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Copy, { className: "uf-w-3.5 uf-h-3.5" })
15725
+ }
15726
+ )
15727
+ ]
15728
+ }
15729
+ )
15434
15730
  ] }),
15435
15731
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "uf-bg-secondary uf-rounded-xl uf-px-2.5", children: [
15436
15732
  /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
@@ -15633,12 +15929,13 @@ function DepositModal({
15633
15929
  destinationChainId,
15634
15930
  destinationTokenAddress,
15635
15931
  hideDepositTracker = false,
15932
+ showBalanceHeader = false,
15636
15933
  transferInputVariant = "double_input",
15637
15934
  onDepositSuccess,
15638
15935
  onDepositError,
15639
15936
  theme = "dark"
15640
15937
  }) {
15641
- const { colors: colors2 } = useTheme();
15938
+ const { colors: colors2, fonts } = useTheme();
15642
15939
  const [view, setView] = (0, import_react8.useState)("main");
15643
15940
  const [cardView, setCardView] = (0, import_react8.useState)(
15644
15941
  "amount"
@@ -15688,6 +15985,28 @@ function DepositModal({
15688
15985
  isLoading: isCountryLoading,
15689
15986
  error: countryError
15690
15987
  } = useAllowedCountry(publishableKey);
15988
+ const {
15989
+ isValid: isAddressValid,
15990
+ failureCode: addressFailureCode,
15991
+ metadata: addressFailureMetadata,
15992
+ isLoading: isAddressValidationLoading
15993
+ } = useAddressValidation({
15994
+ recipientAddress,
15995
+ destinationChainType,
15996
+ destinationChainId,
15997
+ destinationTokenAddress,
15998
+ publishableKey,
15999
+ enabled: open,
16000
+ // Only validate when modal is open
16001
+ refetchOnMount: "always"
16002
+ });
16003
+ const addressValidationMessages = i18n2.transferCrypto.addressValidation;
16004
+ const getAddressValidationErrorMessage = (code, metadata) => {
16005
+ if (!code) return addressValidationMessages.defaultError;
16006
+ const errors = addressValidationMessages.errors;
16007
+ const template = errors[code] ?? addressValidationMessages.defaultError;
16008
+ return interpolate(template, metadata);
16009
+ };
15691
16010
  (0, import_react8.useEffect)(() => {
15692
16011
  if (!open || wallets.length > 0) return;
15693
16012
  let retryTimeout = null;
@@ -15772,10 +16091,16 @@ function DepositModal({
15772
16091
  DepositHeader,
15773
16092
  {
15774
16093
  title: modalTitle || "Deposit",
15775
- onClose: handleClose
16094
+ onClose: handleClose,
16095
+ showBalance: showBalanceHeader,
16096
+ balanceAddress: recipientAddress,
16097
+ balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" ? destinationChainType : void 0,
16098
+ balanceChainId: destinationChainId,
16099
+ balanceTokenAddress: destinationTokenAddress,
16100
+ publishableKey
15776
16101
  }
15777
16102
  ),
15778
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "uf-pb-4 uf-space-y-3", children: isCountryLoading || !projectConfig ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
16103
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "uf-pb-4 uf-space-y-3", children: isCountryLoading || isAddressValidationLoading || !projectConfig ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
15779
16104
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SkeletonButton, { variant: "with-icons" }),
15780
16105
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SkeletonButton, { variant: "with-icons" }),
15781
16106
  !hideDepositTracker && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SkeletonButton, {})
@@ -15793,6 +16118,16 @@ function DepositModal({
15793
16118
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: "No Tokens Available" }),
15794
16119
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: "There are no supported tokens available from your current location." })
15795
16120
  ] })
16121
+ ) : isAddressValid === false ? (
16122
+ /* Invalid recipient address state (e.g., Algorand not opted in) */
16123
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
16124
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "uf-w-16 uf-h-16 uf-rounded-full uf-bg-muted uf-flex uf-items-center uf-justify-center uf-mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(TriangleAlert, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
16125
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: addressValidationMessages.unableToReceiveFunds }),
16126
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: getAddressValidationErrorMessage(
16127
+ addressFailureCode,
16128
+ addressFailureMetadata
16129
+ ) })
16130
+ ] })
15796
16131
  ) : (
15797
16132
  /* Normal deposit options */
15798
16133
  /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
@@ -15974,12 +16309,12 @@ function UnifoldProvider2({
15974
16309
  publishableKey,
15975
16310
  config
15976
16311
  }) {
15977
- const [isOpen, setIsOpen] = (0, import_react20.useState)(false);
15978
- const [depositConfig, setDepositConfig] = (0, import_react20.useState)(
16312
+ const [isOpen, setIsOpen] = (0, import_react21.useState)(false);
16313
+ const [depositConfig, setDepositConfig] = (0, import_react21.useState)(
15979
16314
  null
15980
16315
  );
15981
- const [resolvedTheme, setResolvedTheme] = import_react20.default.useState("dark");
15982
- import_react20.default.useEffect(() => {
16316
+ const [resolvedTheme, setResolvedTheme] = import_react21.default.useState("dark");
16317
+ import_react21.default.useEffect(() => {
15983
16318
  const appearance = config?.appearance || "dark";
15984
16319
  if (appearance === "auto") {
15985
16320
  const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
@@ -15994,9 +16329,9 @@ function UnifoldProvider2({
15994
16329
  setResolvedTheme(appearance);
15995
16330
  }
15996
16331
  }, [config?.appearance]);
15997
- const depositPromiseRef = import_react20.default.useRef(null);
15998
- const closeTimeoutRef = import_react20.default.useRef(null);
15999
- const beginDeposit = (0, import_react20.useCallback)((config2) => {
16332
+ const depositPromiseRef = import_react21.default.useRef(null);
16333
+ const closeTimeoutRef = import_react21.default.useRef(null);
16334
+ const beginDeposit = (0, import_react21.useCallback)((config2) => {
16000
16335
  if (closeTimeoutRef.current) {
16001
16336
  clearTimeout(closeTimeoutRef.current);
16002
16337
  closeTimeoutRef.current = null;
@@ -16016,7 +16351,7 @@ function UnifoldProvider2({
16016
16351
  setIsOpen(true);
16017
16352
  return promise;
16018
16353
  }, []);
16019
- const closeDeposit = (0, import_react20.useCallback)(() => {
16354
+ const closeDeposit = (0, import_react21.useCallback)(() => {
16020
16355
  if (depositPromiseRef.current) {
16021
16356
  depositPromiseRef.current.reject({
16022
16357
  message: "Deposit cancelled by user",
@@ -16030,7 +16365,7 @@ function UnifoldProvider2({
16030
16365
  closeTimeoutRef.current = null;
16031
16366
  }, 200);
16032
16367
  }, []);
16033
- const handleDepositSuccess = (0, import_react20.useCallback)((data) => {
16368
+ const handleDepositSuccess = (0, import_react21.useCallback)((data) => {
16034
16369
  if (depositConfig?.onSuccess) {
16035
16370
  depositConfig.onSuccess(data);
16036
16371
  }
@@ -16039,7 +16374,7 @@ function UnifoldProvider2({
16039
16374
  depositPromiseRef.current = null;
16040
16375
  }
16041
16376
  }, [depositConfig]);
16042
- const handleDepositError = (0, import_react20.useCallback)((error) => {
16377
+ const handleDepositError = (0, import_react21.useCallback)((error) => {
16043
16378
  console.error("[UnifoldProvider] Deposit error:", error);
16044
16379
  if (depositConfig?.onError) {
16045
16380
  depositConfig.onError(error);
@@ -16049,7 +16384,7 @@ function UnifoldProvider2({
16049
16384
  depositPromiseRef.current = null;
16050
16385
  }
16051
16386
  }, [depositConfig]);
16052
- const contextValue = (0, import_react20.useMemo)(
16387
+ const contextValue = (0, import_react21.useMemo)(
16053
16388
  () => ({
16054
16389
  beginDeposit,
16055
16390
  closeDeposit,
@@ -16083,6 +16418,7 @@ function UnifoldProvider2({
16083
16418
  destinationChainId: depositConfig.destinationChainId,
16084
16419
  destinationTokenAddress: depositConfig.destinationTokenAddress,
16085
16420
  hideDepositTracker: config?.hideDepositTracker,
16421
+ showBalanceHeader: config?.showBalanceHeader,
16086
16422
  transferInputVariant: config?.transferInputVariant,
16087
16423
  onDepositSuccess: handleDepositSuccess,
16088
16424
  onDepositError: handleDepositError,
@@ -16093,10 +16429,10 @@ function UnifoldProvider2({
16093
16429
  }
16094
16430
  ) }) });
16095
16431
  }
16096
- var ConnectContext = import_react20.default.createContext(null);
16432
+ var ConnectContext = import_react21.default.createContext(null);
16097
16433
  function useUnifold2() {
16098
16434
  const baseContext = useUnifold();
16099
- const connectContext = import_react20.default.useContext(ConnectContext);
16435
+ const connectContext = import_react21.default.useContext(ConnectContext);
16100
16436
  if (typeof window === "undefined") {
16101
16437
  return {
16102
16438
  publishableKey: "",