@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.
- package/dist/evaa-bridge.cjs +655 -345
- package/dist/evaa-bridge.cjs.map +1 -1
- package/dist/evaa-bridge.mjs +660 -350
- package/dist/evaa-bridge.mjs.map +1 -1
- package/dist/index.d.ts +7 -3
- package/package.json +7 -11
package/dist/evaa-bridge.mjs
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
2017
|
-
/* @__PURE__ */ jsxs(
|
|
2018
|
-
/* @__PURE__ */
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
/* @__PURE__ */
|
|
2023
|
-
|
|
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
|
-
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
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
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
/* @__PURE__ */ jsx("div", { className: "
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
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
|
-
},
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
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
|
-
|
|
4178
|
-
|
|
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
|
-
|
|
4201
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
6346
|
-
throw new Error("No
|
|
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
|
|
6355
|
-
|
|
6381
|
+
const ethBalance = await publicClient.getBalance({
|
|
6382
|
+
address
|
|
6383
|
+
});
|
|
6384
|
+
balance = parseFloat(formatUnits(ethBalance, token.decimals));
|
|
6356
6385
|
} else {
|
|
6357
|
-
if (!
|
|
6386
|
+
if (!isAddress(token.address)) {
|
|
6358
6387
|
continue;
|
|
6359
6388
|
}
|
|
6360
|
-
const
|
|
6361
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
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(
|
|
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
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
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 {
|
|
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
|
|
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
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
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(
|
|
7099
|
+
async checkTonTransaction(bocBase64, timeoutMs = 36e4) {
|
|
6945
7100
|
const deadline = Date.now() + timeoutMs;
|
|
6946
|
-
const
|
|
6947
|
-
|
|
6948
|
-
|
|
6949
|
-
|
|
6950
|
-
|
|
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
|
-
|
|
6953
|
-
|
|
6954
|
-
|
|
6955
|
-
|
|
6956
|
-
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
const
|
|
6961
|
-
|
|
6962
|
-
|
|
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
|
-
|
|
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 {
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
7357
|
+
let txBlockNumber = null;
|
|
7358
|
+
while (Date.now() < deadline && !txBlockNumber) {
|
|
7157
7359
|
try {
|
|
7158
7360
|
const info = await tronWeb.trx.getTransactionInfo(txHash);
|
|
7159
|
-
|
|
7160
|
-
if (info) {
|
|
7361
|
+
if (info && info.blockNumber) {
|
|
7161
7362
|
const result = info.receipt?.result;
|
|
7162
|
-
if (result
|
|
7163
|
-
|
|
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:
|
|
7387
|
+
completed: false,
|
|
7388
|
+
error: error2.message
|
|
7166
7389
|
};
|
|
7167
7390
|
}
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
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
|
-
|
|
7183
|
-
|
|
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
|
|
7428
|
+
console.debug("TRON getCurrentBlock error:", e);
|
|
7194
7429
|
}
|
|
7195
|
-
await new Promise((r) => setTimeout(r,
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
7987
|
+
Card,
|
|
7678
7988
|
{
|
|
7679
7989
|
className: cn(
|
|
7680
|
-
"
|
|
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(
|
|
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(
|
|
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,
|