@rash2x/bridge-widget 0.1.4 → 0.1.5

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.
@@ -13,19 +13,20 @@ import { twMerge } from "tailwind-merge";
13
13
  import { createPortal } from "react-dom";
14
14
  import { AnimatePresence, motion } from "framer-motion";
15
15
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
16
- import { useAccount, useDisconnect, useWalletClient } from "wagmi";
16
+ import { useAccount, useConnect, useDisconnect, useWalletClient, usePublicClient } from "wagmi";
17
17
  import { useWallet } from "@tronweb3/tronwallet-adapter-react-hooks";
18
18
  import { useTonAddress, useTonConnectUI } from "@tonconnect/ui-react";
19
19
  import { useQuery, useQueryClient } from "@tanstack/react-query";
20
- import { Address, Cell } from "@ton/core";
20
+ import { Address, beginCell as beginCell$1, storeMessage, loadMessage, Cell } from "@ton/core";
21
21
  import { VariableSizeList } from "react-window";
22
22
  import * as SwitchPrimitive from "@radix-ui/react-switch";
23
23
  import { X, ChevronDownIcon } from "lucide-react";
24
24
  import * as AccordionPrimitive from "@radix-ui/react-accordion";
25
- import { ConnectKitButton } from "connectkit";
26
25
  import { t } from "i18next";
27
26
  import { toast, Toaster } from "sonner";
28
- import { ethers, BrowserProvider, Contract, parseUnits } from "ethers";
27
+ import { BrowserProvider, Contract, parseUnits } from "ethers";
28
+ import { isAddress, formatUnits } from "viem";
29
+ import { TonClient, Address as Address$1, beginCell } from "@ton/ton";
29
30
  import { TronLinkAdapterName } from "@tronweb3/tronwallet-adapters";
30
31
  const norm = (s) => (s ?? "").toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
31
32
  const POPULAR_ORDER = [
@@ -509,7 +510,7 @@ function cn(...inputs) {
509
510
  return twMerge(clsx(inputs));
510
511
  }
511
512
  const buttonVariants = cva(
512
- "inline-flex items-center rounded-full justify-center gap-2 whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
513
+ "inline-flex items-center rounded-full text-lg justify-center gap-2 whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
513
514
  {
514
515
  variants: {
515
516
  variant: {
@@ -782,19 +783,6 @@ const Tip = (props) => {
782
783
  /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: text }) })
783
784
  ] });
784
785
  };
