@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.mjs CHANGED
@@ -1145,7 +1145,7 @@ ${new this._window.XMLSerializer().serializeToString(e3)}`;
1145
1145
 
1146
1146
  // src/provider.tsx
1147
1147
  import React38, {
1148
- useState as useState22,
1148
+ useState as useState23,
1149
1149
  useCallback as useCallback10,
1150
1150
  useMemo as useMemo13
1151
1151
  } from "react";
@@ -1217,7 +1217,7 @@ function useUnifold() {
1217
1217
  }
1218
1218
 
1219
1219
  // ../ui-react/dist/index.mjs
1220
- import { useState as useState16, useEffect as useEffect132 } from "react";
1220
+ import { useState as useState17, useEffect as useEffect142 } from "react";
1221
1221
 
1222
1222
  // ../../node_modules/.pnpm/lucide-react@0.454.0_react@18.3.1/node_modules/lucide-react/dist/esm/createLucideIcon.js
1223
1223
  import { forwardRef as forwardRef2, createElement as createElement2 } from "react";
@@ -6007,7 +6007,7 @@ var twMerge = /* @__PURE__ */ createTailwindMerge(getDefaultConfig);
6007
6007
  import * as React36 from "react";
6008
6008
  import { jsx as jsx17 } from "react/jsx-runtime";
6009
6009
  import { jsx as jsx23, jsxs as jsxs4 } from "react/jsx-runtime";
6010
- import { useState as useState82, useEffect as useEffect42, useRef as useRef22 } from "react";
6010
+ import { useState as useState92, useEffect as useEffect52, useRef as useRef22 } from "react";
6011
6011
 
6012
6012
  // ../core/dist/index.mjs
6013
6013
  import { useQuery } from "@tanstack/react-query";
@@ -6294,12 +6294,14 @@ async function getTokenChains() {
6294
6294
  return response.json();
6295
6295
  }
6296
6296
  function getChainName(chains, chainType, chainId) {
6297
+ if (!chainType && !chainId) return "Unknown";
6297
6298
  const target = chains.find((c) => c.chain_id === chainId && c.chain_type === chainType);
6298
6299
  if (target) return target.chain_name;
6299
6300
  const byId = chains.find((c) => c.chain_id === chainId);
6300
6301
  if (byId) return byId.chain_name;
6301
6302
  const byType = chains.find((c) => c.chain_type === chainType);
6302
6303
  if (byType) return byType.chain_name;
6304
+ if (!chainType) return chainId || "Unknown";
6303
6305
  return chainType.charAt(0).toUpperCase() + chainType.slice(1);
6304
6306
  }
6305
6307
  async function getProjectConfig(publishableKey) {
@@ -6333,6 +6335,49 @@ async function getIpAddress() {
6333
6335
  }
6334
6336
  return response.json();
6335
6337
  }
6338
+ async function getAddressBalance(address, chainType, chainId, tokenAddress, publishableKey) {
6339
+ const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6340
+ validatePublishableKey(pk);
6341
+ const url = `${API_BASE_URL}/v1/public/addresses/balance`;
6342
+ const response = await fetch(url, {
6343
+ method: "POST",
6344
+ headers: {
6345
+ "Content-Type": "application/json",
6346
+ accept: "application/json",
6347
+ "x-publishable-key": pk
6348
+ },
6349
+ body: JSON.stringify({
6350
+ address,
6351
+ chain_type: chainType,
6352
+ chain_id: chainId,
6353
+ token_address: tokenAddress
6354
+ })
6355
+ });
6356
+ if (!response.ok) {
6357
+ throw new Error(`Failed to fetch address balance: ${response.statusText}`);
6358
+ }
6359
+ const data = await response.json();
6360
+ return data;
6361
+ }
6362
+ async function verifyRecipientAddress(request, publishableKey) {
6363
+ const pk = publishableKey || DEFAULT_PUBLISHABLE_KEY;
6364
+ validatePublishableKey(pk);
6365
+ const response = await fetch(`${API_BASE_URL}/v1/public/addresses/verify`, {
6366
+ method: "POST",
6367
+ headers: {
6368
+ accept: "application/json",
6369
+ "x-publishable-key": pk,
6370
+ "Content-Type": "application/json"
6371
+ },
6372
+ body: JSON.stringify(request)
6373
+ });
6374
+ if (!response.ok) {
6375
+ throw new Error(
6376
+ `Failed to verify recipient address: ${response.statusText}`
6377
+ );
6378
+ }
6379
+ return response.json();
6380
+ }
6336
6381
  function useUserIp() {
6337
6382
  const {
6338
6383
  data: userIpInfo,
@@ -6432,22 +6477,23 @@ var en_default = {
6432
6477
  var i18n = en_default;
6433
6478
 
6434
6479
  // ../ui-react/dist/index.mjs
6435
- import { useState as useState32 } from "react";
6480
+ import { useState as useState42 } from "react";
6481
+ import { useEffect as useEffect22, useState as useState22 } from "react";
6436
6482
  import { jsx as jsx32, jsxs as jsxs22 } from "react/jsx-runtime";
6437
6483
  import * as React37 from "react";
6438
6484
  import { jsx as jsx42, jsxs as jsxs32 } from "react/jsx-runtime";
6439
6485
  import { Fragment as Fragment8, jsx as jsx52, jsxs as jsxs42 } from "react/jsx-runtime";
6440
6486
  import { jsx as jsx62, jsxs as jsxs5 } from "react/jsx-runtime";
6441
6487
  import { useQuery as useQuery2 } from "@tanstack/react-query";
6442
- import { useState as useState42, useEffect as useEffect22, useRef as useRef16 } from "react";
6488
+ import { useState as useState52, useEffect as useEffect32, useRef as useRef16 } from "react";
6489
+ import { useState as useState82 } from "react";
6443
6490
  import { useState as useState72 } from "react";
6444
- import { useState as useState62 } from "react";
6445
- import { useEffect as useEffect32, useState as useState52 } from "react";
6491
+ import { useEffect as useEffect42, useState as useState62 } from "react";
6446
6492
  import { jsx as jsx72, jsxs as jsxs6 } from "react/jsx-runtime";
6447
6493
  import { Fragment as Fragment23, jsx as jsx82, jsxs as jsxs7 } from "react/jsx-runtime";
6448
6494
  import { jsx as jsx92 } from "react/jsx-runtime";
6449
6495
  import { jsx as jsx102, jsxs as jsxs8 } from "react/jsx-runtime";
6450
- import { useEffect as useEffect52, useState as useState92 } from "react";
6496
+ import { useEffect as useEffect62, useState as useState102 } from "react";
6451
6497
  import { jsx as jsx112, jsxs as jsxs9 } from "react/jsx-runtime";
6452
6498
  import * as React42 from "react";
6453
6499
  import { jsx as jsx122 } from "react/jsx-runtime";
@@ -6459,11 +6505,12 @@ import { jsx as jsx152, jsxs as jsxs12 } from "react/jsx-runtime";
6459
6505
  import * as React72 from "react";
6460
6506
  import { jsx as jsx162, jsxs as jsxs13 } from "react/jsx-runtime";
6461
6507
  import { useQuery as useQuery22 } from "@tanstack/react-query";
6462
- import { useState as useState142, useEffect as useEffect112 } from "react";
6508
+ import { useQuery as useQuery3 } from "@tanstack/react-query";
6509
+ import { useState as useState152, useEffect as useEffect122 } from "react";
6463
6510
  var import_qr_code_styling = __toESM(require_qr_code_styling(), 1);
6464
- import { useEffect as useEffect92, useRef as useRef32 } from "react";
6511
+ import { useEffect as useEffect102, useRef as useRef32 } from "react";
6465
6512
  import { jsx as jsx172 } from "react/jsx-runtime";
6466
- import { useState as useState132, useMemo as useMemo32, useEffect as useEffect102 } from "react";
6513
+ import { useState as useState142, useMemo as useMemo32, useEffect as useEffect112 } from "react";
6467
6514
  import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
6468
6515
  import * as React82 from "react";
6469
6516
 
@@ -9205,7 +9252,7 @@ var Content22 = TooltipContent;
9205
9252
  // ../ui-react/dist/index.mjs
9206
9253
  import { jsx as jsx19 } from "react/jsx-runtime";
9207
9254
  import { Fragment as Fragment42, jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
9208
- import { useState as useState152, useEffect as useEffect122 } from "react";
9255
+ import { useState as useState162, useEffect as useEffect132 } from "react";
9209
9256
  import * as React92 from "react";
9210
9257
 
9211
9258
  // ../../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
@@ -10594,6 +10641,35 @@ import { jsx as jsx24 } from "react/jsx-runtime";
10594
10641
  function cn(...inputs) {
10595
10642
  return twMerge(clsx(inputs));
10596
10643
  }
10644
+ function truncateAddress(address, startChars = 10, endChars = 8) {
10645
+ if (!address) return "";
10646
+ const totalChars = startChars + endChars + 3;
10647
+ if (address.length <= totalChars) return address;
10648
+ return `${address.slice(0, startChars)}...${address.slice(-endChars)}`;
10649
+ }
10650
+ function formatEstimatedTime(seconds) {
10651
+ if (seconds == null) {
10652
+ return "< 1 min";
10653
+ }
10654
+ if (seconds < 60) {
10655
+ return `< ${seconds} sec${seconds > 1 ? "s" : ""}`;
10656
+ } else if (seconds < 3600) {
10657
+ const mins = Math.ceil(seconds / 60);
10658
+ return `< ${mins} min${mins > 1 ? "s" : ""}`;
10659
+ } else {
10660
+ let hrs = Math.floor(seconds / 3600);
10661
+ let mins = Math.ceil(seconds % 3600 / 60);
10662
+ if (mins === 60) {
10663
+ hrs += 1;
10664
+ mins = 0;
10665
+ }
10666
+ const hrLabel = hrs > 1 ? "hrs" : "hr";
10667
+ if (mins === 0) {
10668
+ return `< ${hrs} ${hrLabel}`;
10669
+ }
10670
+ return `< ${hrs} ${hrLabel} ${mins} min${mins > 1 ? "s" : ""}`;
10671
+ }
10672
+ }
10597
10673
  var defaultColors = {
10598
10674
  light: {
10599
10675
  // Background colors
@@ -11035,10 +11111,83 @@ function DepositHeader({
11035
11111
  showClose = true,
11036
11112
  onBack,
11037
11113
  onClose,
11038
- badge
11114
+ badge,
11115
+ showBalance = false,
11116
+ balanceAddress,
11117
+ balanceChainType,
11118
+ balanceChainId,
11119
+ balanceTokenAddress,
11120
+ publishableKey
11039
11121
  }) {
11040
11122
  const { colors: colors2, fonts, components } = useTheme();
11041
- return /* @__PURE__ */ jsxs22("div", { className: "uf-flex uf-items-center uf-justify-between uf-pb-6", children: [
11123
+ const [balance, setBalance] = useState22(null);
11124
+ const [isLoadingBalance, setIsLoadingBalance] = useState22(false);
11125
+ useEffect22(() => {
11126
+ if (!showBalance || !balanceAddress || !balanceChainType || !balanceChainId || !balanceTokenAddress || !publishableKey) {
11127
+ setBalance(null);
11128
+ setIsLoadingBalance(false);
11129
+ return;
11130
+ }
11131
+ let cancelled = false;
11132
+ setIsLoadingBalance(true);
11133
+ getAddressBalance(
11134
+ balanceAddress,
11135
+ balanceChainType,
11136
+ balanceChainId,
11137
+ balanceTokenAddress,
11138
+ publishableKey
11139
+ ).then((response) => {
11140
+ if (cancelled) return;
11141
+ if (response.balance && response.balance.amount !== "0") {
11142
+ const value = Number(response.balance.amount) / 10 ** response.balance.token.decimals;
11143
+ let formatted;
11144
+ let maxDecimals = 4;
11145
+ const symbol = response.balance.token.symbol?.toUpperCase() || "";
11146
+ if (symbol === "BTC" || symbol === "WBTC") {
11147
+ maxDecimals = 8;
11148
+ } else if (symbol === "ETH" || symbol === "WETH") {
11149
+ maxDecimals = 6;
11150
+ }
11151
+ if (value >= 1) {
11152
+ formatted = value.toLocaleString(void 0, {
11153
+ minimumFractionDigits: 2,
11154
+ maximumFractionDigits: maxDecimals
11155
+ });
11156
+ } else if (value > 0) {
11157
+ formatted = value.toLocaleString(void 0, {
11158
+ minimumFractionDigits: 2,
11159
+ maximumFractionDigits: maxDecimals,
11160
+ minimumSignificantDigits: 2,
11161
+ maximumSignificantDigits: 6
11162
+ });
11163
+ } else {
11164
+ formatted = value.toExponential(2);
11165
+ }
11166
+ const balanceText = response.balance.amount_usd ? `Balance: $${response.balance.amount_usd} (${formatted} ${response.balance.token.symbol})` : `Balance: ${formatted} ${response.balance.token.symbol}`;
11167
+ setBalance(balanceText);
11168
+ } else {
11169
+ setBalance(null);
11170
+ }
11171
+ }).catch((error) => {
11172
+ if (cancelled) return;
11173
+ console.error("Error fetching balance:", error);
11174
+ setBalance(null);
11175
+ }).finally(() => {
11176
+ if (cancelled) return;
11177
+ setIsLoadingBalance(false);
11178
+ });
11179
+ return () => {
11180
+ cancelled = true;
11181
+ };
11182
+ }, [
11183
+ showBalance,
11184
+ balanceAddress,
11185
+ balanceChainType,
11186
+ balanceChainId,
11187
+ balanceTokenAddress,
11188
+ publishableKey
11189
+ ]);
11190
+ return /* @__PURE__ */ jsx32("div", { children: /* @__PURE__ */ jsxs22("div", { className: "uf-flex uf-items-center uf-justify-between uf-pb-6", children: [
11042
11191
  showBack ? /* @__PURE__ */ jsx32(
11043
11192
  "button",
11044
11193
  {
@@ -11048,8 +11197,32 @@ function DepositHeader({
11048
11197
  children: /* @__PURE__ */ jsx32(ArrowLeft, { className: "uf-w-5 uf-h-5" })
11049
11198
  }
11050
11199
  ) : /* @__PURE__ */ jsx32("div", { className: "uf-w-5 uf-h-5 uf-invisible" }),
11051
- badge ? /* @__PURE__ */ jsxs22("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
11052
- /* @__PURE__ */ jsx32(
11200
+ /* @__PURE__ */ jsxs22("div", { className: "uf-flex uf-flex-col uf-items-center", children: [
11201
+ badge ? /* @__PURE__ */ jsxs22("div", { className: "uf-flex uf-items-center uf-gap-2", children: [
11202
+ /* @__PURE__ */ jsx32(
11203
+ DialogTitle2,
11204
+ {
11205
+ className: "uf-text-center uf-text-base",
11206
+ style: {
11207
+ color: components.header.titleColor,
11208
+ fontFamily: fonts.medium
11209
+ },
11210
+ children: title
11211
+ }
11212
+ ),
11213
+ /* @__PURE__ */ jsx32(
11214
+ "div",
11215
+ {
11216
+ className: "uf-px-2 uf-py-0.5 uf-rounded-full uf-text-[10px]",
11217
+ style: {
11218
+ backgroundColor: colors2.card,
11219
+ color: colors2.foregroundMuted,
11220
+ fontFamily: fonts.regular
11221
+ },
11222
+ children: badge.count
11223
+ }
11224
+ )
11225
+ ] }) : /* @__PURE__ */ jsx32(
11053
11226
  DialogTitle2,
11054
11227
  {
11055
11228
  className: "uf-text-center uf-text-base",
@@ -11060,29 +11233,19 @@ function DepositHeader({
11060
11233
  children: title
11061
11234
  }
11062
11235
  ),
11063
- /* @__PURE__ */ jsx32(
11236
+ showBalance && (isLoadingBalance ? /* @__PURE__ */ jsx32("div", { className: "uf-h-3 uf-w-32 uf-bg-muted uf-rounded uf-animate-pulse uf-mt-2" }) : balance ? /* @__PURE__ */ jsx32(
11064
11237
  "div",
11065
11238
  {
11066
- className: "uf-px-2 uf-py-0.5 uf-rounded-full uf-text-[10px]",
11239
+ className: "uf-text-xs uf-mt-2",
11067
11240
  style: {
11068
- backgroundColor: colors2.card,
11069
- color: colors2.foregroundMuted,
11070
- fontFamily: fonts.regular
11241
+ color: colors2.foreground,
11242
+ fontFamily: fonts.regular,
11243
+ opacity: 0.7
11071
11244
  },
11072
- children: badge.count
11245
+ children: balance
11073
11246
  }
11074
- )
11075
- ] }) : /* @__PURE__ */ jsx32(
11076
- DialogTitle2,
11077
- {
11078
- className: "uf-text-center uf-text-base",
11079
- style: {
11080
- color: components.header.titleColor,
11081
- fontFamily: fonts.medium
11082
- },
11083
- children: title
11084
- }
11085
- ),
11247
+ ) : null)
11248
+ ] }),
11086
11249
  showClose ? /* @__PURE__ */ jsx32(
11087
11250
  "button",
11088
11251
  {
@@ -11092,7 +11255,7 @@ function DepositHeader({
11092
11255
  children: /* @__PURE__ */ jsx32(X, { className: "uf-w-5 uf-h-5" })
11093
11256
  }
11094
11257
  ) : /* @__PURE__ */ jsx32("div", { className: "uf-w-5 uf-h-5 uf-invisible" })
11095
- ] });
11258
+ ] }) });
11096
11259
  }
11097
11260
  function CurrencyListItem({
11098
11261
  currency,
@@ -11193,7 +11356,7 @@ function CurrencyModal({
11193
11356
  themeClass = ""
11194
11357
  }) {
11195
11358
  const { colors: colors2, fonts, components } = useTheme();
11196
- const [searchQuery, setSearchQuery] = useState32("");
11359
+ const [searchQuery, setSearchQuery] = useState42("");
11197
11360
  const preferredCurrencies = preferredCurrencyCodes.map(
11198
11361
  (code) => currencies.find(
11199
11362
  (currency) => currency.currency_code.toLowerCase() === code.toLowerCase()
@@ -11348,8 +11511,8 @@ var en_default2 = {
11348
11511
  seeTerms: "See terms",
11349
11512
  termsApply: "Terms apply"
11350
11513
  },
11351
- supportedToken: "Supported token",
11352
- supportedChain: "Supported chain",
11514
+ selectedToken: "Selected token",
11515
+ selectedChain: "Selected chain",
11353
11516
  depositAddress: {
11354
11517
  label: "Your deposit address",
11355
11518
  tooltip: "Send any supported token to this address, and it will be automatically converted to {{token}} in your account."
@@ -11363,7 +11526,19 @@ var en_default2 = {
11363
11526
  minimumDeposit: "Minimum: {{amount}}",
11364
11527
  minimumDepositTooltip: "The minimum amount you can deposit on the selected network.",
11365
11528
  selectTokenDeposit: "Your deposit token",
11366
- selectTokenDepositTooltip: "Select the token you want to deposit with in order to begin the deposit process."
11529
+ selectTokenDepositTooltip: "Select the token you want to deposit with in order to begin the deposit process.",
11530
+ addressValidation: {
11531
+ validating: "Verifying recipient address...",
11532
+ unableToReceiveFunds: "Unable to Receive Funds",
11533
+ errors: {
11534
+ token_not_supported: "The destination token is not supported",
11535
+ not_opted_in: "Please make sure you opt-in {{token_symbol}}({{chain_name}}) before receiving funds",
11536
+ insufficient_balance: "Recipient account does not meet the minimum balance requirement",
11537
+ account_not_found: "Recipient account does not exist on {{chain_name}}",
11538
+ validation_error: "Unable to verify recipient address on {{chain_name}}"
11539
+ },
11540
+ defaultError: "The recipient address cannot receive funds for the selected token"
11541
+ }
11367
11542
  },
11368
11543
  depositModal: {
11369
11544
  transferCrypto: {
@@ -11372,7 +11547,7 @@ var en_default2 = {
11372
11547
  },
11373
11548
  depositWithCard: {
11374
11549
  title: "Deposit with Card",
11375
- subtitle: "$50,000 limit \u2022 2 min"
11550
+ subtitle: "$50,000 limit"
11376
11551
  },
11377
11552
  quotes: "Quotes"
11378
11553
  },
@@ -11388,6 +11563,13 @@ var en_default2 = {
11388
11563
  }
11389
11564
  };
11390
11565
  var i18n2 = en_default2;
11566
+ function interpolate(template, params) {
11567
+ if (!params) return template;
11568
+ return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
11569
+ const value = params[key];
11570
+ return value !== void 0 ? String(value) : `{{${key}}}`;
11571
+ });
11572
+ }
11391
11573
  function useDepositPolling({
11392
11574
  userId,
11393
11575
  publishableKey,
@@ -11395,14 +11577,14 @@ function useDepositPolling({
11395
11577
  onDepositSuccess,
11396
11578
  onDepositError
11397
11579
  }) {
11398
- const [executions, setExecutions] = useState42([]);
11399
- const [isPolling, setIsPolling] = useState42(false);
11580
+ const [executions, setExecutions] = useState52([]);
11581
+ const [isPolling, setIsPolling] = useState52(false);
11400
11582
  const pollingIntervalRef = useRef16(
11401
11583
  null
11402
11584
  );
11403
- const [modalOpenedAt] = useState42(/* @__PURE__ */ new Date());
11404
- const [trackedExecutions, setTrackedExecutions] = useState42(/* @__PURE__ */ new Map());
11405
- useEffect22(() => {
11585
+ const [modalOpenedAt] = useState52(/* @__PURE__ */ new Date());
11586
+ const [trackedExecutions, setTrackedExecutions] = useState52(/* @__PURE__ */ new Map());
11587
+ useEffect32(() => {
11406
11588
  if (!userId || !modalOpenedAt || !enabled) return;
11407
11589
  const pollInterval = setInterval(async () => {
11408
11590
  try {
@@ -11513,12 +11695,12 @@ function formatCurrency(currency) {
11513
11695
  }
11514
11696
  function DepositDetailContent({ execution }) {
11515
11697
  const { colors: colors2, fonts, components } = useTheme();
11516
- const [chains, setChains] = useState52([]);
11517
- const [showNetworkDetails, setShowNetworkDetails] = useState52(false);
11518
- useEffect32(() => {
11698
+ const [chains, setChains] = useState62([]);
11699
+ const [showNetworkDetails, setShowNetworkDetails] = useState62(false);
11700
+ useEffect42(() => {
11519
11701
  getTokenChains().then((response) => setChains(response.data)).catch((err) => console.error("Failed to fetch chains:", err));
11520
11702
  }, []);
11521
- useEffect32(() => {
11703
+ useEffect42(() => {
11522
11704
  setShowNetworkDetails(false);
11523
11705
  }, [execution?.id]);
11524
11706
  const isPending = execution.status === ExecutionStatus.PENDING || execution.status === ExecutionStatus.WAITING || execution.status === ExecutionStatus.DELAYED;
@@ -11813,6 +11995,37 @@ function DepositDetailContent({ execution }) {
11813
11995
  ]
11814
11996
  }
11815
11997
  ),
11998
+ isPending && /* @__PURE__ */ jsxs6(
11999
+ "div",
12000
+ {
12001
+ className: "uf-flex uf-justify-between uf-items-center uf-px-4 uf-py-3 uf-border-b",
12002
+ style: { borderColor: colors2.border },
12003
+ children: [
12004
+ /* @__PURE__ */ jsx72(
12005
+ "span",
12006
+ {
12007
+ className: "uf-text-sm",
12008
+ style: {
12009
+ color: components.card.labelColor,
12010
+ fontFamily: fonts.regular
12011
+ },
12012
+ children: "Estimated delivery time"
12013
+ }
12014
+ ),
12015
+ /* @__PURE__ */ jsx72(
12016
+ "span",
12017
+ {
12018
+ style: {
12019
+ color: components.card.titleColor,
12020
+ fontFamily: fonts.regular,
12021
+ fontSize: "14px"
12022
+ },
12023
+ children: formatEstimatedTime(execution?.estimated_processing_time)
12024
+ }
12025
+ )
12026
+ ]
12027
+ }
12028
+ ),
11816
12029
  /* @__PURE__ */ jsxs6(
11817
12030
  "div",
11818
12031
  {
@@ -11996,7 +12209,7 @@ function DepositSuccessToast({
11996
12209
  onClose,
11997
12210
  execution
11998
12211
  }) {
11999
- const [detailModalOpen, setDetailModalOpen] = useState62(false);
12212
+ const [detailModalOpen, setDetailModalOpen] = useState72(false);
12000
12213
  const { themeClass, colors: colors2, fonts, components } = useTheme();
12001
12214
  const isPending = status === ExecutionStatus.PENDING || status === ExecutionStatus.WAITING || status === ExecutionStatus.DELAYED;
12002
12215
  const formatDateTime = (timestamp) => {
@@ -12130,14 +12343,28 @@ function DepositSuccessToast({
12130
12343
  }
12131
12344
  )
12132
12345
  ] }),
12133
- /* @__PURE__ */ jsx82(
12134
- "div",
12135
- {
12136
- className: "uf-font-medium uf-text-sm uf-flex-shrink-0",
12137
- style: { color: colors2.background },
12138
- children: formatUsdAmount(sourceAmountUsd)
12139
- }
12140
- ),
12346
+ /* @__PURE__ */ jsxs7("div", { className: "uf-flex-shrink-0 uf-text-right", children: [
12347
+ /* @__PURE__ */ jsx82(
12348
+ "div",
12349
+ {
12350
+ className: "uf-font-medium uf-text-sm",
12351
+ style: { color: colors2.background },
12352
+ children: formatUsdAmount(sourceAmountUsd)
12353
+ }
12354
+ ),
12355
+ isPending && execution?.estimated_processing_time && /* @__PURE__ */ jsxs7(
12356
+ "p",
12357
+ {
12358
+ className: "uf-text-xs",
12359
+ style: { color: colors2.foregroundMuted },
12360
+ children: [
12361
+ "Est.",
12362
+ " ",
12363
+ formatEstimatedTime(execution.estimated_processing_time)
12364
+ ]
12365
+ }
12366
+ )
12367
+ ] }),
12141
12368
  /* @__PURE__ */ jsx82(
12142
12369
  "button",
12143
12370
  {
@@ -12183,7 +12410,7 @@ function DepositPollingToasts({
12183
12410
  executions,
12184
12411
  horizontalPadding = "24px"
12185
12412
  }) {
12186
- const [closedExecutionIds, setClosedExecutionIds] = useState72(
12413
+ const [closedExecutionIds, setClosedExecutionIds] = useState82(
12187
12414
  /* @__PURE__ */ new Set()
12188
12415
  );
12189
12416
  const handleClose = (executionId) => {
@@ -12261,28 +12488,28 @@ function BuyWithCard({
12261
12488
  assetCdnUrl
12262
12489
  }) {
12263
12490
  const { colors: colors2, fonts, components } = useTheme();
12264
- const [amount, setAmount] = useState82("");
12265
- const [currency, setCurrency] = useState82("usd");
12266
- const [hasManualCurrencySelection, setHasManualCurrencySelection] = useState82(false);
12267
- const [hasManualAmountEntry, setHasManualAmountEntry] = useState82(false);
12268
- const [showCurrencyModal, setShowCurrencyModal] = useState82(false);
12269
- const [quotes, setQuotes] = useState82([]);
12270
- const [quotesLoading, setQuotesLoading] = useState82(false);
12271
- const [quotesError, setQuotesError] = useState82(null);
12272
- const [amountValidationError, setAmountValidationError] = useState82(null);
12273
- const [internalView, setInternalView] = useState82("amount");
12274
- const [defaultToken, setDefaultToken] = useState82(
12491
+ const [amount, setAmount] = useState92("");
12492
+ const [currency, setCurrency] = useState92("usd");
12493
+ const [hasManualCurrencySelection, setHasManualCurrencySelection] = useState92(false);
12494
+ const [hasManualAmountEntry, setHasManualAmountEntry] = useState92(false);
12495
+ const [showCurrencyModal, setShowCurrencyModal] = useState92(false);
12496
+ const [quotes, setQuotes] = useState92([]);
12497
+ const [quotesLoading, setQuotesLoading] = useState92(false);
12498
+ const [quotesError, setQuotesError] = useState92(null);
12499
+ const [amountValidationError, setAmountValidationError] = useState92(null);
12500
+ const [internalView, setInternalView] = useState92("amount");
12501
+ const [defaultToken, setDefaultToken] = useState92(
12275
12502
  null
12276
12503
  );
12277
- const [defaultTokenLoading, setDefaultTokenLoading] = useState82(false);
12504
+ const [defaultTokenLoading, setDefaultTokenLoading] = useState92(false);
12278
12505
  const { userIpInfo, isLoading: isLoadingIp } = useUserIp2();
12279
- const [onrampSession, setOnrampSession] = useState82(
12506
+ const [onrampSession, setOnrampSession] = useState92(
12280
12507
  null
12281
12508
  );
12282
12509
  const currentView = externalView ?? internalView;
12283
12510
  const showQuotesView = currentView === "quotes";
12284
12511
  const showOnrampView = currentView === "onramp";
12285
- useEffect42(() => {
12512
+ useEffect52(() => {
12286
12513
  if (externalView) {
12287
12514
  setInternalView(externalView);
12288
12515
  }
@@ -12295,31 +12522,31 @@ function BuyWithCard({
12295
12522
  onViewChange?.(newView);
12296
12523
  }
12297
12524
  };
12298
- const [selectedProvider, setSelectedProvider] = useState82(
12525
+ const [selectedProvider, setSelectedProvider] = useState92(
12299
12526
  null
12300
12527
  );
12301
- const [isAutoSelected, setIsAutoSelected] = useState82(true);
12302
- const [autoSelectedProvider, setAutoSelectedProvider] = useState82(null);
12303
- const [hoveredProviderIndex, setHoveredProviderIndex] = useState82(null);
12304
- const [hasManualSelection, setHasManualSelection] = useState82(false);
12528
+ const [isAutoSelected, setIsAutoSelected] = useState92(true);
12529
+ const [autoSelectedProvider, setAutoSelectedProvider] = useState92(null);
12530
+ const [hoveredProviderIndex, setHoveredProviderIndex] = useState92(null);
12531
+ const [hasManualSelection, setHasManualSelection] = useState92(false);
12305
12532
  const selectedProviderRef = useRef22(null);
12306
12533
  const hasManualSelectionRef = useRef22(false);
12307
- useEffect42(() => {
12534
+ useEffect52(() => {
12308
12535
  selectedProviderRef.current = selectedProvider;
12309
12536
  }, [selectedProvider]);
12310
- useEffect42(() => {
12537
+ useEffect52(() => {
12311
12538
  hasManualSelectionRef.current = hasManualSelection;
12312
12539
  }, [hasManualSelection]);
12313
- const [internalWallets, setInternalWallets] = useState82([]);
12314
- const [walletsLoading, setWalletsLoading] = useState82(
12540
+ const [internalWallets, setInternalWallets] = useState92([]);
12541
+ const [walletsLoading, setWalletsLoading] = useState92(
12315
12542
  !externalWallets?.length
12316
12543
  );
12317
12544
  const wallets = externalWallets?.length ? externalWallets : internalWallets;
12318
- const [countdown, setCountdown] = useState82(60);
12319
- const [fiatCurrencies, setFiatCurrencies] = useState82([]);
12320
- const [preferredCurrencyCodes, setPreferredCurrencyCodes] = useState82([]);
12321
- const [currenciesLoading, setCurrenciesLoading] = useState82(true);
12322
- const [destinationToken, setDestinationToken] = useState82(null);
12545
+ const [countdown, setCountdown] = useState92(60);
12546
+ const [fiatCurrencies, setFiatCurrencies] = useState92([]);
12547
+ const [preferredCurrencyCodes, setPreferredCurrencyCodes] = useState92([]);
12548
+ const [currenciesLoading, setCurrenciesLoading] = useState92(true);
12549
+ const [destinationToken, setDestinationToken] = useState92(null);
12323
12550
  const { executions, isPolling } = useDepositPolling({
12324
12551
  userId,
12325
12552
  publishableKey,
@@ -12331,7 +12558,7 @@ function BuyWithCard({
12331
12558
  const destinationTokenIcon = destinationToken?.icon_url;
12332
12559
  const destinationChainIcon = destinationToken?.chain_icon_url;
12333
12560
  const destinationChainName = destinationToken?.chain_name;
12334
- useEffect42(() => {
12561
+ useEffect52(() => {
12335
12562
  async function fetchFiatCurrencies() {
12336
12563
  try {
12337
12564
  const response = await getFiatCurrencies(publishableKey);
@@ -12345,7 +12572,7 @@ function BuyWithCard({
12345
12572
  }
12346
12573
  fetchFiatCurrencies();
12347
12574
  }, [publishableKey]);
12348
- useEffect42(() => {
12575
+ useEffect52(() => {
12349
12576
  if (hasManualCurrencySelection) return;
12350
12577
  if (fiatCurrencies.length === 0 || !userIpInfo?.alpha2) return;
12351
12578
  const userCountryCode = userIpInfo.alpha2.toUpperCase();
@@ -12373,7 +12600,7 @@ function BuyWithCard({
12373
12600
  hasManualAmountEntry
12374
12601
  ]);
12375
12602
  const prevCurrencyRef = useRef22(null);
12376
- useEffect42(() => {
12603
+ useEffect52(() => {
12377
12604
  if (fiatCurrencies.length === 0) return;
12378
12605
  if (prevCurrencyRef.current !== null && prevCurrencyRef.current !== currency) {
12379
12606
  const currentCurrency = fiatCurrencies.find(
@@ -12385,7 +12612,7 @@ function BuyWithCard({
12385
12612
  }
12386
12613
  prevCurrencyRef.current = currency;
12387
12614
  }, [currency]);
12388
- useEffect42(() => {
12615
+ useEffect52(() => {
12389
12616
  if (externalWallets?.length) {
12390
12617
  setWalletsLoading(false);
12391
12618
  return;
@@ -12438,7 +12665,7 @@ function BuyWithCard({
12438
12665
  publishableKey,
12439
12666
  externalWallets
12440
12667
  ]);
12441
- useEffect42(() => {
12668
+ useEffect52(() => {
12442
12669
  async function fetchDestinationToken() {
12443
12670
  try {
12444
12671
  const response = await getTokenMetadata(
@@ -12456,7 +12683,7 @@ function BuyWithCard({
12456
12683
  }
12457
12684
  fetchDestinationToken();
12458
12685
  }, [publishableKey]);
12459
- useEffect42(() => {
12686
+ useEffect52(() => {
12460
12687
  async function fetchDefaultToken() {
12461
12688
  if (!destinationTokenAddress || !destinationChainId || !destinationChainType) {
12462
12689
  return;
@@ -12492,7 +12719,7 @@ function BuyWithCard({
12492
12719
  isLoadingIp,
12493
12720
  publishableKey
12494
12721
  ]);
12495
- useEffect42(() => {
12722
+ useEffect52(() => {
12496
12723
  const amountNum = parseFloat(amount);
12497
12724
  if (isNaN(amountNum) || amountNum <= 0) {
12498
12725
  setQuotes([]);
@@ -12601,7 +12828,7 @@ function BuyWithCard({
12601
12828
  setQuotesLoading(false);
12602
12829
  }
12603
12830
  };
12604
- useEffect42(() => {
12831
+ useEffect52(() => {
12605
12832
  if (quotes.length === 0) return;
12606
12833
  const timer = setInterval(() => {
12607
12834
  setCountdown((prev) => {
@@ -12936,6 +13163,21 @@ function BuyWithCard({
12936
13163
  },
12937
13164
  children: quotesError
12938
13165
  }
13166
+ ),
13167
+ defaultToken?.estimated_processing_time && !quotesLoading && selectedProvider && /* @__PURE__ */ jsxs8(
13168
+ "div",
13169
+ {
13170
+ className: "uf-text-xs uf-mt-2 uf-px-1",
13171
+ style: {
13172
+ color: components.card.subtitleColor,
13173
+ fontFamily: fonts.regular
13174
+ },
13175
+ children: [
13176
+ "Estimated delivery time:",
13177
+ " ",
13178
+ formatEstimatedTime(defaultToken.estimated_processing_time)
13179
+ ]
13180
+ }
12939
13181
  )
12940
13182
  ] }),
12941
13183
  /* @__PURE__ */ jsx102(
@@ -13379,9 +13621,9 @@ function DepositsModal({
13379
13621
  themeClass = ""
13380
13622
  }) {
13381
13623
  const { colors: colors2 } = useTheme();
13382
- const [allExecutions, setAllExecutions] = useState92(sessionExecutions);
13383
- const [selectedExecution, setSelectedExecution] = useState92(null);
13384
- useEffect52(() => {
13624
+ const [allExecutions, setAllExecutions] = useState102(sessionExecutions);
13625
+ const [selectedExecution, setSelectedExecution] = useState102(null);
13626
+ useEffect62(() => {
13385
13627
  if (!open || !userId) return;
13386
13628
  const fetchExecutions = async () => {
13387
13629
  try {
@@ -13403,7 +13645,7 @@ function DepositsModal({
13403
13645
  clearInterval(pollInterval);
13404
13646
  };
13405
13647
  }, [open, userId, publishableKey, sessionExecutions]);
13406
- useEffect52(() => {
13648
+ useEffect62(() => {
13407
13649
  if (!open) {
13408
13650
  setSelectedExecution(null);
13409
13651
  }
@@ -13762,6 +14004,60 @@ function useAllowedCountry(publishableKey) {
13762
14004
  error
13763
14005
  };
13764
14006
  }
14007
+ function useAddressValidation({
14008
+ recipientAddress,
14009
+ destinationChainType,
14010
+ destinationChainId,
14011
+ destinationTokenAddress,
14012
+ publishableKey,
14013
+ enabled = true,
14014
+ refetchOnMount = false
14015
+ }) {
14016
+ const shouldValidate = enabled && !!recipientAddress && !!destinationChainType && !!destinationChainId && !!destinationTokenAddress;
14017
+ const { data, isLoading, error } = useQuery3({
14018
+ queryKey: [
14019
+ "unifold",
14020
+ "addressValidation",
14021
+ recipientAddress,
14022
+ destinationChainType,
14023
+ destinationChainId,
14024
+ destinationTokenAddress
14025
+ ],
14026
+ queryFn: () => verifyRecipientAddress(
14027
+ {
14028
+ chain_type: destinationChainType,
14029
+ chain_id: destinationChainId,
14030
+ token_address: destinationTokenAddress,
14031
+ recipient_address: recipientAddress
14032
+ },
14033
+ publishableKey
14034
+ ),
14035
+ enabled: shouldValidate,
14036
+ refetchOnMount,
14037
+ refetchOnReconnect: false,
14038
+ refetchOnWindowFocus: false,
14039
+ staleTime: 1e3 * 60 * 5,
14040
+ // 5 minutes - address state can change
14041
+ gcTime: 1e3 * 60 * 30
14042
+ // 30 minutes
14043
+ });
14044
+ if (!shouldValidate) {
14045
+ return {
14046
+ isValid: null,
14047
+ failureCode: null,
14048
+ metadata: null,
14049
+ isLoading: false,
14050
+ error: null
14051
+ };
14052
+ }
14053
+ return {
14054
+ isValid: data?.valid ?? null,
14055
+ failureCode: data?.failure_code ?? null,
14056
+ metadata: data?.metadata ?? null,
14057
+ isLoading,
14058
+ error: error ?? null
14059
+ };
14060
+ }
13765
14061
  function StyledQRCode({
13766
14062
  value,
13767
14063
  size: size4 = 200,
@@ -13771,7 +14067,7 @@ function StyledQRCode({
13771
14067
  }) {
13772
14068
  const ref = useRef32(null);
13773
14069
  const qrCodeRef = useRef32(null);
13774
- useEffect92(() => {
14070
+ useEffect102(() => {
13775
14071
  if (!ref.current) return;
13776
14072
  if (!qrCodeRef.current) {
13777
14073
  qrCodeRef.current = new import_qr_code_styling.default({
@@ -13811,7 +14107,7 @@ function StyledQRCode({
13811
14107
  qrCodeRef.current.append(ref.current);
13812
14108
  }
13813
14109
  }, []);
13814
- useEffect92(() => {
14110
+ useEffect102(() => {
13815
14111
  if (qrCodeRef.current) {
13816
14112
  qrCodeRef.current.update({
13817
14113
  data: value,
@@ -13905,10 +14201,10 @@ function TokenSelectorSheet({
13905
14201
  }) {
13906
14202
  const { themeClass, colors: colors2, fonts, components } = useTheme();
13907
14203
  const isDarkMode = themeClass.includes("uf-dark");
13908
- const [searchQuery, setSearchQuery] = useState132("");
13909
- const [recentTokens, setRecentTokens] = useState132([]);
13910
- const [hoveredTokenKey, setHoveredTokenKey] = useState132(null);
13911
- useEffect102(() => {
14204
+ const [searchQuery, setSearchQuery] = useState142("");
14205
+ const [recentTokens, setRecentTokens] = useState142([]);
14206
+ const [hoveredTokenKey, setHoveredTokenKey] = useState142(null);
14207
+ useEffect112(() => {
13912
14208
  setRecentTokens(getRecentTokens());
13913
14209
  }, []);
13914
14210
  const allOptions = useMemo32(() => {
@@ -14337,13 +14633,13 @@ function TransferCryptoSingleInput({
14337
14633
  }) {
14338
14634
  const { themeClass, colors: colors2, fonts, components } = useTheme();
14339
14635
  const isDarkMode = themeClass.includes("uf-dark");
14340
- const [token, setToken] = useState142("USDC");
14341
- const [chain, setChain] = useState142("solana:mainnet");
14342
- const [copied, setCopied] = useState142(false);
14343
- const [internalWallets, setInternalWallets] = useState142([]);
14344
- const [loading, setLoading] = useState142(!externalWallets?.length);
14636
+ const [token, setToken] = useState152("USDC");
14637
+ const [chain, setChain] = useState152("solana:mainnet");
14638
+ const [copied, setCopied] = useState152(false);
14639
+ const [internalWallets, setInternalWallets] = useState152([]);
14640
+ const [loading, setLoading] = useState152(!externalWallets?.length);
14345
14641
  const wallets = externalWallets?.length ? externalWallets : internalWallets;
14346
- const [error, setError] = useState142(null);
14642
+ const [error, setError] = useState152(null);
14347
14643
  const { executions: depositExecutions, isPolling } = useDepositPolling({
14348
14644
  userId,
14349
14645
  publishableKey,
@@ -14351,11 +14647,11 @@ function TransferCryptoSingleInput({
14351
14647
  onDepositSuccess,
14352
14648
  onDepositError
14353
14649
  });
14354
- const [supportedTokens, setSupportedTokens] = useState142([]);
14355
- const [tokensLoading, setTokensLoading] = useState142(true);
14356
- const [detailsExpanded, setDetailsExpanded] = useState142(false);
14357
- const [depositsModalOpen, setDepositsModalOpen] = useState142(false);
14358
- const [tokenSelectorOpen, setTokenSelectorOpen] = useState142(false);
14650
+ const [supportedTokens, setSupportedTokens] = useState152([]);
14651
+ const [tokensLoading, setTokensLoading] = useState152(true);
14652
+ const [detailsExpanded, setDetailsExpanded] = useState152(false);
14653
+ const [depositsModalOpen, setDepositsModalOpen] = useState152(false);
14654
+ const [tokenSelectorOpen, setTokenSelectorOpen] = useState152(false);
14359
14655
  const allChainsMap = /* @__PURE__ */ new Map();
14360
14656
  supportedTokens.forEach((t5) => {
14361
14657
  t5.chains.forEach((c) => {
@@ -14373,7 +14669,7 @@ function TransferCryptoSingleInput({
14373
14669
  const currentChainType = currentChainData?.chain_type || "ethereum";
14374
14670
  const currentWallet = getWalletByChainType(wallets, currentChainType);
14375
14671
  const depositAddress = currentWallet?.address || "";
14376
- useEffect112(() => {
14672
+ useEffect122(() => {
14377
14673
  async function fetchSupportedTokens() {
14378
14674
  try {
14379
14675
  setTokensLoading(true);
@@ -14445,12 +14741,12 @@ function TransferCryptoSingleInput({
14445
14741
  destinationChainId,
14446
14742
  destinationChainType
14447
14743
  ]);
14448
- useEffect112(() => {
14744
+ useEffect122(() => {
14449
14745
  if (onExecutionsChange) {
14450
14746
  onExecutionsChange(depositExecutions);
14451
14747
  }
14452
14748
  }, [depositExecutions, onExecutionsChange]);
14453
- useEffect112(() => {
14749
+ useEffect122(() => {
14454
14750
  if (externalWallets?.length) {
14455
14751
  setLoading(false);
14456
14752
  return;
@@ -14507,7 +14803,7 @@ function TransferCryptoSingleInput({
14507
14803
  publishableKey,
14508
14804
  externalWallets
14509
14805
  ]);
14510
- useEffect112(() => {
14806
+ useEffect122(() => {
14511
14807
  if (!supportedTokens.length) return;
14512
14808
  const currentToken = supportedTokens.find((t5) => t5.symbol === token);
14513
14809
  if (!currentToken || currentToken.chains.length === 0) return;
@@ -14699,24 +14995,24 @@ function TransferCryptoSingleInput({
14699
14995
  )
14700
14996
  ] })
14701
14997
  ] }),
14702
- loading ? /* @__PURE__ */ jsx20("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__ */ jsx20("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__ */ jsxs15("div", { className: "uf-relative", children: [
14703
- /* @__PURE__ */ jsx20(
14704
- "button",
14705
- {
14706
- onClick: handleCopyAddress,
14707
- disabled: !depositAddress,
14708
- 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",
14709
- children: depositAddress || t2.noAddressAvailable
14710
- }
14711
- ),
14712
- depositAddress && /* @__PURE__ */ jsx20(
14713
- "span",
14714
- {
14715
- 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"}`,
14716
- children: copied ? /* @__PURE__ */ jsx20(Check, { className: "uf-w-3 uf-h-3" }) : /* @__PURE__ */ jsx20(Copy, { className: "uf-w-3 uf-h-3" })
14717
- }
14718
- )
14719
- ] })
14998
+ loading ? /* @__PURE__ */ jsx20("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__ */ jsx20("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__ */ jsxs15(
14999
+ "button",
15000
+ {
15001
+ onClick: handleCopyAddress,
15002
+ disabled: !depositAddress,
15003
+ 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",
15004
+ children: [
15005
+ /* @__PURE__ */ jsx20("span", { className: "uf-text-xs uf-font-mono uf-truncate uf-min-w-0", children: depositAddress ? truncateAddress(depositAddress, 18, 12) : t2.noAddressAvailable }),
15006
+ depositAddress && /* @__PURE__ */ jsx20(
15007
+ "span",
15008
+ {
15009
+ className: `uf-flex-shrink-0 uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
15010
+ children: copied ? /* @__PURE__ */ jsx20(Check, { className: "uf-w-3.5 uf-h-3.5" }) : /* @__PURE__ */ jsx20(Copy, { className: "uf-w-3.5 uf-h-3.5" })
15011
+ }
15012
+ )
15013
+ ]
15014
+ }
15015
+ )
14720
15016
  ] }),
14721
15017
  /* @__PURE__ */ jsxs15("div", { className: "uf-bg-secondary uf-rounded-xl uf-px-2.5", children: [
14722
15018
  /* @__PURE__ */ jsxs15(
@@ -15019,13 +15315,13 @@ function TransferCryptoDoubleInput({
15019
15315
  }) {
15020
15316
  const { themeClass, colors: colors2, fonts, components } = useTheme();
15021
15317
  const isDarkMode = themeClass.includes("uf-dark");
15022
- const [token, setToken] = useState152("USDC");
15023
- const [chain, setChain] = useState152("solana:mainnet");
15024
- const [copied, setCopied] = useState152(false);
15025
- const [internalWallets, setInternalWallets] = useState152([]);
15026
- const [loading, setLoading] = useState152(!externalWallets?.length);
15318
+ const [token, setToken] = useState162("USDC");
15319
+ const [chain, setChain] = useState162("solana:mainnet");
15320
+ const [copied, setCopied] = useState162(false);
15321
+ const [internalWallets, setInternalWallets] = useState162([]);
15322
+ const [loading, setLoading] = useState162(!externalWallets?.length);
15027
15323
  const wallets = externalWallets?.length ? externalWallets : internalWallets;
15028
- const [error, setError] = useState152(null);
15324
+ const [error, setError] = useState162(null);
15029
15325
  const { executions: depositExecutions, isPolling } = useDepositPolling({
15030
15326
  userId,
15031
15327
  publishableKey,
@@ -15033,10 +15329,10 @@ function TransferCryptoDoubleInput({
15033
15329
  onDepositSuccess,
15034
15330
  onDepositError
15035
15331
  });
15036
- const [supportedTokens, setSupportedTokens] = useState152([]);
15037
- const [tokensLoading, setTokensLoading] = useState152(true);
15038
- const [detailsExpanded, setDetailsExpanded] = useState152(false);
15039
- const [depositsModalOpen, setDepositsModalOpen] = useState152(false);
15332
+ const [supportedTokens, setSupportedTokens] = useState162([]);
15333
+ const [tokensLoading, setTokensLoading] = useState162(true);
15334
+ const [detailsExpanded, setDetailsExpanded] = useState162(false);
15335
+ const [depositsModalOpen, setDepositsModalOpen] = useState162(false);
15040
15336
  const allChainsMap = /* @__PURE__ */ new Map();
15041
15337
  supportedTokens.forEach((t5) => {
15042
15338
  t5.chains.forEach((c) => {
@@ -15054,7 +15350,7 @@ function TransferCryptoDoubleInput({
15054
15350
  const currentChainType = currentChainData?.chain_type || "ethereum";
15055
15351
  const currentWallet = getWalletByChainType(wallets, currentChainType);
15056
15352
  const depositAddress = currentWallet?.address || "";
15057
- useEffect122(() => {
15353
+ useEffect132(() => {
15058
15354
  async function fetchSupportedTokens() {
15059
15355
  try {
15060
15356
  setTokensLoading(true);
@@ -15097,12 +15393,12 @@ function TransferCryptoDoubleInput({
15097
15393
  destinationChainId,
15098
15394
  destinationChainType
15099
15395
  ]);
15100
- useEffect122(() => {
15396
+ useEffect132(() => {
15101
15397
  if (onExecutionsChange) {
15102
15398
  onExecutionsChange(depositExecutions);
15103
15399
  }
15104
15400
  }, [depositExecutions, onExecutionsChange]);
15105
- useEffect122(() => {
15401
+ useEffect132(() => {
15106
15402
  if (externalWallets?.length) {
15107
15403
  setLoading(false);
15108
15404
  return;
@@ -15159,7 +15455,7 @@ function TransferCryptoDoubleInput({
15159
15455
  publishableKey,
15160
15456
  externalWallets
15161
15457
  ]);
15162
- useEffect122(() => {
15458
+ useEffect132(() => {
15163
15459
  if (!supportedTokens.length) return;
15164
15460
  const currentToken = supportedTokens.find((t5) => t5.symbol === token);
15165
15461
  if (!currentToken || currentToken.chains.length === 0) return;
@@ -15249,7 +15545,7 @@ function TransferCryptoDoubleInput({
15249
15545
  children: [
15250
15546
  /* @__PURE__ */ jsxs17("div", { className: "uf-grid uf-grid-cols-2 uf-gap-2.5", children: [
15251
15547
  /* @__PURE__ */ jsxs17("div", { children: [
15252
- /* @__PURE__ */ jsx222("div", { className: "uf-text-xs uf-text-muted-foreground uf-mb-2 uf-flex uf-items-center uf-gap-1", children: t3.supportedToken }),
15548
+ /* @__PURE__ */ jsx222("div", { className: "uf-text-xs uf-text-muted-foreground uf-mb-2 uf-flex uf-items-center uf-gap-1", children: t3.selectedToken }),
15253
15549
  /* @__PURE__ */ jsxs17(
15254
15550
  Select2,
15255
15551
  {
@@ -15273,7 +15569,7 @@ function TransferCryptoDoubleInput({
15273
15569
  ] }),
15274
15570
  /* @__PURE__ */ jsxs17("div", { children: [
15275
15571
  /* @__PURE__ */ jsxs17("div", { className: "uf-text-xs uf-text-muted-foreground uf-mb-2 uf-flex uf-items-center uf-gap-1", children: [
15276
- t3.supportedChain,
15572
+ t3.selectedChain,
15277
15573
  /* @__PURE__ */ jsxs17("span", { className: "uf-text-amber-400 uf-font-medium", children: [
15278
15574
  "$",
15279
15575
  minDepositUsd,
@@ -15387,24 +15683,24 @@ function TransferCryptoDoubleInput({
15387
15683
  )
15388
15684
  ] })
15389
15685
  ] }),
15390
- loading ? /* @__PURE__ */ jsx222("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__ */ jsx222("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__ */ jsxs17("div", { className: "uf-relative", children: [
15391
- /* @__PURE__ */ jsx222(
15392
- "button",
15393
- {
15394
- onClick: handleCopyAddress,
15395
- disabled: !depositAddress,
15396
- 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",
15397
- children: depositAddress || t3.noAddressAvailable
15398
- }
15399
- ),
15400
- depositAddress && /* @__PURE__ */ jsx222(
15401
- "span",
15402
- {
15403
- 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"}`,
15404
- children: copied ? /* @__PURE__ */ jsx222(Check, { className: "uf-w-3 uf-h-3" }) : /* @__PURE__ */ jsx222(Copy, { className: "uf-w-3 uf-h-3" })
15405
- }
15406
- )
15407
- ] })
15686
+ loading ? /* @__PURE__ */ jsx222("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__ */ jsx222("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__ */ jsxs17(
15687
+ "button",
15688
+ {
15689
+ onClick: handleCopyAddress,
15690
+ disabled: !depositAddress,
15691
+ 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",
15692
+ children: [
15693
+ /* @__PURE__ */ jsx222("span", { className: "uf-text-xs uf-font-mono uf-truncate uf-min-w-0", children: depositAddress ? truncateAddress(depositAddress, 18, 12) : t3.noAddressAvailable }),
15694
+ depositAddress && /* @__PURE__ */ jsx222(
15695
+ "span",
15696
+ {
15697
+ className: `uf-flex-shrink-0 uf-transition-colors ${copied ? "uf-text-green-500" : "uf-text-muted-foreground"}`,
15698
+ children: copied ? /* @__PURE__ */ jsx222(Check, { className: "uf-w-3.5 uf-h-3.5" }) : /* @__PURE__ */ jsx222(Copy, { className: "uf-w-3.5 uf-h-3.5" })
15699
+ }
15700
+ )
15701
+ ]
15702
+ }
15703
+ )
15408
15704
  ] }),