785
- function toLD(human, decimals) {
786
- const [i = "0", f = ""] = human.replace(",", ".").split(".");
787
- const frac = (f + "0".repeat(decimals)).slice(0, decimals);
788
- return BigInt(i + frac).toString();
789
- }
790
- function fromLD(ld, decimals) {
791
- const bi = BigInt(ld || "0");
792
- if (decimals === 0) return Number(bi);
793
- const base = BigInt(10) ** BigInt(decimals);
794
- const int = bi / base;
795
- const frac = Number(bi % base) / Number(base);
796
- return Number(int) + frac;
797
- }
798
786
  async function getChains() {
799
787
  const res = await fetch("https://stargate.finance/api/v1/chains", {
800
788
  credentials: "same-origin"
@@ -842,49 +830,16 @@ async function getDestTokens(srcChainKey, srcTokenAddr) {
842
830
  });
843
831
  return unique;
844
832
  }
845
- const isTonFriendly = (a) => !!a && /^[A-Za-z0-9_-]{48,}$/.test(a);
846
- function normalizeTickerSymbol(s) {
833
+ function normalizeTickerSymbol$1(s) {
847
834
  return s.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
848
835
  }
849
- async function getSwapBalances(accountFriendly) {
850
- if (!isTonFriendly(accountFriendly)) throw new Error("Invalid TON address");
851
- const accRes = await fetch(
852
- `https://tonapi.io/v2/accounts/${accountFriendly}`
853
- );
854
- if (!accRes.ok) throw new Error(`TON account fetch failed: ${accRes.status}`);
855
- const acc = await accRes.json();
856
- const ton = fromLD(String(acc?.balance ?? "0"), 9);
857
- const result = {
858
- TON: { balance: ton, address: "ton-native" }
859
- };
860
- const jetsRes = await fetch(
861
- `https://tonapi.io/v2/accounts/${accountFriendly}/jettons?limit=200`
862
- );
863
- if (!jetsRes.ok) return result;
864
- const jets = await jetsRes.json();
865
- const items = jets?.balances ?? jets?.jettons ?? jets?.items ?? [];
866
- for (const it of items) {
867
- const rawSym = (it?.jetton?.symbol ?? it?.symbol ?? "").toString();
868
- if (!rawSym) continue;
869
- const symUpper = rawSym.toUpperCase();
870
- const symNorm = normalizeTickerSymbol(symUpper);
871
- const master = (it?.jetton?.address ?? it?.address ?? "").toString();
872
- const decimals = Number(it?.jetton?.decimals ?? it?.decimals ?? 9) || 9;
873
- const raw = (it?.balance ?? "0").toString();
874
- const human = fromLD(raw, decimals);
875
- const entry = { balance: human, address: master };
876
- result[symUpper] = entry;
877
- if (symNorm !== symUpper) result[symNorm] = entry;
878
- }
879
- return result;
880
- }
881
836
  const BASE_URL$1 = "https://icons-ckg.pages.dev/stargate-light/tokens";
882
837
  const TokenSymbol = ({
883
838
  symbol,
884
839
  className = "w-4 h-4",
885
840
  alt
886
841
  }) => {
887
- const normalizedSymbol = normalizeTickerSymbol(symbol).toLowerCase();
842
+ const normalizedSymbol = normalizeTickerSymbol$1(symbol).toLowerCase();
888
843
  const src = `${BASE_URL$1}/${normalizedSymbol}.svg`;
889
844
  return /* @__PURE__ */ jsx("img", { src, alt: alt ?? symbol, className });
890
845
  };
@@ -1298,6 +1253,19 @@ function useChainStrategies() {
1298
1253
  }
1299
1254
  return context;
1300
1255
  }
1256
+ function toLD(human, decimals) {
1257
+ const [i = "0", f = ""] = human.replace(",", ".").split(".");
1258
+ const frac = (f + "0".repeat(decimals)).slice(0, decimals);
1259
+ return BigInt(i + frac).toString();
1260
+ }
1261
+ function fromLD(ld, decimals) {
1262
+ const bi = BigInt(ld || "0");
1263
+ if (decimals === 0) return Number(bi);
1264
+ const base = BigInt(10) ** BigInt(decimals);
1265
+ const int = bi / base;
1266
+ const frac = Number(bi % base) / Number(base);
1267
+ return Number(int) + frac;
1268
+ }
1301
1269
  function resolveTokenOnChainFromMatrix$2(assetMatrix, assetSymbol, chainKey) {
1302
1270
  if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
1303
1271
  const byChain = assetMatrix[assetSymbol.toUpperCase()];
@@ -1479,20 +1447,17 @@ function useBalances(chainKey, address) {
1479
1447
  const data = query.data;
1480
1448
  if (data) {
1481
1449
  for (const [sum, v] of Object.entries(data)) {
1482
- map.set(normalizeTickerSymbol(sum), Number(v.balance ?? 0));
1450
+ map.set(normalizeTickerSymbol$1(sum), Number(v.balance ?? 0));
1483
1451
  }
1484
1452
  }
1485
1453
  return map;
1486
1454
  }, [query.data]);
1487
1455
  const getBalance = useCallback(
1488
- (symbol) => balanceBySymbol.get(normalizeTickerSymbol(symbol)) ?? 0,
1456
+ (symbol) => balanceBySymbol.get(normalizeTickerSymbol$1(symbol)) ?? 0,
1489
1457
  [balanceBySymbol]
1490
1458
  );
1491
1459
  const isLoading = query.isLoading || query.isFetching;
1492
- const balances = useMemo(
1493
- () => query.data || {},
1494
- [query.data]
1495
- );
1460
+ const balances = useMemo(() => query.data || {}, [query.data]);
1496
1461
  return {
1497
1462
  balances,
1498
1463
  getBalance,
@@ -1948,17 +1913,17 @@ const RefreshButton = () => {
1948
1913
  return /* @__PURE__ */ jsx(
1949
1914
  Button,
1950
1915
  {
1951
- className: `cursor-pointer py-1.5 h-8.5 hover:scale-110 shadow-none px-8.5 hover:bg-secondary bg-secondary !rounded-40 ${spinning ? "opacity-70" : ""}`,
1952
1916
  onClick: handleRefresh,
1953
1917
  disabled: spinning,
1918
+ variant: "secondary",
1919
+ size: "sm",
1954
1920
  children: /* @__PURE__ */ jsx(
1955
1921
  ReloadIcon,
1956
1922
  {
1957
1923
  style: {
1958
1924
  transform: `rotate(${turns * 180}deg)`,
1959
1925
  transition: "transform 300ms linear"
1960
- },
1961
- className: "size-4 text-foreground m-1 will-change-transform"
1926
+ }
1962
1927
  }
1963
1928
  )
1964
1929
  }
@@ -1966,7 +1931,6 @@ const RefreshButton = () => {
1966
1931
  };
1967
1932
  const SelectTokenButton = ({
1968
1933
  onClick,
1969
- className,
1970
1934
  token
1971
1935
  }) => {
1972
1936
  const { t: t2 } = useTranslation();
@@ -1977,7 +1941,8 @@ const SelectTokenButton = ({
1977
1941
  Button,
1978
1942
  {
1979
1943
  onClick,
1980
- className: `cursor-pointer hover:scale-[1.1] p-1.5 h-8.5 pr-3 !pl-1.5 bg-secondary hover:bg-secondary shadow-none rounded-full flex items-center justify-between gap-3 w-fit shrink-0 ${className ?? ""}`,
1944
+ size: "sm",
1945
+ variant: "secondary",
1981
1946
  type: "button",
1982
1947
  "aria-label": label,
1983
1948
  children: [
@@ -1997,6 +1962,75 @@ const SelectTokenButton = ({
1997
1962
  }
1998
1963
  );
1999
1964
  };
1965
+ function Card({ className, ...props }) {
1966
+ return /* @__PURE__ */ jsx(
1967
+ "div",
1968
+ {
1969
+ "data-slot": "card",
1970
+ className: cn(
1971
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
1972
+ className
1973
+ ),
1974
+ ...props
1975
+ }
1976
+ );
1977
+ }
1978
+ function CardHeader({ className, ...props }) {
1979
+ return /* @__PURE__ */ jsx(
1980
+ "div",
1981
+ {
1982
+ "data-slot": "card-header",
1983
+ className: cn(
1984
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
1985
+ className
1986
+ ),
1987
+ ...props
1988
+ }
1989
+ );
1990
+ }
1991
+ function CardTitle({ className, ...props }) {
1992
+ return /* @__PURE__ */ jsx(
1993
+ "div",
1994
+ {
1995
+ "data-slot": "card-title",
1996
+ className: cn("leading-none font-semibold", className),
1997
+ ...props
1998
+ }
1999
+ );
2000
+ }
2001
+ function CardAction({ className, ...props }) {
2002
+ return /* @__PURE__ */ jsx(
2003
+ "div",
2004
+ {
2005
+ "data-slot": "card-action",
2006
+ className: cn(
2007
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
2008
+ className
2009
+ ),
2010
+ ...props
2011
+ }
2012
+ );
2013
+ }
2014
+ function CardContent({ className, ...props }) {
2015
+ return /* @__PURE__ */ jsx(
2016
+ "div",
2017
+ {
2018
+ "data-slot": "card-content",
2019
+ className: cn("px-6", className),
2020
+ ...props
2021
+ }
2022
+ );
2023
+ }
2024
+ function CardFooter({ className, ...props }) {
2025
+ return /* @__PURE__ */ jsx(
2026
+ "div",
2027
+ {
2028
+ "data-slot": "card-footer",
2029
+ className: cn("flex items-center px-6 [.border-t]:pt-6", className),
2030
+ ...props
2031
+ }
2032
+ );
2033
+ }
2000
2034
  const FormHeaderComponent = ({ modalContainer }) => {
2001
2035
  const { t: t2 } = useTranslation();
2002
2036
  const { isOpen, onClose, onOpen } = useModal();
@@ -2013,29 +2047,14 @@ const FormHeaderComponent = ({ modalContainer }) => {
2013
2047
  const sum = selectedAssetSymbol.toUpperCase();
2014
2048
  return assets.find((a) => a.symbol.toUpperCase() === sum) ?? assets[0];
2015
2049
  }, [assets, selectedAssetSymbol]);
2016
- return /* @__PURE__ */ jsxs(Fragment, { children: [
2017
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2018
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
2019
- /* @__PURE__ */ jsx("span", { className: "text-sm font-normal leading-3.5 text-muted-foreground", children: t2("bridge.selectToken") }),
2020
- /* @__PURE__ */ jsx(SelectTokenButton, { token: current, onClick: onOpen })
2021
- ] }),
2022
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
2023
- /* @__PURE__ */ jsx(RefreshButton, {}),
2024
- /* @__PURE__ */ jsx(
2025
- Button,
2026
- {
2027
- className: "cursor-pointer py-1.5 h-8.5 hover:scale-110 shadow-none px-8.5 hover:bg-secondary bg-secondary !rounded-40",
2028
- onClick: onOpenSettings,
2029
- children: /* @__PURE__ */ jsx(
2030
- BoltIcon,
2031
- {
2032
- className: "size-4 text-foreground m-1",
2033
- stroke: "currentColor"
2034
- }
2035
- )
2036
- }
2037
- )
2038
- ] })
2050
+ return /* @__PURE__ */ jsxs(CardHeader, { className: "gap-y-0", children: [
2051
+ /* @__PURE__ */ jsxs(CardTitle, { className: "flex items-center gap-2.5", children: [
2052
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-normal leading-3.5 text-muted-foreground", children: t2("bridge.selectToken") }),
2053
+ /* @__PURE__ */ jsx(SelectTokenButton, { token: current, onClick: onOpen })
2054
+ ] }),
2055
+ /* @__PURE__ */ jsxs(CardAction, { className: "flex items-center gap-2.5", children: [
2056
+ /* @__PURE__ */ jsx(RefreshButton, {}),
2057
+ /* @__PURE__ */ jsx(Button, { onClick: onOpenSettings, size: "sm", variant: "secondary", children: /* @__PURE__ */ jsx(BoltIcon, { stroke: "currentColor" }) })
2039
2058
  ] }),
2040
2059
  /* @__PURE__ */ jsx(
2041
2060
  TokenSelectModal,
@@ -3921,7 +3940,7 @@ function useSilentValidations(amountString) {
3921
3940
  ]);
3922
3941
  return validationResult;
3923
3942
  }
3924
- function useTransferAction() {
3943
+ const SubmitButton = () => {
3925
3944
  const { t: t2 } = useTranslation();
3926
3945
  const { chainRegistry } = useChainStrategies();
3927
3946
  const { srcAddress, dstAddress } = useAddresses();
@@ -3997,7 +4016,7 @@ function useTransferAction() {
3997
4016
  maximumAmountFormatted,
3998
4017
  chainRegistry
3999
4018
  ]);
4000
- const onClick = async () => {
4019
+ const handleClick = async () => {
4001
4020
  if (isBusy) return;
4002
4021
  if (missingSrc && srcChainKey) {
4003
4022
  onOpen("src");
@@ -4017,24 +4036,7 @@ function useTransferAction() {
4017
4036
  }
4018
4037
  };
4019
4038
  const disabled = isBusy || amountNum <= 0 || status === "loading" || isBalanceLoading || hasInsufficientBalance || hasAmountTooLarge || !gas.hasEnoughGas || noRoute || !isValidForTransfer;
4020
- return {
4021
- // состояния
4022
- isBusy,
4023
- canTransfer,
4024
- missingSrc,
4025
- missingDst,
4026
- hasEnoughGas: gas.hasEnoughGas,
4027
- noRoute,
4028
- // представление
4029
- label,
4030
- disabled,
4031
- // действие
4032
- onClick
4033
- };
4034
- }
4035
- const SubmitButton = () => {
4036
- const { label, disabled, onClick } = useTransferAction();
4037
- return /* @__PURE__ */ jsx(Button, { onClick, disabled, size: "lg", children: label });
4039
+ return /* @__PURE__ */ jsx(Button, { onClick: handleClick, disabled, className: "w-full", children: label });
4038
4040
  };
4039
4041
  function short(addr) {
4040
4042
  return addr.slice(0, 4) + "…" + addr.slice(-4);
@@ -4044,6 +4046,7 @@ const WalletSelectModal = ({
4044
4046
  }) => {
4045
4047
  const { t: t2 } = useTranslation();
4046
4048
  const { isOpen, onClose } = useWalletSelectModal();
4049
+ const { connect, connectors, isPending } = useConnect();
4047
4050
  const { chainRegistry } = useChainStrategies();
4048
4051
  const tonWallet = chainRegistry.getStrategyByType("ton");
4049
4052
  const metaMaskWallet = chainRegistry.getStrategyByType("evm");
@@ -4076,7 +4079,13 @@ const WalletSelectModal = ({
4076
4079
  onDisconnect: () => tronWallet.disconnect()
4077
4080
  });
4078
4081
  }
4079
- const isWalletConnected = (walletId) => connectedWallets.some((w) => w.id === walletId);
4082
+ const isWalletConnected = (walletId) => {
4083
+ const isEvmConnector = connectors.some((c) => c.id === walletId);
4084
+ if (isEvmConnector && metaMaskWallet?.isConnected()) {
4085
+ return true;
4086
+ }
4087
+ return connectedWallets.some((w) => w.id === walletId);
4088
+ };
4080
4089
  const tonWallets = [
4081
4090
  {
4082
4091
  id: "ton",
@@ -4085,14 +4094,15 @@ const WalletSelectModal = ({
4085
4094
  enabled: true
4086
4095
  }
4087
4096
  ];
4088
- const evmWallets = [
4089
- {
4090
- id: "metamask",
4091
- name: t2("wallets.metaMask"),
4092
- icon: MetaMaskIcon,
4093
- enabled: true
4094
- }
4095
- ];
4097
+ const evmWallets = connectors.filter(
4098
+ (connector) => connector.id === "walletConnect" || connector.id === "metaMaskSDK"
4099
+ ).map((connector) => ({
4100
+ id: connector.id,
4101
+ name: connector.name,
4102
+ icon: MetaMaskIcon,
4103
+ // You can add a WalletConnect icon here
4104
+ enabled: true
4105
+ }));
4096
4106
  const tronWallets = [
4097
4107
  {
4098
4108
  id: "tronlink",
@@ -4122,9 +4132,6 @@ const WalletSelectModal = ({
4122
4132
  case "ton":
4123
4133
  await tonWallet?.connect();
4124
4134
  break;
4125
- case "metamask":
4126
- await metaMaskWallet?.connect();
4127
- break;
4128
4135
  case "tronlink":
4129
4136
  await tronWallet?.connect();
4130
4137
  break;
@@ -4149,56 +4156,47 @@ const WalletSelectModal = ({
4149
4156
  /* @__PURE__ */ jsx("div", { className: "px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase", children: t2("wallets.connected") }),
4150
4157
  /* @__PURE__ */ jsx("div", { className: "", children: connectedWallets.map((wallet) => {
4151
4158
  const IconComponent = wallet.icon;
4152
- return /* @__PURE__ */ jsx("div", { className: "", children: /* @__PURE__ */ jsxs(
4153
- Button,
4154
- {
4155
- className: "w-full cursor-pointer bg-transparent flex shadow-none items-center justify-between gap-2.5 px-5 py-2.5 hover:bg-muted h-auto rounded-12 transition-[300]",
4156
- children: [
4157
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
4158
- /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
4159
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
4160
- /* @__PURE__ */ jsx("p", { className: "font-extrabold text-foreground text-sm leading-4 truncate", children: short(wallet.address) }),
4161
- /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: wallet.name })
4162
- ] })
4163
- ] }) }),
4164
- /* @__PURE__ */ jsx("div", { onClick: () => {
4159
+ return /* @__PURE__ */ jsx("div", { className: "", children: /* @__PURE__ */ jsxs(Button, { className: "w-full cursor-pointer bg-transparent flex shadow-none items-center justify-between gap-2.5 px-5 py-2.5 hover:bg-muted h-auto rounded-12 transition-[300]", children: [
4160
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5", children: [
4161
+ /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
4162
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
4163
+ /* @__PURE__ */ jsx("p", { className: "font-extrabold text-foreground text-sm leading-4 truncate", children: short(wallet.address) }),
4164
+ /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: wallet.name })
4165
+ ] })
4166
+ ] }) }),
4167
+ /* @__PURE__ */ jsx(
4168
+ "div",
4169
+ {
4170
+ onClick: () => {
4165
4171
  wallet.onDisconnect();
4166
4172
  onClose();
4167
- }, className: "text-sm font-medium text-muted-foreground", children: /* @__PURE__ */ jsx(ExitIcon, { className: "text-[#808080] size-6" }) })
4168
- ]
4169
- }
4170
- ) }, wallet.id);
4173
+ },
4174
+ className: "text-sm font-medium text-muted-foreground",
4175
+ children: /* @__PURE__ */ jsx(ExitIcon, { className: "text-[#808080] size-6" })
4176
+ }
4177
+ )
4178
+ ] }) }, wallet.id);
4171
4179
  }) })