15409
15705
  /* @__PURE__ */ jsxs17("div", { className: "uf-bg-secondary uf-rounded-xl uf-px-2.5", children: [
15410
15706
  /* @__PURE__ */ jsxs17(
@@ -15607,26 +15903,27 @@ function DepositModal({
15607
15903
  destinationChainId,
15608
15904
  destinationTokenAddress,
15609
15905
  hideDepositTracker = false,
15906
+ showBalanceHeader = false,
15610
15907
  transferInputVariant = "double_input",
15611
15908
  onDepositSuccess,
15612
15909
  onDepositError,
15613
15910
  theme = "dark"
15614
15911
  }) {
15615
- const { colors: colors2 } = useTheme();
15616
- const [view, setView] = useState16("main");
15617
- const [cardView, setCardView] = useState16(
15912
+ const { colors: colors2, fonts } = useTheme();
15913
+ const [view, setView] = useState17("main");
15914
+ const [cardView, setCardView] = useState17(
15618
15915
  "amount"
15619
15916
  );
15620
- const [quotesCount, setQuotesCount] = useState16(0);
15621
- const [depositsModalOpen, setDepositsModalOpen] = useState16(false);
15622
- const [depositExecutions, setDepositExecutions] = useState16([]);
15623
- const [projectConfig, setProjectConfig] = useState16(null);
15624
- const [wallets, setWallets] = useState16([]);
15625
- const [walletsLoading, setWalletsLoading] = useState16(false);
15626
- useEffect132(() => {
15917
+ const [quotesCount, setQuotesCount] = useState17(0);
15918
+ const [depositsModalOpen, setDepositsModalOpen] = useState17(false);
15919
+ const [depositExecutions, setDepositExecutions] = useState17([]);
15920
+ const [projectConfig, setProjectConfig] = useState17(null);
15921
+ const [wallets, setWallets] = useState17([]);
15922
+ const [walletsLoading, setWalletsLoading] = useState17(false);
15923
+ useEffect142(() => {
15627
15924
  setProjectConfig(null);
15628
15925
  }, [publishableKey]);
15629
- useEffect132(() => {
15926
+ useEffect142(() => {
15630
15927
  setWallets([]);
15631
15928
  }, [
15632
15929
  userId,
@@ -15636,10 +15933,10 @@ function DepositModal({
15636
15933
  destinationTokenAddress,
15637
15934
  publishableKey
15638
15935
  ]);
15639
- const [resolvedTheme, setResolvedTheme] = useState16(
15936
+ const [resolvedTheme, setResolvedTheme] = useState17(
15640
15937
  theme === "auto" ? "dark" : theme
15641
15938
  );
15642
- useEffect132(() => {
15939
+ useEffect142(() => {
15643
15940
  if (theme === "auto") {
15644
15941
  const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
15645
15942
  setResolvedTheme(mediaQuery.matches ? "dark" : "light");
@@ -15652,7 +15949,7 @@ function DepositModal({
15652
15949
  setResolvedTheme(theme);
15653
15950
  }
15654
15951
  }, [theme]);
15655
- useEffect132(() => {
15952
+ useEffect142(() => {
15656
15953
  if (open && !projectConfig) {
15657
15954
  getProjectConfig(publishableKey).then(setProjectConfig).catch(console.error);
15658
15955
  }
@@ -15662,7 +15959,29 @@ function DepositModal({
15662
15959
  isLoading: isCountryLoading,
15663
15960
  error: countryError
15664
15961
  } = useAllowedCountry(publishableKey);
15665
- useEffect132(() => {
15962
+ const {
15963
+ isValid: isAddressValid,
15964
+ failureCode: addressFailureCode,
15965
+ metadata: addressFailureMetadata,
15966
+ isLoading: isAddressValidationLoading
15967
+ } = useAddressValidation({
15968
+ recipientAddress,
15969
+ destinationChainType,
15970
+ destinationChainId,
15971
+ destinationTokenAddress,
15972
+ publishableKey,
15973
+ enabled: open,
15974
+ // Only validate when modal is open
15975
+ refetchOnMount: "always"
15976
+ });
15977
+ const addressValidationMessages = i18n2.transferCrypto.addressValidation;
15978
+ const getAddressValidationErrorMessage = (code, metadata) => {
15979
+ if (!code) return addressValidationMessages.defaultError;
15980
+ const errors = addressValidationMessages.errors;
15981
+ const template = errors[code] ?? addressValidationMessages.defaultError;
15982
+ return interpolate(template, metadata);
15983
+ };
15984
+ useEffect142(() => {
15666
15985
  if (!open || wallets.length > 0) return;
15667
15986
  let retryTimeout = null;
15668
15987
  let isCancelled = false;
@@ -15746,10 +16065,16 @@ function DepositModal({
15746
16065
  DepositHeader,
15747
16066
  {
15748
16067
  title: modalTitle || "Deposit",
15749
- onClose: handleClose
16068
+ onClose: handleClose,
16069
+ showBalance: showBalanceHeader,
16070
+ balanceAddress: recipientAddress,
16071
+ balanceChainType: destinationChainType === "ethereum" || destinationChainType === "solana" || destinationChainType === "bitcoin" ? destinationChainType : void 0,
16072
+ balanceChainId: destinationChainId,
16073
+ balanceTokenAddress: destinationTokenAddress,
16074
+ publishableKey
15750
16075
  }
15751
16076
  ),
15752
- /* @__PURE__ */ jsx232("div", { className: "uf-pb-4 uf-space-y-3", children: isCountryLoading || !projectConfig ? /* @__PURE__ */ jsxs18(Fragment52, { children: [
16077
+ /* @__PURE__ */ jsx232("div", { className: "uf-pb-4 uf-space-y-3", children: isCountryLoading || isAddressValidationLoading || !projectConfig ? /* @__PURE__ */ jsxs18(Fragment52, { children: [
15753
16078
  /* @__PURE__ */ jsx232(SkeletonButton, { variant: "with-icons" }),
15754
16079
  /* @__PURE__ */ jsx232(SkeletonButton, { variant: "with-icons" }),
15755
16080
  !hideDepositTracker && /* @__PURE__ */ jsx232(SkeletonButton, {})
@@ -15767,6 +16092,16 @@ function DepositModal({
15767
16092
  /* @__PURE__ */ jsx232("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: "No Tokens Available" }),
15768
16093
  /* @__PURE__ */ jsx232("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: "There are no supported tokens available from your current location." })
15769
16094
  ] })
16095
+ ) : isAddressValid === false ? (
16096
+ /* Invalid recipient address state (e.g., Algorand not opted in) */
16097
+ /* @__PURE__ */ jsxs18("div", { className: "uf-flex uf-flex-col uf-items-center uf-justify-center uf-py-8 uf-px-4 uf-text-center", children: [
16098
+ /* @__PURE__ */ jsx232("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__ */ jsx232(TriangleAlert, { className: "uf-w-8 uf-h-8 uf-text-muted-foreground" }) }),
16099
+ /* @__PURE__ */ jsx232("h3", { className: "uf-text-lg uf-font-semibold uf-text-foreground uf-mb-2", children: addressValidationMessages.unableToReceiveFunds }),
16100
+ /* @__PURE__ */ jsx232("p", { className: "uf-text-sm uf-text-muted-foreground uf-max-w-[280px]", children: getAddressValidationErrorMessage(
16101
+ addressFailureCode,
16102
+ addressFailureMetadata
16103
+ ) })
16104
+ ] })
15770
16105
  ) : (
15771
16106
  /* Normal deposit options */
15772
16107
  /* @__PURE__ */ jsxs18(Fragment52, { children: [
@@ -15948,8 +16283,8 @@ function UnifoldProvider2({
15948
16283
  publishableKey,
15949
16284
  config
15950
16285
  }) {
15951
- const [isOpen, setIsOpen] = useState22(false);
15952
- const [depositConfig, setDepositConfig] = useState22(
16286
+ const [isOpen, setIsOpen] = useState23(false);
16287
+ const [depositConfig, setDepositConfig] = useState23(
15953
16288
  null
15954
16289
  );
15955
16290
  const [resolvedTheme, setResolvedTheme] = React38.useState("dark");
@@ -16057,6 +16392,7 @@ function UnifoldProvider2({
16057
16392
  destinationChainId: depositConfig.destinationChainId,
16058
16393
  destinationTokenAddress: depositConfig.destinationTokenAddress,
16059
16394
  hideDepositTracker: config?.hideDepositTracker,
16395
+ showBalanceHeader: config?.showBalanceHeader,
16060
16396
  transferInputVariant: config?.transferInputVariant,
16061
16397
  onDepositSuccess: handleDepositSuccess,
16062
16398
  onDepositError: handleDepositError,