4172
4180
  ] }),
4173
4181
  categories.map((category) => /* @__PURE__ */ jsxs("div", { children: [
4174
4182
  /* @__PURE__ */ jsx("div", { className: "px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase", children: category.title }),
4175
4183
  /* @__PURE__ */ jsx("div", { className: "", children: category.wallets.map((wallet) => {
4176
4184
  const IconComponent = wallet.icon;
4177
- if (wallet.id === "metamask") {
4178
- return /* @__PURE__ */ jsx("div", { id: wallet.id, children: /* @__PURE__ */ jsx(ConnectKitButton.Custom, { children: ({ isConnecting, show }) => {
4179
- return /* @__PURE__ */ jsx(
4180
- Button,
4181
- {
4182
- type: "button",
4183
- onClick: show,
4184
- disabled: isConnecting,
4185
- className: "w-full cursor-pointer bg-transparent flex shadow-none items-center justify-between gap-2.5 px-5 py-2.5 hover:bg-muted h-auto rounded-12 transition-[300] disabled:opacity-50 disabled:cursor-not-allowed",
4186
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
4187
- /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
4188
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
4189
- /* @__PURE__ */ jsx("p", { className: "font-extrabold text-foreground text-sm leading-4 truncate", children: wallet.name }),
4190
- wallet.comingSoon ? /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: t2("wallets.comingSoon") }) : /* @__PURE__ */ jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: t2("wallets.connect") })
4191
- ] })
4192
- ] })
4193
- }
4194
- );
4195
- } }) }, wallet.id);
4196
- }
4185
+ const isEvmConnector = category.title === t2("wallets.evmWallets");
4186
+ const connector = isEvmConnector ? connectors.find((c) => c.id === wallet.id) : null;
4197
4187
  return /* @__PURE__ */ jsx("div", { className: "", children: /* @__PURE__ */ jsx(
4198
4188
  Button,
4199
4189
  {
4200
- onClick: () => handleWalletSelect(wallet.id),
4201
- disabled: !wallet.enabled,
4190
+ type: "button",
4191
+ onClick: () => {
4192
+ if (connector) {
4193
+ connect({ connector });
4194
+ onClose();
4195
+ } else {
4196
+ handleWalletSelect(wallet.id);
4197
+ }
4198
+ },
4199
+ disabled: isEvmConnector ? isPending : !wallet.enabled,
4202
4200
  className: "w-full cursor-pointer bg-transparent flex shadow-none items-center justify-between gap-2.5 px-5 py-2.5 hover:bg-muted h-auto rounded-12 transition-[300] disabled:opacity-50 disabled:cursor-not-allowed",
4203
4201
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
4204
4202
  /* @__PURE__ */ jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsx(IconComponent, { className: "size-7.5" }) }),
@@ -4214,39 +4212,6 @@ const WalletSelectModal = ({
4214
4212
  ] })
4215
4213
  ] }) });
4216
4214
  };
4217
- function Card({ className, ...props }) {
4218
- return /* @__PURE__ */ jsx(
4219
- "div",
4220
- {
4221
- "data-slot": "card",
4222
- className: cn(
4223
- "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
4224
- className
4225
- ),
4226
- ...props
4227
- }
4228
- );
4229
- }
4230
- function CardContent({ className, ...props }) {
4231
- return /* @__PURE__ */ jsx(
4232
- "div",
4233
- {
4234
- "data-slot": "card-content",
4235
- className: cn("px-6", className),
4236
- ...props
4237
- }
4238
- );
4239
- }
4240
- function CardFooter({ className, ...props }) {
4241
- return /* @__PURE__ */ jsx(
4242
- "div",
4243
- {
4244
- "data-slot": "card-footer",
4245
- className: cn("flex items-center px-6 [.border-t]:pt-6", className),
4246
- ...props
4247
- }
4248
- );
4249
- }
4250
4215
  const TransactionProgressVector = (props) => {
4251
4216
  return /* @__PURE__ */ jsxs("svg", { width: "200", height: "200", viewBox: "0 0 200 200", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props, children: [
4252
4217
  /* @__PURE__ */ jsx(
@@ -6310,6 +6275,49 @@ class ChainStrategyRegistry {
6310
6275
  await strategy.disconnect();
6311
6276
  }
6312
6277
  }
6278
+ const EVM_CONFIG = {
6279
+ usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
6280
+ gasEstimates: {
6281
+ approve: 65000n,
6282
+ bridge: 300000n
6283
+ },
6284
+ gasBuffer: 1.2,
6285
+ // 20% buffer
6286
+ timeout: 3e5,
6287
+ // 5 minutes (increased for slower networks)
6288
+ requiredConfirmations: 3
6289
+ // Wait for 3 confirmations for reorg protection
6290
+ };
6291
+ const TON_CONFIG = {
6292
+ apiUrl: "https://toncenter.com/api/v2",
6293
+ timeout: 36e4,
6294
+ // 6 minutes
6295
+ validUntil: 600
6296
+ // 10 minutes
6297
+ };
6298
+ const TRON_CONFIG = {
6299
+ timeout: 12e4,
6300
+ // 2 minutes (for 19 confirmations)
6301
+ feeLimit: 1e8,
6302
+ // 100 TRX in sun
6303
+ requiredConfirmations: 19,
6304
+ // TRON standard: 19 blocks for confirmation
6305
+ pollingInterval: 3e3
6306
+ // 3 seconds between checks
6307
+ };
6308
+ let tonClientInstance = null;
6309
+ function getTonClient(customClient, apiKey) {
6310
+ if (customClient) {
6311
+ return customClient;
6312
+ }
6313
+ if (!tonClientInstance) {
6314
+ tonClientInstance = new TonClient({
6315
+ endpoint: `${TON_CONFIG.apiUrl}/jsonRPC`,
6316
+ apiKey
6317
+ });
6318
+ }
6319
+ return tonClientInstance;
6320
+ }
6313
6321
  function isNativeAddress(addr) {
6314
6322
  if (!addr) return false;
6315
6323
  const a = addr.toLowerCase();
@@ -6335,44 +6343,72 @@ function formatUnitsFromBigIntStr(valueStr, decimals) {
6335
6343
  const tail = s.slice(s.length - decimals).replace(/0+$/, "");
6336
6344
  return Number(tail ? `${head}.${tail}` : head);
6337
6345
  }
6338
- async function getEvmBalances(address, tokens) {
6346
+ function isTonFriendlyAddress(address) {
6347
+ return !!address && /^[A-Za-z0-9_-]{48,}$/.test(address);
6348
+ }
6349
+ function normalizeTickerSymbol(symbol) {
6350
+ return symbol.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
6351
+ }
6352
+ function parseTonAddress(address) {
6353
+ if (address.startsWith("0x")) {
6354
+ const hex = address.slice(2);
6355
+ return Address$1.parseRaw(`0:${hex}`);
6356
+ }
6357
+ if (address.includes(":")) {
6358
+ return Address$1.parseRaw(address);
6359
+ }
6360
+ return Address$1.parse(address);
6361
+ }
6362
+ async function getEvmBalances(publicClient, address, tokens) {
6339
6363
  const balances = {};
6340
6364
  try {
6341
- if (!address || !ethers.isAddress(address)) {
6365
+ console.log("start getEvmBalances");
6366
+ console.log("publicClient:", publicClient);
6367
+ console.log("isAddress:", isAddress(address));
6368
+ console.log("tokens:", tokens);
6369
+ if (!address || !isAddress(address)) {
6342
6370
  console.warn(`Invalid EVM address provided: ${address}`);
6343
6371
  return balances;
6344
6372
  }
6345
- if (typeof window === "undefined" || !window.ethereum) {
6346
- throw new Error("No ethereum provider found");
6373
+ if (!publicClient) {
6374
+ throw new Error("No public client provided");
6347
6375
  }
6348
- const provider = new ethers.BrowserProvider(window.ethereum);
6349
6376
  for (const token of tokens) {
6350
6377
  try {
6351
6378
  let balance = 0;
6352
6379
  const isNative = isNativeAddress(token.address);
6353
6380
  if (isNative) {
6354
- const ethBalance = await provider.getBalance(address);
6355
- balance = parseFloat(ethers.formatUnits(ethBalance, token.decimals));
6381
+ const ethBalance = await publicClient.getBalance({
6382
+ address
6383
+ });
6384
+ balance = parseFloat(formatUnits(ethBalance, token.decimals));
6356
6385
  } else {
6357
- if (!ethers.isAddress(token.address)) {
6386
+ if (!isAddress(token.address)) {
6358
6387
  continue;
6359
6388
  }
6360
- const code = await provider.getCode(token.address);
6361
- if (code === "0x") {
6389
+ const bytecode = await publicClient.getBytecode({
6390
+ address: token.address
6391
+ });
6392
+ if (!bytecode || bytecode === "0x") {
6362
6393
  continue;
6363
6394
  }
6364
- const contract = new ethers.Contract(
6365
- token.address,
6366
- [
6367
- "function balanceOf(address owner) view returns (uint256)",
6368
- "function decimals() view returns (uint8)"
6369
- ],
6370
- provider
6371
- );
6372
6395
  try {
6373
- const tokenBalance = await contract.balanceOf(address);
6396
+ const tokenBalance = await publicClient.readContract({
6397
+ address: token.address,
6398
+ abi: [
6399
+ {
6400
+ name: "balanceOf",
6401
+ type: "function",
6402
+ stateMutability: "view",
6403
+ inputs: [{ name: "owner", type: "address" }],
6404
+ outputs: [{ name: "balance", type: "uint256" }]
6405
+ }
6406
+ ],
6407
+ functionName: "balanceOf",
6408
+ args: [address]
6409
+ });
6374
6410
  balance = parseFloat(
6375
- ethers.formatUnits(tokenBalance, token.decimals)
6411
+ formatUnits(tokenBalance, token.decimals)
6376
6412
  );
6377
6413
  } catch {
6378
6414
  continue;
@@ -6395,6 +6431,68 @@ async function getEvmBalances(address, tokens) {
6395
6431
  }
6396
6432
  return balances;
6397
6433
  }
6434
+ async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
6435
+ const balances = {};
6436
+ try {
6437
+ if (!isTonFriendlyAddress(address)) {
6438
+ console.warn(`Invalid TON address provided: ${address}`);
6439
+ return balances;
6440
+ }
6441
+ const client = getTonClient(customTonClient, tonApiKey);
6442
+ const accountAddress = Address$1.parse(address);
6443
+ console.log(address);
6444
+ console.log(tokens);
6445
+ try {
6446
+ const balance = await client.getBalance(accountAddress);
6447
+ const tonBalance = Number(balance) / 1e9;
6448
+ console.log("tonBalance", tonBalance);
6449
+ if (tonBalance > 0) {
6450
+ balances.TON = { balance: tonBalance, address: "ton-native" };
6451
+ }
6452
+ } catch (error) {
6453
+ console.warn("Failed to get native TON balance:", error);
6454
+ }
6455
+ for (const token of tokens) {
6456
+ try {
6457
+ if (isNativeAddress(token.address) || token.symbol.toUpperCase() === "TON") {
6458
+ continue;
6459
+ }
6460
+ const jettonMasterAddress = parseTonAddress(token.address);
6461
+ const jettonWalletAddress = await client.runMethod(
6462
+ jettonMasterAddress,
6463
+ "get_wallet_address",
6464
+ [
6465
+ {
6466
+ type: "slice",
6467
+ cell: beginCell().storeAddress(accountAddress).endCell()
6468
+ }
6469
+ ]
6470
+ );
6471
+ const jettonWalletAddr = jettonWalletAddress.stack.readAddress();
6472
+ const jettonData = await client.runMethod(
6473
+ jettonWalletAddr,
6474
+ "get_wallet_data"
6475
+ );
6476
+ const jettonBalance = jettonData.stack.readBigNumber();
6477
+ const humanBalance = Number(jettonBalance) / Math.pow(10, token.decimals);
6478
+ if (humanBalance > 0) {
6479
+ const symbolUpper = token.symbol.toUpperCase();
6480
+ const symbolNorm = normalizeTickerSymbol(symbolUpper);
6481
+ const entry = { balance: humanBalance, address: token.address };
6482
+ balances[symbolUpper] = entry;
6483
+ if (symbolNorm !== symbolUpper) {
6484
+ balances[symbolNorm] = entry;
6485
+ }
6486
+ }
6487
+ } catch (error) {
6488
+ console.debug(`Failed to get balance for ${token.symbol}:`, error);
6489
+ }
6490
+ }
6491
+ } catch (error) {
6492
+ console.error("Failed to get TON balances:", error);
6493
+ }
6494
+ return balances;
6495
+ }
6398
6496
  async function getTronBalances(tronWeb, address, tokens) {
6399
6497
  const balances = {};
6400
6498
  try {
@@ -6438,30 +6536,6 @@ async function getTronBalances(tronWeb, address, tokens) {
6438
6536
  }
6439
6537
  return balances;
6440
6538
  }
6441
- const EVM_CONFIG = {
6442
- usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
6443
- gasEstimates: {
6444
- approve: 65000n,
6445
- bridge: 300000n
6446
- },
6447
- gasBuffer: 1.2,
6448
- // 20% buffer
6449
- timeout: 12e4
6450
- // 2 minutes
6451
- };
6452
- const TON_CONFIG = {
6453
- apiUrl: "https://toncenter.com/api/v2",
6454
- timeout: 36e4,
6455
- // 6 minutes
6456
- validUntil: 600
6457
- // 10 minutes
6458
- };
6459
- const TRON_CONFIG = {
6460
- timeout: 6e4,
6461
- // 1 minute
6462
- feeLimit: 1e8
6463
- // 100 TRX in sun
6464
- };
6465
6539
  const ERC20_ABI = [
6466
6540
  "function approve(address spender, uint256 amount) returns (bool)",
6467
6541
  "function allowance(address owner, address spender) view returns (uint256)",
@@ -6472,7 +6546,9 @@ class EvmChainStrategy {
6472
6546
  constructor(config) {
6473
6547
  __publicField(this, "config");
6474
6548
  __publicField(this, "provider");
6549
+ __publicField(this, "publicClient");
6475
6550
  this.config = config;
6551
+ this.publicClient = config.publicClient;
6476
6552
  if (config.walletClient) {
6477
6553
  this.provider = new BrowserProvider(config.walletClient.transport);
6478
6554
  }
@@ -6511,7 +6587,11 @@ class EvmChainStrategy {
6511
6587
  return t2("wallets.connectEvmWallet");
6512
6588
  }
6513
6589
  async getBalances(address, tokens) {
6514
- return await getEvmBalances(address, tokens);
6590
+ if (!this.publicClient) {
6591
+ console.warn("No publicClient available for balance query");
6592
+ return {};
6593
+ }
6594
+ return await getEvmBalances(this.publicClient, address, tokens);
6515
6595
  }
6516
6596
  isAddressValid(address) {
6517
6597
  if (!address) return false;
@@ -6540,8 +6620,8 @@ class EvmChainStrategy {
6540
6620
  const totalGas = approveGas + bridgeGas;
6541
6621
  const bufferMultiplier = BigInt(Math.floor(EVM_CONFIG.gasBuffer * 100));
6542
6622
  const requiredWei = gasPrice * totalGas * bufferMultiplier / 100n;
6543
- const { formatUnits } = await import("ethers");
6544
- estimatedGas = Number(formatUnits(requiredWei, nativeDecimals));
6623
+ const { formatUnits: formatUnits2 } = await import("ethers");
6624
+ estimatedGas = Number(formatUnits2(requiredWei, nativeDecimals));
6545
6625
  } catch {
6546
6626
  estimatedGas = null;
6547
6627
  }
@@ -6624,17 +6704,14 @@ class EvmChainStrategy {
6624
6704
  if (!provider) {
6625
6705
  throw new ProviderNotAvailableError("evm");
6626
6706
  }
6627
- const deadline = Date.now() + EVM_CONFIG.timeout;
6628
- let receipt = null;
6629
- while (Date.now() < deadline) {
6630
- try {
6631
- receipt = await provider.getTransactionReceipt(txHash);
6632
- if (receipt) break;
6633
- } catch (e) {
6634
- console.debug("Error fetching receipt:", e);
6635
- }
6636
- await new Promise((r) => setTimeout(r, 2500));
6637
- }
6707
+ console.log(
6708
+ `Waiting for ${EVM_CONFIG.requiredConfirmations} confirmations for tx: ${txHash}`
6709
+ );
6710
+ const receipt = await provider.waitForTransaction(
6711
+ txHash,
6712
+ EVM_CONFIG.requiredConfirmations,
6713
+ EVM_CONFIG.timeout
6714
+ );
6638
6715
  if (!receipt) {
6639
6716
  const error = new TransactionTimeoutError("evm", txHash);
6640
6717
  return {
@@ -6649,11 +6726,35 @@ class EvmChainStrategy {
6649
6726
  error: error.message
6650
6727
  };
6651
6728
  }
6652
- console.log("EVM transaction confirmed on-chain");
6729
+ console.log(
6730
+ `EVM transaction confirmed in block ${receipt.blockNumber} with ${EVM_CONFIG.requiredConfirmations} confirmations`
6731
+ );
6653
6732
  return {
6654
6733
  completed: true
6655
6734
  };
6656
6735
  } catch (error) {
6736
+ if (error && typeof error === "object" && "code" in error && error.code === "TRANSACTION_REPLACED") {
6737
+ console.log(
6738
+ `Transaction was replaced: ${"reason" in error ? String(error.reason) : "unknown"}`
6739
+ );
6740
+ if ("receipt" in error && error.receipt) {
6741
+ const replacementReceipt = error.receipt;
6742
+ if (replacementReceipt.status === 1) {
6743
+ console.log(
6744
+ `Replacement transaction succeeded in block ${replacementReceipt.blockNumber}`
6745
+ );
6746
+ return {
6747
+ completed: true
6748
+ };
6749
+ } else {
6750
+ const chainError2 = new TransactionRevertedError("evm", txHash);
6751
+ return {
6752
+ completed: false,
6753
+ error: chainError2.message
6754
+ };
6755
+ }
6756
+ }
6757
+ }
6657
6758
  const chainError = toChainStrategyError(
6658
6759
  error,
6659
6760
  "evm",
@@ -6767,13 +6868,52 @@ class EvmChainStrategy {
6767
6868
  return false;
6768
6869
  }
6769
6870
  }
6871
+ /**
6872
+ * Check if a block has reached finality status (optional, for critical transactions)
6873
+ * This is more stringent than confirmations and protects against long-range reorgs
6874
+ *
6875
+ * Usage: Call this method after waitForCompletion if you need additional security
6876
+ * for high-value transactions. The method checks if the block has been marked as
6877
+ * "finalized" by the Ethereum consensus layer (2/3 of validators).
6878
+ *
6879
+ * @param blockNumber - The block number to check for finality
6880
+ * @returns true if the block is finalized, false otherwise
6881
+ */
6882
+ async checkFinality(blockNumber) {
6883
+ try {
6884
+ const provider = this.provider;
6885
+ if (!provider) {
6886
+ return false;
6887
+ }
6888
+ const finalizedBlock = await provider.getBlock("finalized");
6889
+ if (!finalizedBlock) {
6890
+ console.debug(
6891
+ "Finalized block not available (pre-merge or unsupported)"
6892
+ );
6893
+ return false;
6894
+ }
6895
+ const isFinalized = blockNumber <= finalizedBlock.number;
6896
+ if (isFinalized) {
6897
+ console.log(
6898
+ `Block ${blockNumber} has reached finality (finalized block: ${finalizedBlock.number})`
6899
+ );
6900
+ } else {
6901
+ console.debug(
6902
+ `Block ${blockNumber} not yet finalized (finalized block: ${finalizedBlock.number})`
6903
+ );
6904
+ }
6905
+ return isFinalized;
6906
+ } catch (error) {
6907
+ console.debug("Error checking finality:", error);
6908
+ return false;
6909
+ }
6910
+ }
6770
6911
  }
6771
6912
  class TonChainStrategy {
6772
6913
  constructor(config) {
6773
6914
  __publicField(this, "config");
6774
6915
  this.config = config;
6775
6916
  }
6776
- // ========== Identity ==========
6777
6917
  canHandle(chainKey) {
6778
6918
  return chainKey.toLowerCase() === "ton";
6779
6919
  }
@@ -6783,7 +6923,6 @@ class TonChainStrategy {
6783
6923
  getName() {
6784
6924
  return "TON Chain Strategy";
6785
6925
  }
6786
- // ========== Wallet Management ==========
6787
6926
  async connect() {
6788
6927
  await this.config.tonConnectUI?.openModal();
6789
6928
  }
@@ -6808,17 +6947,26 @@ class TonChainStrategy {
6808
6947
  getConnectLabel(t2) {
6809
6948
  return t2("wallets.connectTonWallet");
6810
6949
  }
6811
- // ========== Balance & Validation ==========
6812
- async getBalances(address) {
6813
- return await getSwapBalances(address);
6950
+ async getBalances(address, tokens) {
6951
+ return await getTonBalances(
6952
+ address,
6953
+ tokens,
6954
+ this.config.tonClient,
6955
+ this.config.tonApiKey
6956
+ );
6814
6957
  }
6815
6958
  isAddressValid(address) {
6816
6959
  if (!address) return false;
6817
6960
  return true;
6818
6961
  }
6819
- // ========== Gas Estimation ==========
6820
6962
  async estimateGasRequirement(params) {
6821
- const { selectedToken, nativeTokenSymbol, amount, balances, reserveFallback } = params;
6963
+ const {
6964
+ selectedToken,
6965
+ nativeTokenSymbol,
6966
+ amount,
6967
+ balances,
6968
+ reserveFallback
6969
+ } = params;
6822
6970
  const nativeSym = nativeTokenSymbol.toUpperCase();
6823
6971
  const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
6824
6972
  const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
@@ -6840,7 +6988,6 @@ class TonChainStrategy {
6840
6988
  isNativeSelected
6841
6989
  };
6842
6990
  }
6843
- // ========== Transaction Execution ==========
6844
6991
  validateSteps(steps) {
6845
6992
  if (!steps || steps.length === 0) {
6846
6993
  throw new InvalidStepsError("ton", "No transaction steps provided");
@@ -6893,19 +7040,17 @@ class TonChainStrategy {
6893
7040
  const result = await this.config.tonConnectUI.sendTransaction(
6894
7041
  transaction
6895
7042
  );
6896
- const hash = this.getTxHash(result.boc);
6897
7043
  return {
6898
7044
  chainKey: "ton",
6899
- hash
7045
+ hash: result.boc
6900
7046
  };
6901
7047
  } catch (error) {
6902
7048
  throw toChainStrategyError(error, "ton", "transaction");
6903
7049
  }
6904
7050
  }
6905
- async waitForCompletion(txHash, context) {
7051
+ async waitForCompletion(txHash) {
6906
7052
  try {
6907
7053
  const confirmed = await this.checkTonTransaction(
6908
- context.srcAddress,
6909
7054
  txHash,
6910
7055
  TON_CONFIG.timeout
6911
7056
  );
@@ -6935,39 +7080,78 @@ class TonChainStrategy {
6935
7080
  };
6936
7081
  }
6937
7082
  }
6938
- // ========== Private Helper Methods ==========
6939
- getTxHash(boc) {
6940
- const cell = Cell.fromBase64(boc);
6941
- const buffer = cell.hash();
6942
- return buffer.toString("hex");
7083
+ getNormalizedExtMessageHash(message) {
7084
+ if (message.info.type !== "external-in") {
7085
+ throw new Error(`Expected external-in message, got ${message.info.type}`);
7086
+ }
7087
+ const normalizedInfo = {
7088
+ ...message.info,
7089
+ src: void 0,
7090
+ importFee: 0n
7091
+ };
7092
+ const normalizedMessage = {
7093
+ ...message,
7094
+ info: normalizedInfo,
7095
+ init: null
7096
+ };
7097
+ return beginCell$1().store(storeMessage(normalizedMessage, { forceRef: true })).endCell().hash();
6943
7098
  }
6944
- async checkTonTransaction(address, txHash, timeoutMs = 36e4) {
7099
+ async checkTonTransaction(bocBase64, timeoutMs = 36e4) {
6945
7100
  const deadline = Date.now() + timeoutMs;
6946
- const normalizedHash = txHash.toLowerCase();
6947
- while (Date.now() < deadline) {
6948
- try {
6949
- const response = await fetch(
6950
- `${TON_CONFIG.apiUrl}/getTransactions?address=${address}&limit=20`
7101
+ const client = getTonClient(this.config.tonClient, this.config.tonApiKey);
7102
+ try {
7103
+ const inMessage = loadMessage(Cell.fromBase64(bocBase64).beginParse());
7104
+ if (inMessage.info.type !== "external-in") {
7105
+ console.debug(
7106
+ "Expected external-in message, got:",
7107
+ inMessage.info.type
6951
7108
  );
6952
- if (!response.ok) {
6953
- console.debug("TonCenter API error:", response.status);
6954
- await new Promise((r) => setTimeout(r, 3e3));
6955
- continue;
6956
- }
6957
- const data = await response.json();
6958
- const transactions = data.result || [];
6959
- for (const tx of transactions) {
6960
- const txIdHash = tx.transaction_id?.hash;
6961
- if (txIdHash && txIdHash.toLowerCase() === normalizedHash) {
6962
- return true;
7109
+ return false;
7110
+ }
7111
+ const accountAddress = inMessage.info.dest;
7112
+ const targetMessageHash = this.getNormalizedExtMessageHash(inMessage);
7113
+ let lt = void 0;
7114
+ let hash = void 0;
7115
+ while (Date.now() < deadline) {
7116
+ try {
7117
+ const transactions = await client.getTransactions(accountAddress, {
7118
+ lt,
7119
+ hash,
7120
+ limit: 10,
7121
+ archival: true
7122
+ });
7123
+ if (transactions.length === 0) {
7124
+ await new Promise((r) => setTimeout(r, 3e3));
7125
+ lt = void 0;
7126
+ hash = void 0;
7127
+ continue;
7128
+ }
7129
+ for (const tx of transactions) {
7130
+ if (tx.inMessage?.info.type === "external-in") {
7131
+ const txInMessageHash = this.getNormalizedExtMessageHash(
7132
+ tx.inMessage
7133
+ );
7134
+ if (txInMessageHash.equals(targetMessageHash)) {
7135
+ console.debug("Transaction found by in-message hash");
7136
+ return true;
7137
+ }
7138
+ }
6963
7139
  }
7140
+ const lastTx = transactions[transactions.length - 1];
7141
+ lt = lastTx.lt.toString();
7142
+ hash = lastTx.hash().toString("base64");
7143
+ } catch (error) {
7144
+ console.debug("Error fetching transactions:", error);
7145
+ await new Promise((r) => setTimeout(r, 3e3));
7146
+ lt = void 0;
7147
+ hash = void 0;
6964
7148
  }
6965
- } catch (e) {
6966
- console.debug("TonCenter polling error:", e);
6967
7149
  }
6968
- await new Promise((r) => setTimeout(r, 3e3));
7150
+ return false;
7151
+ } catch (error) {
7152
+ console.debug("Error parsing BOC or checking transaction:", error);
7153
+ return false;
6969
7154
  }
6970
- return false;
6971
7155
  }
6972
7156
  }
6973
7157
  class TronChainStrategy {
@@ -7028,7 +7212,13 @@ class TronChainStrategy {
7028
7212
  }
7029
7213
  // ========== Gas Estimation ==========
7030
7214
  async estimateGasRequirement(params) {
7031
- const { selectedToken, nativeTokenSymbol, amount, balances, reserveFallback } = params;
7215
+ const {
7216
+ selectedToken,
7217
+ nativeTokenSymbol,
7218
+ amount,
7219
+ balances,
7220
+ reserveFallback
7221
+ } = params;
7032
7222
  const nativeSym = nativeTokenSymbol.toUpperCase();
7033
7223
  const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
7034
7224
  const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
@@ -7050,7 +7240,6 @@ class TronChainStrategy {
7050
7240
  isNativeSelected
7051
7241
  };
7052
7242
  }
7053
- // ========== Transaction Execution ==========
7054
7243
  validateSteps(steps) {
7055
7244
  console.log("validateSteps");
7056
7245
  if (!steps?.length) {
@@ -7085,7 +7274,10 @@ class TronChainStrategy {
7085
7274
  if (String(step.chainKey).toLowerCase() !== "tron") continue;
7086
7275
  const tx = step.transaction;
7087
7276
  if (!tx) {
7088
- throw new InvalidTransactionDataError("tron", "Missing transaction data");
7277
+ throw new InvalidTransactionDataError(
7278
+ "tron",
7279
+ "Missing transaction data"
7280
+ );
7089
7281
  }
7090
7282
  const hexData = typeof tx?.data === "string" ? tx.data : "";
7091
7283
  const parsed = this.parseTronStep(step, tx, hexData);
@@ -7136,13 +7328,19 @@ class TronChainStrategy {
7136
7328
  break;
7137
7329
  }
7138
7330
  default:
7139
- throw new InvalidStepsError("tron", "Unsupported TRON parsed tx kind");
7331
+ throw new InvalidStepsError(
7332
+ "tron",
7333
+ "Unsupported TRON parsed tx kind"
7334
+ );
7140
7335
  }
7141
7336
  const { txid } = await this.signAndBroadcast(tronWeb, unsigned);
7142
7337
  lastTxId = txid;
7143
7338
  }
7144
7339
  if (!lastTxId) {
7145
- throw new TransactionFailedError("tron", "No TRON transaction was executed");
7340
+ throw new TransactionFailedError(
7341
+ "tron",
7342
+ "No TRON transaction was executed"
7343
+ );
7146
7344
  }
7147
7345
  return { hash: lastTxId, chainKey: "tron" };
7148
7346
  }
@@ -7152,47 +7350,84 @@ class TronChainStrategy {
7152
7350
  if (!tronWeb) {
7153
7351
  throw new ProviderNotAvailableError("tron");
7154
7352
  }
7353
+ console.log(
7354
+ `Waiting for ${TRON_CONFIG.requiredConfirmations} confirmations for TRON tx: ${txHash}`
7355
+ );
7155
7356
  const deadline = Date.now() + TRON_CONFIG.timeout;
7156
- while (Date.now() < deadline) {
7357
+ let txBlockNumber = null;
7358
+ while (Date.now() < deadline && !txBlockNumber) {
7157
7359
  try {
7158
7360
  const info = await tronWeb.trx.getTransactionInfo(txHash);
7159
- console.log("TRON transaction info:", info);
7160
- if (info) {
7361
+ if (info && info.blockNumber) {
7161
7362
  const result = info.receipt?.result;
7162
- if (result === "SUCCESS") {
7163
- console.log("TRON transaction confirmed on-chain");
7363
+ if (result !== "SUCCESS") {
7364
+ const msg = this.hexToAscii(info.resMessage) || null;
7365
+ let reason = msg;
7366
+ const cr = info.contractResult?.[0];
7367
+ if (cr && /^0x?08c379a0/i.test(cr)) {
7368
+ try {
7369
+ const clean = cr.replace(/^0x/, "");
7370
+ const len = parseInt(clean.slice(8 + 64, 8 + 64 + 64), 16);
7371
+ const strHex = clean.slice(
7372
+ 8 + 64 + 64,
7373
+ 8 + 64 + 64 + len * 2
7374
+ );
7375
+ const str = this.hexToAscii("0x" + strHex);
7376
+ if (str) reason = str;
7377
+ } catch (e) {
7378
+ console.debug("TRON revert string decode error", e);
7379
+ }
7380
+ }
7381
+ const error2 = new TransactionRevertedError(
7382
+ "tron",
7383
+ txHash,
7384
+ reason || "Unknown error"
7385
+ );
7164
7386
  return {
7165
- completed: true
7387
+ completed: false,
7388
+ error: error2.message
7166
7389
  };
7167
7390
  }
7168
- const msg = this.hexToAscii(info.resMessage) || null;
7169
- let reason = msg;
7170
- const cr = info.contractResult?.[0];
7171
- if (cr && /^0x?08c379a0/i.test(cr)) {
7172
- try {
7173
- const clean = cr.replace(/^0x/, "");
7174
- const len = parseInt(clean.slice(8 + 64, 8 + 64 + 64), 16);
7175
- const strHex = clean.slice(8 + 64 + 64, 8 + 64 + 64 + len * 2);
7176
- const str = this.hexToAscii("0x" + strHex);
7177
- if (str) reason = str;
7178
- } catch (e) {
7179
- console.debug("TRON revert string decode error", e);
7180
- }
7391
+ txBlockNumber = info.blockNumber;
7392
+ console.log(`TRON transaction found in block ${txBlockNumber}`);
7393
+ }
7394
+ } catch (e) {
7395
+ console.debug("TRON getTransactionInfo error:", e);
7396
+ }
7397
+ if (!txBlockNumber) {
7398
+ await new Promise((r) => setTimeout(r, TRON_CONFIG.pollingInterval));
7399
+ }
7400
+ }
7401
+ if (!txBlockNumber) {
7402
+ const error2 = new TransactionTimeoutError("tron", txHash);
7403
+ return {
7404
+ completed: false,
7405
+ error: error2.message
7406
+ };
7407
+ }
7408
+ let confirmations = 0;
7409
+ while (Date.now() < deadline) {
7410
+ try {
7411
+ const currentBlock = await tronWeb.trx.getCurrentBlock();
7412
+ const currentBlockNumber = currentBlock?.block_header?.raw_data?.number;
7413
+ if (currentBlockNumber) {
7414
+ confirmations = currentBlockNumber - txBlockNumber;
7415
+ if (confirmations >= TRON_CONFIG.requiredConfirmations) {
7416
+ console.log(
7417
+ `TRON transaction confirmed in block ${txBlockNumber} with ${confirmations} confirmations`
7418
+ );
7419
+ return {
7420
+ completed: true
7421
+ };
7181
7422
  }
7182
- const error2 = new TransactionRevertedError(
7183
- "tron",
7184
- txHash,
7185
- reason || "Unknown error"
7423
+ console.log(
7424
+ `TRON transaction confirmations: ${confirmations}/${TRON_CONFIG.requiredConfirmations}`
7186
7425
  );
7187
- return {
7188
- completed: false,
7189
- error: error2.message
7190
- };
7191
7426
  }
7192
7427
  } catch (e) {
7193
- console.debug("TRON getTransactionInfo error:", e);
7428
+ console.debug("TRON getCurrentBlock error:", e);
7194
7429
  }
7195
- await new Promise((r) => setTimeout(r, 3e3));
7430
+ await new Promise((r) => setTimeout(r, TRON_CONFIG.pollingInterval));
7196
7431
  }
7197
7432
  const error = new TransactionTimeoutError("tron", txHash);
7198
7433
  return {
@@ -7200,14 +7435,17 @@ class TronChainStrategy {
7200
7435
  error: error.message
7201
7436
  };
7202
7437
  } catch (error) {
7203
- const chainError = toChainStrategyError(error, "tron", "waitForCompletion");
7438
+ const chainError = toChainStrategyError(
7439
+ error,
7440
+ "tron",
7441
+ "waitForCompletion"
7442
+ );
7204
7443
  return {
7205
7444
  completed: false,
7206
7445
  error: chainError.message
7207
7446
  };
7208
7447
  }
7209
7448
  }
7210
- // ========== Private Helper Methods ==========
7211
7449
  getTronWeb() {
7212
7450
  return typeof window !== "undefined" ? window.tronWeb : void 0;
7213
7451
  }
@@ -7260,7 +7498,10 @@ class TronChainStrategy {
7260
7498
  amount: this.extractAmountFromTxData(tx.data)
7261
7499
  };
7262
7500
  }
7263
- throw new InvalidTransactionDataError("tron", "Cannot parse approve transaction");
7501
+ throw new InvalidTransactionDataError(
7502
+ "tron",
7503
+ "Cannot parse approve transaction"
7504
+ );
7264
7505
  }
7265
7506
  if (step?.type === "transfer" || step?.type === "bridge") {
7266
7507
  const s = step;
@@ -7290,7 +7531,10 @@ class TronChainStrategy {
7290
7531
  feeLimit: raw2.feeLimit
7291
7532
  };
7292
7533
  }
7293
- throw new InvalidTransactionDataError("tron", "Cannot parse transfer/bridge transaction");
7534
+ throw new InvalidTransactionDataError(
7535
+ "tron",
7536
+ "Cannot parse transfer/bridge transaction"
7537
+ );
7294
7538
  }
7295
7539
  if (tx?.to && tx?.value && !tx?.data) {
7296
7540
  const v = BigInt(tx.value.toString());
@@ -7310,19 +7554,28 @@ class TronChainStrategy {
7310
7554
  feeLimit: raw.feeLimit
7311
7555
  };
7312
7556
  }
7313
- throw new InvalidTransactionDataError("tron", `Unsupported TRON step type: ${step?.type ?? "unknown"}`);
7557
+ throw new InvalidTransactionDataError(
7558
+ "tron",
7559
+ `Unsupported TRON step type: ${step?.type ?? "unknown"}`
7560
+ );
7314
7561
  }
7315
7562
  extractSpenderFromTxData(data) {
7316
7563
  const clean = data.replace(/^0x/, "");
7317
7564
  if (clean.length < 74) {
7318
- throw new InvalidTransactionDataError("tron", "Invalid transaction data length");
7565
+ throw new InvalidTransactionDataError(
7566
+ "tron",
7567
+ "Invalid transaction data length"
7568
+ );
7319
7569
  }
7320
7570
  return "41" + clean.slice(32, 72);
7321
7571
  }
7322
7572
  extractAmountFromTxData(data) {
7323
7573
  const clean = data.replace(/^0x/, "");
7324
7574
  if (clean.length < 138) {
7325
- throw new InvalidTransactionDataError("tron", "Invalid transaction data length");
7575
+ throw new InvalidTransactionDataError(
7576
+ "tron",
7577
+ "Invalid transaction data length"
7578
+ );
7326
7579
  }
7327
7580
  const amountHex = clean.slice(72, 136);
7328
7581
  return BigInt("0x" + amountHex).toString();
@@ -7330,7 +7583,10 @@ class TronChainStrategy {
7330
7583
  extractRecipientFromTxData(data) {
7331
7584
  const clean = data.replace(/^0x/, "");
7332
7585
  if (clean.length < 74) {
7333
- throw new InvalidTransactionDataError("tron", "Invalid transaction data length");
7586
+ throw new InvalidTransactionDataError(
7587
+ "tron",
7588
+ "Invalid transaction data length"
7589
+ );
7334
7590
  }
7335
7591
  return "41" + clean.slice(32, 72);
7336
7592
  }
@@ -7405,7 +7661,6 @@ class TronChainStrategy {
7405
7661
  return null;
7406
7662
  }
7407
7663
  }
7408
- // ========== Transaction Builders ==========
7409
7664
  async buildApprove(tronWeb, p) {
7410
7665
  const res = await tronWeb.transactionBuilder.triggerSmartContract(
7411
7666
  p.token,
@@ -7418,7 +7673,10 @@ class TronChainStrategy {
7418
7673
  p.from
7419
7674
  );
7420
7675
  if (!res?.transaction) {
7421
- throw new TransactionFailedError("tron", "Failed to build approve transaction");
7676
+ throw new TransactionFailedError(
7677
+ "tron",
7678
+ "Failed to build approve transaction"
7679
+ );
7422
7680
  }
7423
7681
  return res.transaction;
7424
7682
  }
@@ -7434,7 +7692,10 @@ class TronChainStrategy {
7434
7692
  p.from
7435
7693
  );
7436
7694
  if (!res?.transaction) {
7437
- throw new TransactionFailedError("tron", "Failed to build transfer transaction");
7695
+ throw new TransactionFailedError(
7696
+ "tron",
7697
+ "Failed to build transfer transaction"
7698
+ );
7438
7699
  }
7439
7700
  return res.transaction;
7440
7701
  }
@@ -7454,12 +7715,13 @@ class TronChainStrategy {
7454
7715
  p.from
7455
7716
  );
7456
7717
  if (!res?.transaction) {
7457
- throw new TransactionFailedError("tron", "Failed to build raw call transaction");
7718
+ throw new TransactionFailedError(
7719
+ "tron",
7720
+ "Failed to build raw call transaction"
7721
+ );
7458
7722
  }
7459
7723
  return res.transaction;
7460
7724
  }
7461
- // ========== Transaction Sender ==========
7462
- /* eslint-disable @typescript-eslint/no-explicit-any */
7463
7725
  async signAndBroadcast(tronWeb, unsignedTx) {
7464
7726
  const signed = await tronWeb.trx.sign(unsignedTx);
7465
7727
  const sent = await tronWeb.trx.sendRawTransaction(signed);
@@ -7471,33 +7733,77 @@ class TronChainStrategy {
7471
7733
  }
7472
7734
  return { txid: sent.txid };
7473
7735
  }
7736
+ /**
7737
+ * Check if a transaction has been solidified (confirmed by solidityNode)
7738
+ * This is an additional check for critical transactions to ensure they are
7739
+ * truly confirmed and available through the solidityNode API.
7740
+ *
7741
+ * Usage: Call this method after waitForCompletion for high-value transactions
7742
+ * to get additional assurance that the transaction is solidified.
7743
+ *
7744
+ * @param txHash - Transaction hash to check
7745
+ * @returns true if the transaction is solidified, false otherwise
7746
+ */
7747
+ async checkSolidified(txHash) {
7748
+ try {
7749
+ const tronWeb = this.getTronWeb();
7750
+ if (!tronWeb) {
7751
+ return false;
7752
+ }
7753
+ const info = await tronWeb.trx.getTransactionInfo(txHash);
7754
+ if (!info || !info.blockNumber) {
7755
+ console.debug(
7756
+ "Transaction not yet solidified (no blockNumber in info)"
7757
+ );
7758
+ return false;
7759
+ }
7760
+ const result = info.receipt?.result;
7761
+ if (result === "SUCCESS") {
7762
+ console.log(
7763
+ `Transaction ${txHash} is solidified in block ${info.blockNumber}`
7764
+ );
7765
+ return true;
7766
+ }
7767
+ console.debug(`Transaction solidified but result is: ${result}`);
7768
+ return false;
7769
+ } catch (error) {
7770
+ console.debug("Error checking solidified status:", error);
7771
+ return false;
7772
+ }
7773
+ }
7474
7774
  }
7475
7775
  function ChainStrategyProvider({
7476
7776
  children,
7477
7777
  evmWallet,
7478
7778
  tonWallet,
7479
- tronWallet
7779
+ tronWallet,
7780
+ tonClient,
7781
+ tonApiKey
7480
7782
  }) {
7481
7783
  const evmStrategy = useMemo(
7482
7784
  () => new EvmChainStrategy({
7483
7785
  evmAddress: evmWallet.address,
7484
7786
  evmIsConnected: evmWallet.isConnected,
7485
7787
  evmDisconnect: evmWallet.disconnect,
7486
- walletClient: evmWallet.walletClient
7788
+ walletClient: evmWallet.walletClient,
7789
+ publicClient: evmWallet.publicClient
7487
7790
  }),
7488
7791
  [
7489
7792
  evmWallet.address,
7490
7793
  evmWallet.isConnected,
7491
7794
  evmWallet.disconnect,
7492
- evmWallet.walletClient
7795
+ evmWallet.walletClient,
7796
+ evmWallet.publicClient
7493
7797
  ]
7494
7798
  );
7495
7799
  const tonStrategy = useMemo(
7496
7800
  () => new TonChainStrategy({
7497
7801
  tonConnectUI: tonWallet.tonConnectUI,
7498
- tonAddress: tonWallet.address
7802
+ tonAddress: tonWallet.address,
7803
+ tonClient,
7804
+ tonApiKey
7499
7805
  }),
7500
- [tonWallet.tonConnectUI, tonWallet.address]
7806
+ [tonWallet.tonConnectUI, tonWallet.address, tonClient, tonApiKey]
7501
7807
  );
7502
7808
  const tronStrategy = useMemo(
7503
7809
  () => new TronChainStrategy({
@@ -7533,6 +7839,7 @@ const EvaaBridgeWithProviders = (props) => {
7533
7839
  const { address: evmAddress, isConnected: evmIsConnected } = useAccount();
7534
7840
  const { disconnect: evmDisconnect } = useDisconnect();
7535
7841
  const { data: walletClient } = useWalletClient();
7842
+ const publicClient = usePublicClient();
7536
7843
  const {
7537
7844
  address: tronAddress,
7538
7845
  connected: tronConnected,
@@ -7557,7 +7864,8 @@ const EvaaBridgeWithProviders = (props) => {
7557
7864
  address: evmAddress,
7558
7865
  isConnected: evmIsConnected,
7559
7866
  disconnect: evmDisconnect,
7560
- walletClient
7867
+ walletClient,
7868
+ publicClient
7561
7869
  },
7562
7870
  tonWallet: {
7563
7871
  tonConnectUI,
@@ -7570,6 +7878,8 @@ const EvaaBridgeWithProviders = (props) => {
7570
7878
  connect: tronConnect,
7571
7879
  disconnect: tronDisconnect
7572
7880
  },
7881
+ tonClient: props.tonClient,
7882
+ tonApiKey: props.tonApiKey,
7573
7883
  children: /* @__PURE__ */ jsx(EvaaBridgeContent, { ...props })
7574
7884
  }
7575
7885
  );
@@ -7674,16 +7984,16 @@ const EvaaBridgeContent = ({
7674
7984
  }, [chains, assetMatrix, allowedFromChains]);
7675
7985
  return /* @__PURE__ */ jsxs(Fragment, { children: [
7676
7986
  /* @__PURE__ */ jsxs(
7677
- "section",
7987
+ Card,
7678
7988
  {
7679
7989
  className: cn(
7680
- "p-5 bg-card max-w-md w-full rounded-3xl mx-auto flex flex-col gap-4 relative",
7990
+ "max-w-md w-full mx-auto flex flex-col relative",
7681
7991
  className
7682
7992
  ),
7683
7993
  ref: modalContainerRef,
7684
7994
  children: [
7685
7995
  /* @__PURE__ */ jsx(FormHeader, { modalContainer: modalContainerRef.current }),
7686
- /* @__PURE__ */ jsxs("div", { className: "space-y-[1px]", children: [
7996
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-[1px]", children: [
7687
7997
  /* @__PURE__ */ jsx(
7688
7998
  SwapSection,
7689
7999
  {
@@ -7721,10 +8031,10 @@ const EvaaBridgeContent = ({
7721
8031
  enabled: sendToAnother,
7722
8032
  onToggle: () => setSendToAnother((v) => !v)
7723
8033
  }
7724
- )
8034
+ ),
8035
+ /* @__PURE__ */ jsx(SubmitButton, {})
7725
8036
  ] }),
7726
- /* @__PURE__ */ jsx(SubmitButton, {}),
7727
- /* @__PURE__ */ jsx(ReceiveRow, {})
8037
+ /* @__PURE__ */ jsx(CardFooter, { children: /* @__PURE__ */ jsx(ReceiveRow, {}) })
7728
8038
  ]
7729
8039
  }
7730
8040
  ),
@@ -7813,8 +8123,8 @@ export {
7813
8123
  getQuoteDetails,
7814
8124
  getQuoteFees,
7815
8125
  getQuotesByPriority,
7816
- getSwapBalances,
7817
8126
  getTokens,
8127
+ getTonBalances,
7818
8128
  getTronBalances,
7819
8129
  isAddressValidForChain,
7820
8130
  isEvmAddress,
@@ -7823,7 +8133,7 @@ export {
7823
8133
  isZeroAddr,
7824
8134
  listAssetsForSelect,
7825
8135
  lookupTokenMeta,
7826
- normalizeTickerSymbol,
8136
+ normalizeTickerSymbol$1 as normalizeTickerSymbol,
7827
8137
  pollUntilDelivered,
7828
8138
  resolveTokenOnChain,
7829
8139
  resolveTokenOnChainFromMatrix$2 as resolveTokenOnChainFromMatrix,