@rash2x/bridge-widget 0.1.3 → 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 +703 -405
- package/dist/evaa-bridge.cjs.map +1 -1
- package/dist/evaa-bridge.mjs +708 -410
- package/dist/evaa-bridge.mjs.map +1 -1
- package/dist/index.d.ts +7 -3
- package/package.json +7 -11
package/dist/evaa-bridge.cjs
CHANGED
|
@@ -44,10 +44,11 @@ const reactWindow = require("react-window");
|
|
|
44
44
|
const SwitchPrimitive = require("@radix-ui/react-switch");
|
|
45
45
|
const lucideReact = require("lucide-react");
|
|
46
46
|
const AccordionPrimitive = require("@radix-ui/react-accordion");
|
|
47
|
-
const connectkit = require("connectkit");
|
|
48
47
|
const i18next = require("i18next");
|
|
49
48
|
const sonner = require("sonner");
|
|
50
49
|
const ethers = require("ethers");
|
|
50
|
+
const viem = require("viem");
|
|
51
|
+
const ton = require("@ton/ton");
|
|
51
52
|
const tronwalletAdapters = require("@tronweb3/tronwallet-adapters");
|
|
52
53
|
function _interopNamespaceDefault(e) {
|
|
53
54
|
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
@@ -551,7 +552,7 @@ function cn(...inputs) {
|
|
|
551
552
|
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
552
553
|
}
|
|
553
554
|
const buttonVariants = classVarianceAuthority.cva(
|
|
554
|
-
"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",
|
|
555
|
+
"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",
|
|
555
556
|
{
|
|
556
557
|
variants: {
|
|
557
558
|
variant: {
|
|
@@ -824,19 +825,6 @@ const Tip = (props) => {
|
|
|
824
825
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: text }) })
|
|
825
826
|
] });
|
|
826
827
|
};
|
|
827
|
-
function toLD(human, decimals) {
|
|
828
|
-
const [i = "0", f = ""] = human.replace(",", ".").split(".");
|
|
829
|
-
const frac = (f + "0".repeat(decimals)).slice(0, decimals);
|
|
830
|
-
return BigInt(i + frac).toString();
|
|
831
|
-
}
|
|
832
|
-
function fromLD(ld, decimals) {
|
|
833
|
-
const bi = BigInt(ld || "0");
|
|
834
|
-
if (decimals === 0) return Number(bi);
|
|
835
|
-
const base = BigInt(10) ** BigInt(decimals);
|
|
836
|
-
const int = bi / base;
|
|
837
|
-
const frac = Number(bi % base) / Number(base);
|
|
838
|
-
return Number(int) + frac;
|
|
839
|
-
}
|
|
840
828
|
async function getChains() {
|
|
841
829
|
const res = await fetch("https://stargate.finance/api/v1/chains", {
|
|
842
830
|
credentials: "same-origin"
|
|
@@ -884,49 +872,16 @@ async function getDestTokens(srcChainKey, srcTokenAddr) {
|
|
|
884
872
|
});
|
|
885
873
|
return unique;
|
|
886
874
|
}
|
|
887
|
-
|
|
888
|
-
function normalizeTickerSymbol(s) {
|
|
875
|
+
function normalizeTickerSymbol$1(s) {
|
|
889
876
|
return s.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
|
|
890
877
|
}
|
|
891
|
-
async function getSwapBalances(accountFriendly) {
|
|
892
|
-
if (!isTonFriendly(accountFriendly)) throw new Error("Invalid TON address");
|
|
893
|
-
const accRes = await fetch(
|
|
894
|
-
`https://tonapi.io/v2/accounts/${accountFriendly}`
|
|
895
|
-
);
|
|
896
|
-
if (!accRes.ok) throw new Error(`TON account fetch failed: ${accRes.status}`);
|
|
897
|
-
const acc = await accRes.json();
|
|
898
|
-
const ton = fromLD(String(acc?.balance ?? "0"), 9);
|
|
899
|
-
const result = {
|
|
900
|
-
TON: { balance: ton, address: "ton-native" }
|
|
901
|
-
};
|
|
902
|
-
const jetsRes = await fetch(
|
|
903
|
-
`https://tonapi.io/v2/accounts/${accountFriendly}/jettons?limit=200`
|
|
904
|
-
);
|
|
905
|
-
if (!jetsRes.ok) return result;
|
|
906
|
-
const jets = await jetsRes.json();
|
|
907
|
-
const items = jets?.balances ?? jets?.jettons ?? jets?.items ?? [];
|
|
908
|
-
for (const it of items) {
|
|
909
|
-
const rawSym = (it?.jetton?.symbol ?? it?.symbol ?? "").toString();
|
|
910
|
-
if (!rawSym) continue;
|
|
911
|
-
const symUpper = rawSym.toUpperCase();
|
|
912
|
-
const symNorm = normalizeTickerSymbol(symUpper);
|
|
913
|
-
const master = (it?.jetton?.address ?? it?.address ?? "").toString();
|
|
914
|
-
const decimals = Number(it?.jetton?.decimals ?? it?.decimals ?? 9) || 9;
|
|
915
|
-
const raw = (it?.balance ?? "0").toString();
|
|
916
|
-
const human = fromLD(raw, decimals);
|
|
917
|
-
const entry = { balance: human, address: master };
|
|
918
|
-
result[symUpper] = entry;
|
|
919
|
-
if (symNorm !== symUpper) result[symNorm] = entry;
|
|
920
|
-
}
|
|
921
|
-
return result;
|
|
922
|
-
}
|
|
923
878
|
const BASE_URL$1 = "https://icons-ckg.pages.dev/stargate-light/tokens";
|
|
924
879
|
const TokenSymbol = ({
|
|
925
880
|
symbol,
|
|
926
881
|
className = "w-4 h-4",
|
|
927
882
|
alt
|
|
928
883
|
}) => {
|
|
929
|
-
const normalizedSymbol = normalizeTickerSymbol(symbol).toLowerCase();
|
|
884
|
+
const normalizedSymbol = normalizeTickerSymbol$1(symbol).toLowerCase();
|
|
930
885
|
const src = `${BASE_URL$1}/${normalizedSymbol}.svg`;
|
|
931
886
|
return /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt: alt ?? symbol, className });
|
|
932
887
|
};
|
|
@@ -1340,6 +1295,19 @@ function useChainStrategies() {
|
|
|
1340
1295
|
}
|
|
1341
1296
|
return context;
|
|
1342
1297
|
}
|
|
1298
|
+
function toLD(human, decimals) {
|
|
1299
|
+
const [i = "0", f = ""] = human.replace(",", ".").split(".");
|
|
1300
|
+
const frac = (f + "0".repeat(decimals)).slice(0, decimals);
|
|
1301
|
+
return BigInt(i + frac).toString();
|
|
1302
|
+
}
|
|
1303
|
+
function fromLD(ld, decimals) {
|
|
1304
|
+
const bi = BigInt(ld || "0");
|
|
1305
|
+
if (decimals === 0) return Number(bi);
|
|
1306
|
+
const base = BigInt(10) ** BigInt(decimals);
|
|
1307
|
+
const int = bi / base;
|
|
1308
|
+
const frac = Number(bi % base) / Number(base);
|
|
1309
|
+
return Number(int) + frac;
|
|
1310
|
+
}
|
|
1343
1311
|
function resolveTokenOnChainFromMatrix$2(assetMatrix, assetSymbol, chainKey) {
|
|
1344
1312
|
if (!assetMatrix || !assetSymbol || !chainKey) return void 0;
|
|
1345
1313
|
const byChain = assetMatrix[assetSymbol.toUpperCase()];
|
|
@@ -1521,18 +1489,19 @@ function useBalances(chainKey, address) {
|
|
|
1521
1489
|
const data = query.data;
|
|
1522
1490
|
if (data) {
|
|
1523
1491
|
for (const [sum, v] of Object.entries(data)) {
|
|
1524
|
-
map.set(normalizeTickerSymbol(sum), Number(v.balance ?? 0));
|
|
1492
|
+
map.set(normalizeTickerSymbol$1(sum), Number(v.balance ?? 0));
|
|
1525
1493
|
}
|
|
1526
1494
|
}
|
|
1527
1495
|
return map;
|
|
1528
1496
|
}, [query.data]);
|
|
1529
1497
|
const getBalance = require$$0.useCallback(
|
|
1530
|
-
(symbol) => balanceBySymbol.get(normalizeTickerSymbol(symbol)) ?? 0,
|
|
1498
|
+
(symbol) => balanceBySymbol.get(normalizeTickerSymbol$1(symbol)) ?? 0,
|
|
1531
1499
|
[balanceBySymbol]
|
|
1532
1500
|
);
|
|
1533
1501
|
const isLoading = query.isLoading || query.isFetching;
|
|
1502
|
+
const balances = require$$0.useMemo(() => query.data || {}, [query.data]);
|
|
1534
1503
|
return {
|
|
1535
|
-
balances
|
|
1504
|
+
balances,
|
|
1536
1505
|
getBalance,
|
|
1537
1506
|
isLoading,
|
|
1538
1507
|
query
|
|
@@ -1986,17 +1955,17 @@ const RefreshButton = () => {
|
|
|
1986
1955
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1987
1956
|
Button,
|
|
1988
1957
|
{
|
|
1989
|
-
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" : ""}`,
|
|
1990
1958
|
onClick: handleRefresh,
|
|
1991
1959
|
disabled: spinning,
|
|
1960
|
+
variant: "secondary",
|
|
1961
|
+
size: "sm",
|
|
1992
1962
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1993
1963
|
ReloadIcon,
|
|
1994
1964
|
{
|
|
1995
1965
|
style: {
|
|
1996
1966
|
transform: `rotate(${turns * 180}deg)`,
|
|
1997
1967
|
transition: "transform 300ms linear"
|
|
1998
|
-
}
|
|
1999
|
-
className: "size-4 text-foreground m-1 will-change-transform"
|
|
1968
|
+
}
|
|
2000
1969
|
}
|
|
2001
1970
|
)
|
|
2002
1971
|
}
|
|
@@ -2004,7 +1973,6 @@ const RefreshButton = () => {
|
|
|
2004
1973
|
};
|
|
2005
1974
|
const SelectTokenButton = ({
|
|
2006
1975
|
onClick,
|
|
2007
|
-
className,
|
|
2008
1976
|
token
|
|
2009
1977
|
}) => {
|
|
2010
1978
|
const { t } = reactI18next.useTranslation();
|
|
@@ -2015,7 +1983,8 @@ const SelectTokenButton = ({
|
|
|
2015
1983
|
Button,
|
|
2016
1984
|
{
|
|
2017
1985
|
onClick,
|
|
2018
|
-
|
|
1986
|
+
size: "sm",
|
|
1987
|
+
variant: "secondary",
|
|
2019
1988
|
type: "button",
|
|
2020
1989
|
"aria-label": label,
|
|
2021
1990
|
children: [
|
|
@@ -2035,6 +2004,75 @@ const SelectTokenButton = ({
|
|
|
2035
2004
|
}
|
|
2036
2005
|
);
|
|
2037
2006
|
};
|
|
2007
|
+
function Card({ className, ...props }) {
|
|
2008
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2009
|
+
"div",
|
|
2010
|
+
{
|
|
2011
|
+
"data-slot": "card",
|
|
2012
|
+
className: cn(
|
|
2013
|
+
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
2014
|
+
className
|
|
2015
|
+
),
|
|
2016
|
+
...props
|
|
2017
|
+
}
|
|
2018
|
+
);
|
|
2019
|
+
}
|
|
2020
|
+
function CardHeader({ className, ...props }) {
|
|
2021
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2022
|
+
"div",
|
|
2023
|
+
{
|
|
2024
|
+
"data-slot": "card-header",
|
|
2025
|
+
className: cn(
|
|
2026
|
+
"@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",
|
|
2027
|
+
className
|
|
2028
|
+
),
|
|
2029
|
+
...props
|
|
2030
|
+
}
|
|
2031
|
+
);
|
|
2032
|
+
}
|
|
2033
|
+
function CardTitle({ className, ...props }) {
|
|
2034
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2035
|
+
"div",
|
|
2036
|
+
{
|
|
2037
|
+
"data-slot": "card-title",
|
|
2038
|
+
className: cn("leading-none font-semibold", className),
|
|
2039
|
+
...props
|
|
2040
|
+
}
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
function CardAction({ className, ...props }) {
|
|
2044
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2045
|
+
"div",
|
|
2046
|
+
{
|
|
2047
|
+
"data-slot": "card-action",
|
|
2048
|
+
className: cn(
|
|
2049
|
+
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
2050
|
+
className
|
|
2051
|
+
),
|
|
2052
|
+
...props
|
|
2053
|
+
}
|
|
2054
|
+
);
|
|
2055
|
+
}
|
|
2056
|
+
function CardContent({ className, ...props }) {
|
|
2057
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2058
|
+
"div",
|
|
2059
|
+
{
|
|
2060
|
+
"data-slot": "card-content",
|
|
2061
|
+
className: cn("px-6", className),
|
|
2062
|
+
...props
|
|
2063
|
+
}
|
|
2064
|
+
);
|
|
2065
|
+
}
|
|
2066
|
+
function CardFooter({ className, ...props }) {
|
|
2067
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2068
|
+
"div",
|
|
2069
|
+
{
|
|
2070
|
+
"data-slot": "card-footer",
|
|
2071
|
+
className: cn("flex items-center px-6 [.border-t]:pt-6", className),
|
|
2072
|
+
...props
|
|
2073
|
+
}
|
|
2074
|
+
);
|
|
2075
|
+
}
|
|
2038
2076
|
const FormHeaderComponent = ({ modalContainer }) => {
|
|
2039
2077
|
const { t } = reactI18next.useTranslation();
|
|
2040
2078
|
const { isOpen, onClose, onOpen } = useModal();
|
|
@@ -2051,29 +2089,14 @@ const FormHeaderComponent = ({ modalContainer }) => {
|
|
|
2051
2089
|
const sum = selectedAssetSymbol.toUpperCase();
|
|
2052
2090
|
return assets.find((a) => a.symbol.toUpperCase() === sum) ?? assets[0];
|
|
2053
2091
|
}, [assets, selectedAssetSymbol]);
|
|
2054
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2055
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2056
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2061
|
-
|
|
2062
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2063
|
-
Button,
|
|
2064
|
-
{
|
|
2065
|
-
className: "cursor-pointer py-1.5 h-8.5 hover:scale-110 shadow-none px-8.5 hover:bg-secondary bg-secondary !rounded-40",
|
|
2066
|
-
onClick: onOpenSettings,
|
|
2067
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2068
|
-
BoltIcon,
|
|
2069
|
-
{
|
|
2070
|
-
className: "size-4 text-foreground m-1",
|
|
2071
|
-
stroke: "currentColor"
|
|
2072
|
-
}
|
|
2073
|
-
)
|
|
2074
|
-
}
|
|
2075
|
-
)
|
|
2076
|
-
] })
|
|
2092
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(CardHeader, { className: "gap-y-0", children: [
|
|
2093
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardTitle, { className: "flex items-center gap-2.5", children: [
|
|
2094
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-normal leading-3.5 text-muted-foreground", children: t("bridge.selectToken") }),
|
|
2095
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTokenButton, { token: current, onClick: onOpen })
|
|
2096
|
+
] }),
|
|
2097
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardAction, { className: "flex items-center gap-2.5", children: [
|
|
2098
|
+
/* @__PURE__ */ jsxRuntime.jsx(RefreshButton, {}),
|
|
2099
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: onOpenSettings, size: "sm", variant: "secondary", children: /* @__PURE__ */ jsxRuntime.jsx(BoltIcon, { stroke: "currentColor" }) })
|
|
2077
2100
|
] }),
|
|
2078
2101
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2079
2102
|
TokenSelectModal,
|
|
@@ -3763,7 +3786,9 @@ function useBridgeTransaction() {
|
|
|
3763
3786
|
function useGasEstimate(amountNum) {
|
|
3764
3787
|
const { fromChain } = useChainsStore();
|
|
3765
3788
|
const { selectedAssetSymbol, tokens } = useTokensStore();
|
|
3766
|
-
const
|
|
3789
|
+
const getSourceGasReserveHuman = useSettingsStore(
|
|
3790
|
+
(state) => state.getSourceGasReserveHuman
|
|
3791
|
+
);
|
|
3767
3792
|
const { srcAddress } = useAddresses();
|
|
3768
3793
|
const { balances, isLoading: balancesLoading } = useBalances(
|
|
3769
3794
|
fromChain?.chainKey,
|
|
@@ -3771,59 +3796,44 @@ function useGasEstimate(amountNum) {
|
|
|
3771
3796
|
);
|
|
3772
3797
|
const { chainRegistry } = useChainStrategies();
|
|
3773
3798
|
const balancesKnown = !balancesLoading;
|
|
3774
|
-
const
|
|
3775
|
-
null
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
}
|
|
3813
|
-
}
|
|
3814
|
-
estimateGas();
|
|
3815
|
-
return () => {
|
|
3816
|
-
cancelled = true;
|
|
3817
|
-
};
|
|
3818
|
-
}, [
|
|
3819
|
-
fromChain,
|
|
3820
|
-
selectedAssetSymbol,
|
|
3821
|
-
tokens,
|
|
3822
|
-
amountNum,
|
|
3823
|
-
balances,
|
|
3824
|
-
chainRegistry,
|
|
3825
|
-
getSourceGasReserveHuman
|
|
3826
|
-
]);
|
|
3799
|
+
const strategy = require$$0.useMemo(() => {
|
|
3800
|
+
if (!fromChain) return null;
|
|
3801
|
+
return chainRegistry.getStrategy(fromChain.chainKey);
|
|
3802
|
+
}, [fromChain, chainRegistry]);
|
|
3803
|
+
const { data: gasRequirement } = reactQuery.useQuery({
|
|
3804
|
+
queryKey: [
|
|
3805
|
+
"gas-estimate",
|
|
3806
|
+
fromChain?.chainKey,
|
|
3807
|
+
selectedAssetSymbol,
|
|
3808
|
+
amountNum,
|
|
3809
|
+
balances
|
|
3810
|
+
],
|
|
3811
|
+
queryFn: async () => {
|
|
3812
|
+
if (!fromChain || !strategy) {
|
|
3813
|
+
return null;
|
|
3814
|
+
}
|
|
3815
|
+
const selectedToken = tokens?.find(
|
|
3816
|
+
(t) => t.symbol.toUpperCase() === selectedAssetSymbol?.toUpperCase()
|
|
3817
|
+
) || null;
|
|
3818
|
+
const nativeTokenSymbol = fromChain.nativeCurrency?.symbol ?? "";
|
|
3819
|
+
const nativeDecimals = fromChain.nativeCurrency?.decimals || 18;
|
|
3820
|
+
const reserveFallback = getSourceGasReserveHuman(fromChain.chainKey);
|
|
3821
|
+
const result = await strategy.estimateGasRequirement({
|
|
3822
|
+
selectedToken,
|
|
3823
|
+
nativeTokenSymbol,
|
|
3824
|
+
amount: amountNum,
|
|
3825
|
+
balances,
|
|
3826
|
+
nativeDecimals,
|
|
3827
|
+
reserveFallback
|
|
3828
|
+
});
|
|
3829
|
+
return result;
|
|
3830
|
+
},
|
|
3831
|
+
enabled: !!fromChain && !!strategy,
|
|
3832
|
+
staleTime: 3e4,
|
|
3833
|
+
gcTime: 5 * 6e4,
|
|
3834
|
+
refetchOnWindowFocus: false,
|
|
3835
|
+
retry: 1
|
|
3836
|
+
});
|
|
3827
3837
|
return {
|
|
3828
3838
|
nativeSym: gasRequirement?.nativeSym || "",
|
|
3829
3839
|
nativeBalance: gasRequirement?.nativeBalance || 0,
|
|
@@ -3972,7 +3982,7 @@ function useSilentValidations(amountString) {
|
|
|
3972
3982
|
]);
|
|
3973
3983
|
return validationResult;
|
|
3974
3984
|
}
|
|
3975
|
-
|
|
3985
|
+
const SubmitButton = () => {
|
|
3976
3986
|
const { t } = reactI18next.useTranslation();
|
|
3977
3987
|
const { chainRegistry } = useChainStrategies();
|
|
3978
3988
|
const { srcAddress, dstAddress } = useAddresses();
|
|
@@ -4048,7 +4058,7 @@ function useTransferAction() {
|
|
|
4048
4058
|
maximumAmountFormatted,
|
|
4049
4059
|
chainRegistry
|
|
4050
4060
|
]);
|
|
4051
|
-
const
|
|
4061
|
+
const handleClick = async () => {
|
|
4052
4062
|
if (isBusy) return;
|
|
4053
4063
|
if (missingSrc && srcChainKey) {
|
|
4054
4064
|
onOpen("src");
|
|
@@ -4068,24 +4078,7 @@ function useTransferAction() {
|
|
|
4068
4078
|
}
|
|
4069
4079
|
};
|
|
4070
4080
|
const disabled = isBusy || amountNum <= 0 || status === "loading" || isBalanceLoading || hasInsufficientBalance || hasAmountTooLarge || !gas.hasEnoughGas || noRoute || !isValidForTransfer;
|
|
4071
|
-
return {
|
|
4072
|
-
// состояния
|
|
4073
|
-
isBusy,
|
|
4074
|
-
canTransfer,
|
|
4075
|
-
missingSrc,
|
|
4076
|
-
missingDst,
|
|
4077
|
-
hasEnoughGas: gas.hasEnoughGas,
|
|
4078
|
-
noRoute,
|
|
4079
|
-
// представление
|
|
4080
|
-
label,
|
|
4081
|
-
disabled,
|
|
4082
|
-
// действие
|
|
4083
|
-
onClick
|
|
4084
|
-
};
|
|
4085
|
-
}
|
|
4086
|
-
const SubmitButton = () => {
|
|
4087
|
-
const { label, disabled, onClick } = useTransferAction();
|
|
4088
|
-
return /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick, disabled, size: "lg", children: label });
|
|
4081
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Button, { onClick: handleClick, disabled, className: "w-full", children: label });
|
|
4089
4082
|
};
|
|
4090
4083
|
function short(addr) {
|
|
4091
4084
|
return addr.slice(0, 4) + "…" + addr.slice(-4);
|
|
@@ -4095,6 +4088,7 @@ const WalletSelectModal = ({
|
|
|
4095
4088
|
}) => {
|
|
4096
4089
|
const { t } = reactI18next.useTranslation();
|
|
4097
4090
|
const { isOpen, onClose } = useWalletSelectModal();
|
|
4091
|
+
const { connect, connectors, isPending } = wagmi.useConnect();
|
|
4098
4092
|
const { chainRegistry } = useChainStrategies();
|
|
4099
4093
|
const tonWallet = chainRegistry.getStrategyByType("ton");
|
|
4100
4094
|
const metaMaskWallet = chainRegistry.getStrategyByType("evm");
|
|
@@ -4127,7 +4121,13 @@ const WalletSelectModal = ({
|
|
|
4127
4121
|
onDisconnect: () => tronWallet.disconnect()
|
|
4128
4122
|
});
|
|
4129
4123
|
}
|
|
4130
|
-
const isWalletConnected = (walletId) =>
|
|
4124
|
+
const isWalletConnected = (walletId) => {
|
|
4125
|
+
const isEvmConnector = connectors.some((c) => c.id === walletId);
|
|
4126
|
+
if (isEvmConnector && metaMaskWallet?.isConnected()) {
|
|
4127
|
+
return true;
|
|
4128
|
+
}
|
|
4129
|
+
return connectedWallets.some((w) => w.id === walletId);
|
|
4130
|
+
};
|
|
4131
4131
|
const tonWallets = [
|
|
4132
4132
|
{
|
|
4133
4133
|
id: "ton",
|
|
@@ -4136,14 +4136,15 @@ const WalletSelectModal = ({
|
|
|
4136
4136
|
enabled: true
|
|
4137
4137
|
}
|
|
4138
4138
|
];
|
|
4139
|
-
const evmWallets =
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4139
|
+
const evmWallets = connectors.filter(
|
|
4140
|
+
(connector) => connector.id === "walletConnect" || connector.id === "metaMaskSDK"
|
|
4141
|
+
).map((connector) => ({
|
|
4142
|
+
id: connector.id,
|
|
4143
|
+
name: connector.name,
|
|
4144
|
+
icon: MetaMaskIcon,
|
|
4145
|
+
// You can add a WalletConnect icon here
|
|
4146
|
+
enabled: true
|
|
4147
|
+
}));
|
|
4147
4148
|
const tronWallets = [
|
|
4148
4149
|
{
|
|
4149
4150
|
id: "tronlink",
|
|
@@ -4173,9 +4174,6 @@ const WalletSelectModal = ({
|
|
|
4173
4174
|
case "ton":
|
|
4174
4175
|
await tonWallet?.connect();
|
|
4175
4176
|
break;
|
|
4176
|
-
case "metamask":
|
|
4177
|
-
await metaMaskWallet?.connect();
|
|
4178
|
-
break;
|
|
4179
4177
|
case "tronlink":
|
|
4180
4178
|
await tronWallet?.connect();
|
|
4181
4179
|
break;
|
|
@@ -4200,56 +4198,47 @@ const WalletSelectModal = ({
|
|
|
4200
4198
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase", children: t("wallets.connected") }),
|
|
4201
4199
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "", children: connectedWallets.map((wallet) => {
|
|
4202
4200
|
const IconComponent = wallet.icon;
|
|
4203
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { onClick: () => {
|
|
4201
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "", children: /* @__PURE__ */ jsxRuntime.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: [
|
|
4202
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
|
|
4203
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: "size-7.5" }) }),
|
|
4204
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
|
|
4205
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-extrabold text-foreground text-sm leading-4 truncate", children: short(wallet.address) }),
|
|
4206
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: wallet.name })
|
|
4207
|
+
] })
|
|
4208
|
+
] }) }),
|
|
4209
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4210
|
+
"div",
|
|
4211
|
+
{
|
|
4212
|
+
onClick: () => {
|
|
4216
4213
|
wallet.onDisconnect();
|
|
4217
4214
|
onClose();
|
|
4218
|
-
},
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4215
|
+
},
|
|
4216
|
+
className: "text-sm font-medium text-muted-foreground",
|
|
4217
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ExitIcon, { className: "text-[#808080] size-6" })
|
|
4218
|
+
}
|
|
4219
|
+
)
|
|
4220
|
+
] }) }, wallet.id);
|
|
4222
4221
|
}) })
|
|
4223
4222
|
] }),
|
|
4224
4223
|
categories.map((category) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
4225
4224
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-2 leading-4 text-base font-semibold text-muted-foreground uppercase", children: category.title }),
|
|
4226
4225
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "", children: category.wallets.map((wallet) => {
|
|
4227
4226
|
const IconComponent = wallet.icon;
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4231
|
-
Button,
|
|
4232
|
-
{
|
|
4233
|
-
type: "button",
|
|
4234
|
-
onClick: show,
|
|
4235
|
-
disabled: isConnecting,
|
|
4236
|
-
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",
|
|
4237
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
4238
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: "size-7.5" }) }),
|
|
4239
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start min-w-0", children: [
|
|
4240
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-extrabold text-foreground text-sm leading-4 truncate", children: wallet.name }),
|
|
4241
|
-
wallet.comingSoon ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: t("wallets.comingSoon") }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs leading-3 font-semibold text-muted-foreground", children: t("wallets.connect") })
|
|
4242
|
-
] })
|
|
4243
|
-
] })
|
|
4244
|
-
}
|
|
4245
|
-
);
|
|
4246
|
-
} }) }, wallet.id);
|
|
4247
|
-
}
|
|
4227
|
+
const isEvmConnector = category.title === t("wallets.evmWallets");
|
|
4228
|
+
const connector = isEvmConnector ? connectors.find((c) => c.id === wallet.id) : null;
|
|
4248
4229
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4249
4230
|
Button,
|
|
4250
4231
|
{
|
|
4251
|
-
|
|
4252
|
-
|
|
4232
|
+
type: "button",
|
|
4233
|
+
onClick: () => {
|
|
4234
|
+
if (connector) {
|
|
4235
|
+
connect({ connector });
|
|
4236
|
+
onClose();
|
|
4237
|
+
} else {
|
|
4238
|
+
handleWalletSelect(wallet.id);
|
|
4239
|
+
}
|
|
4240
|
+
},
|
|
4241
|
+
disabled: isEvmConnector ? isPending : !wallet.enabled,
|
|
4253
4242
|
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",
|
|
4254
4243
|
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
4255
4244
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-7.5 h-7.5 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(IconComponent, { className: "size-7.5" }) }),
|
|
@@ -4265,39 +4254,6 @@ const WalletSelectModal = ({
|
|
|
4265
4254
|
] })
|
|
4266
4255
|
] }) });
|
|
4267
4256
|
};
|
|
4268
|
-
function Card({ className, ...props }) {
|
|
4269
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4270
|
-
"div",
|
|
4271
|
-
{
|
|
4272
|
-
"data-slot": "card",
|
|
4273
|
-
className: cn(
|
|
4274
|
-
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
4275
|
-
className
|
|
4276
|
-
),
|
|
4277
|
-
...props
|
|
4278
|
-
}
|
|
4279
|
-
);
|
|
4280
|
-
}
|
|
4281
|
-
function CardContent({ className, ...props }) {
|
|
4282
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4283
|
-
"div",
|
|
4284
|
-
{
|
|
4285
|
-
"data-slot": "card-content",
|
|
4286
|
-
className: cn("px-6", className),
|
|
4287
|
-
...props
|
|
4288
|
-
}
|
|
4289
|
-
);
|
|
4290
|
-
}
|
|
4291
|
-
function CardFooter({ className, ...props }) {
|
|
4292
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4293
|
-
"div",
|
|
4294
|
-
{
|
|
4295
|
-
"data-slot": "card-footer",
|
|
4296
|
-
className: cn("flex items-center px-6 [.border-t]:pt-6", className),
|
|
4297
|
-
...props
|
|
4298
|
-
}
|
|
4299
|
-
);
|
|
4300
|
-
}
|
|
4301
4257
|
const TransactionProgressVector = (props) => {
|
|
4302
4258
|
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "200", height: "200", viewBox: "0 0 200 200", fill: "none", xmlns: "http://www.w3.org/2000/svg", ...props, children: [
|
|
4303
4259
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -6361,6 +6317,49 @@ class ChainStrategyRegistry {
|
|
|
6361
6317
|
await strategy.disconnect();
|
|
6362
6318
|
}
|
|
6363
6319
|
}
|
|
6320
|
+
const EVM_CONFIG = {
|
|
6321
|
+
usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
6322
|
+
gasEstimates: {
|
|
6323
|
+
approve: 65000n,
|
|
6324
|
+
bridge: 300000n
|
|
6325
|
+
},
|
|
6326
|
+
gasBuffer: 1.2,
|
|
6327
|
+
// 20% buffer
|
|
6328
|
+
timeout: 3e5,
|
|
6329
|
+
// 5 minutes (increased for slower networks)
|
|
6330
|
+
requiredConfirmations: 3
|
|
6331
|
+
// Wait for 3 confirmations for reorg protection
|
|
6332
|
+
};
|
|
6333
|
+
const TON_CONFIG = {
|
|
6334
|
+
apiUrl: "https://toncenter.com/api/v2",
|
|
6335
|
+
timeout: 36e4,
|
|
6336
|
+
// 6 minutes
|
|
6337
|
+
validUntil: 600
|
|
6338
|
+
// 10 minutes
|
|
6339
|
+
};
|
|
6340
|
+
const TRON_CONFIG = {
|
|
6341
|
+
timeout: 12e4,
|
|
6342
|
+
// 2 minutes (for 19 confirmations)
|
|
6343
|
+
feeLimit: 1e8,
|
|
6344
|
+
// 100 TRX in sun
|
|
6345
|
+
requiredConfirmations: 19,
|
|
6346
|
+
// TRON standard: 19 blocks for confirmation
|
|
6347
|
+
pollingInterval: 3e3
|
|
6348
|
+
// 3 seconds between checks
|
|
6349
|
+
};
|
|
6350
|
+
let tonClientInstance = null;
|
|
6351
|
+
function getTonClient(customClient, apiKey) {
|
|
6352
|
+
if (customClient) {
|
|
6353
|
+
return customClient;
|
|
6354
|
+
}
|
|
6355
|
+
if (!tonClientInstance) {
|
|
6356
|
+
tonClientInstance = new ton.TonClient({
|
|
6357
|
+
endpoint: `${TON_CONFIG.apiUrl}/jsonRPC`,
|
|
6358
|
+
apiKey
|
|
6359
|
+
});
|
|
6360
|
+
}
|
|
6361
|
+
return tonClientInstance;
|
|
6362
|
+
}
|
|
6364
6363
|
function isNativeAddress(addr) {
|
|
6365
6364
|
if (!addr) return false;
|
|
6366
6365
|
const a = addr.toLowerCase();
|
|
@@ -6386,44 +6385,72 @@ function formatUnitsFromBigIntStr(valueStr, decimals) {
|
|
|
6386
6385
|
const tail = s.slice(s.length - decimals).replace(/0+$/, "");
|
|
6387
6386
|
return Number(tail ? `${head}.${tail}` : head);
|
|
6388
6387
|
}
|
|
6389
|
-
|
|
6388
|
+
function isTonFriendlyAddress(address) {
|
|
6389
|
+
return !!address && /^[A-Za-z0-9_-]{48,}$/.test(address);
|
|
6390
|
+
}
|
|
6391
|
+
function normalizeTickerSymbol(symbol) {
|
|
6392
|
+
return symbol.toUpperCase().replace(/₮/g, "T").replace(/[^A-Z0-9]/g, "");
|
|
6393
|
+
}
|
|
6394
|
+
function parseTonAddress(address) {
|
|
6395
|
+
if (address.startsWith("0x")) {
|
|
6396
|
+
const hex = address.slice(2);
|
|
6397
|
+
return ton.Address.parseRaw(`0:${hex}`);
|
|
6398
|
+
}
|
|
6399
|
+
if (address.includes(":")) {
|
|
6400
|
+
return ton.Address.parseRaw(address);
|
|
6401
|
+
}
|
|
6402
|
+
return ton.Address.parse(address);
|
|
6403
|
+
}
|
|
6404
|
+
async function getEvmBalances(publicClient, address, tokens) {
|
|
6390
6405
|
const balances = {};
|
|
6391
6406
|
try {
|
|
6392
|
-
|
|
6407
|
+
console.log("start getEvmBalances");
|
|
6408
|
+
console.log("publicClient:", publicClient);
|
|
6409
|
+
console.log("isAddress:", viem.isAddress(address));
|
|
6410
|
+
console.log("tokens:", tokens);
|
|
6411
|
+
if (!address || !viem.isAddress(address)) {
|
|
6393
6412
|
console.warn(`Invalid EVM address provided: ${address}`);
|
|
6394
6413
|
return balances;
|
|
6395
6414
|
}
|
|
6396
|
-
if (
|
|
6397
|
-
throw new Error("No
|
|
6415
|
+
if (!publicClient) {
|
|
6416
|
+
throw new Error("No public client provided");
|
|
6398
6417
|
}
|
|
6399
|
-
const provider = new ethers.ethers.BrowserProvider(window.ethereum);
|
|
6400
6418
|
for (const token of tokens) {
|
|
6401
6419
|
try {
|
|
6402
6420
|
let balance = 0;
|
|
6403
6421
|
const isNative = isNativeAddress(token.address);
|
|
6404
6422
|
if (isNative) {
|
|
6405
|
-
const ethBalance = await
|
|
6406
|
-
|
|
6423
|
+
const ethBalance = await publicClient.getBalance({
|
|
6424
|
+
address
|
|
6425
|
+
});
|
|
6426
|
+
balance = parseFloat(viem.formatUnits(ethBalance, token.decimals));
|
|
6407
6427
|
} else {
|
|
6408
|
-
if (!
|
|
6428
|
+
if (!viem.isAddress(token.address)) {
|
|
6409
6429
|
continue;
|
|
6410
6430
|
}
|
|
6411
|
-
const
|
|
6412
|
-
|
|
6431
|
+
const bytecode = await publicClient.getBytecode({
|
|
6432
|
+
address: token.address
|
|
6433
|
+
});
|
|
6434
|
+
if (!bytecode || bytecode === "0x") {
|
|
6413
6435
|
continue;
|
|
6414
6436
|
}
|
|
6415
|
-
const contract = new ethers.ethers.Contract(
|
|
6416
|
-
token.address,
|
|
6417
|
-
[
|
|
6418
|
-
"function balanceOf(address owner) view returns (uint256)",
|
|
6419
|
-
"function decimals() view returns (uint8)"
|
|
6420
|
-
],
|
|
6421
|
-
provider
|
|
6422
|
-
);
|
|
6423
6437
|
try {
|
|
6424
|
-
const tokenBalance = await
|
|
6438
|
+
const tokenBalance = await publicClient.readContract({
|
|
6439
|
+
address: token.address,
|
|
6440
|
+
abi: [
|
|
6441
|
+
{
|
|
6442
|
+
name: "balanceOf",
|
|
6443
|
+
type: "function",
|
|
6444
|
+
stateMutability: "view",
|
|
6445
|
+
inputs: [{ name: "owner", type: "address" }],
|
|
6446
|
+
outputs: [{ name: "balance", type: "uint256" }]
|
|
6447
|
+
}
|
|
6448
|
+
],
|
|
6449
|
+
functionName: "balanceOf",
|
|
6450
|
+
args: [address]
|
|
6451
|
+
});
|
|
6425
6452
|
balance = parseFloat(
|
|
6426
|
-
|
|
6453
|
+
viem.formatUnits(tokenBalance, token.decimals)
|
|
6427
6454
|
);
|
|
6428
6455
|
} catch {
|
|
6429
6456
|
continue;
|
|
@@ -6446,6 +6473,68 @@ async function getEvmBalances(address, tokens) {
|
|
|
6446
6473
|
}
|
|
6447
6474
|
return balances;
|
|
6448
6475
|
}
|
|
6476
|
+
async function getTonBalances(address, tokens, customTonClient, tonApiKey) {
|
|
6477
|
+
const balances = {};
|
|
6478
|
+
try {
|
|
6479
|
+
if (!isTonFriendlyAddress(address)) {
|
|
6480
|
+
console.warn(`Invalid TON address provided: ${address}`);
|
|
6481
|
+
return balances;
|
|
6482
|
+
}
|
|
6483
|
+
const client = getTonClient(customTonClient, tonApiKey);
|
|
6484
|
+
const accountAddress = ton.Address.parse(address);
|
|
6485
|
+
console.log(address);
|
|
6486
|
+
console.log(tokens);
|
|
6487
|
+
try {
|
|
6488
|
+
const balance = await client.getBalance(accountAddress);
|
|
6489
|
+
const tonBalance = Number(balance) / 1e9;
|
|
6490
|
+
console.log("tonBalance", tonBalance);
|
|
6491
|
+
if (tonBalance > 0) {
|
|
6492
|
+
balances.TON = { balance: tonBalance, address: "ton-native" };
|
|
6493
|
+
}
|
|
6494
|
+
} catch (error) {
|
|
6495
|
+
console.warn("Failed to get native TON balance:", error);
|
|
6496
|
+
}
|
|
6497
|
+
for (const token of tokens) {
|
|
6498
|
+
try {
|
|
6499
|
+
if (isNativeAddress(token.address) || token.symbol.toUpperCase() === "TON") {
|
|
6500
|
+
continue;
|
|
6501
|
+
}
|
|
6502
|
+
const jettonMasterAddress = parseTonAddress(token.address);
|
|
6503
|
+
const jettonWalletAddress = await client.runMethod(
|
|
6504
|
+
jettonMasterAddress,
|
|
6505
|
+
"get_wallet_address",
|
|
6506
|
+
[
|
|
6507
|
+
{
|
|
6508
|
+
type: "slice",
|
|
6509
|
+
cell: ton.beginCell().storeAddress(accountAddress).endCell()
|
|
6510
|
+
}
|
|
6511
|
+
]
|
|
6512
|
+
);
|
|
6513
|
+
const jettonWalletAddr = jettonWalletAddress.stack.readAddress();
|
|
6514
|
+
const jettonData = await client.runMethod(
|
|
6515
|
+
jettonWalletAddr,
|
|
6516
|
+
"get_wallet_data"
|
|
6517
|
+
);
|
|
6518
|
+
const jettonBalance = jettonData.stack.readBigNumber();
|
|
6519
|
+
const humanBalance = Number(jettonBalance) / Math.pow(10, token.decimals);
|
|
6520
|
+
if (humanBalance > 0) {
|
|
6521
|
+
const symbolUpper = token.symbol.toUpperCase();
|
|
6522
|
+
const symbolNorm = normalizeTickerSymbol(symbolUpper);
|
|
6523
|
+
const entry = { balance: humanBalance, address: token.address };
|
|
6524
|
+
balances[symbolUpper] = entry;
|
|
6525
|
+
if (symbolNorm !== symbolUpper) {
|
|
6526
|
+
balances[symbolNorm] = entry;
|
|
6527
|
+
}
|
|
6528
|
+
}
|
|
6529
|
+
} catch (error) {
|
|
6530
|
+
console.debug(`Failed to get balance for ${token.symbol}:`, error);
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
} catch (error) {
|
|
6534
|
+
console.error("Failed to get TON balances:", error);
|
|
6535
|
+
}
|
|
6536
|
+
return balances;
|
|
6537
|
+
}
|
|
6449
6538
|
async function getTronBalances(tronWeb, address, tokens) {
|
|
6450
6539
|
const balances = {};
|
|
6451
6540
|
try {
|
|
@@ -6489,30 +6578,6 @@ async function getTronBalances(tronWeb, address, tokens) {
|
|
|
6489
6578
|
}
|
|
6490
6579
|
return balances;
|
|
6491
6580
|
}
|
|
6492
|
-
const EVM_CONFIG = {
|
|
6493
|
-
usdtAddress: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
|
|
6494
|
-
gasEstimates: {
|
|
6495
|
-
approve: 65000n,
|
|
6496
|
-
bridge: 300000n
|
|
6497
|
-
},
|
|
6498
|
-
gasBuffer: 1.2,
|
|
6499
|
-
// 20% buffer
|
|
6500
|
-
timeout: 12e4
|
|
6501
|
-
// 2 minutes
|
|
6502
|
-
};
|
|
6503
|
-
const TON_CONFIG = {
|
|
6504
|
-
apiUrl: "https://toncenter.com/api/v2",
|
|
6505
|
-
timeout: 36e4,
|
|
6506
|
-
// 6 minutes
|
|
6507
|
-
validUntil: 600
|
|
6508
|
-
// 10 minutes
|
|
6509
|
-
};
|
|
6510
|
-
const TRON_CONFIG = {
|
|
6511
|
-
timeout: 6e4,
|
|
6512
|
-
// 1 minute
|
|
6513
|
-
feeLimit: 1e8
|
|
6514
|
-
// 100 TRX in sun
|
|
6515
|
-
};
|
|
6516
6581
|
const ERC20_ABI = [
|
|
6517
6582
|
"function approve(address spender, uint256 amount) returns (bool)",
|
|
6518
6583
|
"function allowance(address owner, address spender) view returns (uint256)",
|
|
@@ -6523,12 +6588,13 @@ class EvmChainStrategy {
|
|
|
6523
6588
|
constructor(config) {
|
|
6524
6589
|
__publicField(this, "config");
|
|
6525
6590
|
__publicField(this, "provider");
|
|
6591
|
+
__publicField(this, "publicClient");
|
|
6526
6592
|
this.config = config;
|
|
6593
|
+
this.publicClient = config.publicClient;
|
|
6527
6594
|
if (config.walletClient) {
|
|
6528
6595
|
this.provider = new ethers.BrowserProvider(config.walletClient.transport);
|
|
6529
6596
|
}
|
|
6530
6597
|
}
|
|
6531
|
-
// ========== Identity ==========
|
|
6532
6598
|
canHandle(chainKey) {
|
|
6533
6599
|
const key = chainKey.toLowerCase();
|
|
6534
6600
|
return key !== "ton" && key !== "tron";
|
|
@@ -6539,7 +6605,6 @@ class EvmChainStrategy {
|
|
|
6539
6605
|
getName() {
|
|
6540
6606
|
return "EVM Chain Strategy";
|
|
6541
6607
|
}
|
|
6542
|
-
// ========== Wallet Management ==========
|
|
6543
6608
|
async connect() {
|
|
6544
6609
|
}
|
|
6545
6610
|
async disconnect() {
|
|
@@ -6563,15 +6628,17 @@ class EvmChainStrategy {
|
|
|
6563
6628
|
getConnectLabel(t) {
|
|
6564
6629
|
return t("wallets.connectEvmWallet");
|
|
6565
6630
|
}
|
|
6566
|
-
// ========== Balance & Validation ==========
|
|
6567
6631
|
async getBalances(address, tokens) {
|
|
6568
|
-
|
|
6632
|
+
if (!this.publicClient) {
|
|
6633
|
+
console.warn("No publicClient available for balance query");
|
|
6634
|
+
return {};
|
|
6635
|
+
}
|
|
6636
|
+
return await getEvmBalances(this.publicClient, address, tokens);
|
|
6569
6637
|
}
|
|
6570
6638
|
isAddressValid(address) {
|
|
6571
6639
|
if (!address) return false;
|
|
6572
6640
|
return /^0x[0-9a-fA-F]{40}$/.test(address);
|
|
6573
6641
|
}
|
|
6574
|
-
// ========== Gas Estimation ==========
|
|
6575
6642
|
async estimateGasRequirement(params) {
|
|
6576
6643
|
const provider = this.provider;
|
|
6577
6644
|
const {
|
|
@@ -6618,7 +6685,6 @@ class EvmChainStrategy {
|
|
|
6618
6685
|
isNativeSelected
|
|
6619
6686
|
};
|
|
6620
6687
|
}
|
|
6621
|
-
// ========== Transaction Execution ==========
|
|
6622
6688
|
validateSteps(steps) {
|
|
6623
6689
|
if (!steps || steps.length === 0) {
|
|
6624
6690
|
throw new InvalidStepsError("evm", "No transaction steps provided");
|
|
@@ -6680,17 +6746,14 @@ class EvmChainStrategy {
|
|
|
6680
6746
|
if (!provider) {
|
|
6681
6747
|
throw new ProviderNotAvailableError("evm");
|
|
6682
6748
|
}
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
}
|
|
6692
|
-
await new Promise((r) => setTimeout(r, 2500));
|
|
6693
|
-
}
|
|
6749
|
+
console.log(
|
|
6750
|
+
`Waiting for ${EVM_CONFIG.requiredConfirmations} confirmations for tx: ${txHash}`
|
|
6751
|
+
);
|
|
6752
|
+
const receipt = await provider.waitForTransaction(
|
|
6753
|
+
txHash,
|
|
6754
|
+
EVM_CONFIG.requiredConfirmations,
|
|
6755
|
+
EVM_CONFIG.timeout
|
|
6756
|
+
);
|
|
6694
6757
|
if (!receipt) {
|
|
6695
6758
|
const error = new TransactionTimeoutError("evm", txHash);
|
|
6696
6759
|
return {
|
|
@@ -6705,11 +6768,35 @@ class EvmChainStrategy {
|
|
|
6705
6768
|
error: error.message
|
|
6706
6769
|
};
|
|
6707
6770
|
}
|
|
6708
|
-
console.log(
|
|
6771
|
+
console.log(
|
|
6772
|
+
`EVM transaction confirmed in block ${receipt.blockNumber} with ${EVM_CONFIG.requiredConfirmations} confirmations`
|
|
6773
|
+
);
|
|
6709
6774
|
return {
|
|
6710
6775
|
completed: true
|
|
6711
6776
|
};
|
|
6712
6777
|
} catch (error) {
|
|
6778
|
+
if (error && typeof error === "object" && "code" in error && error.code === "TRANSACTION_REPLACED") {
|
|
6779
|
+
console.log(
|
|
6780
|
+
`Transaction was replaced: ${"reason" in error ? String(error.reason) : "unknown"}`
|
|
6781
|
+
);
|
|
6782
|
+
if ("receipt" in error && error.receipt) {
|
|
6783
|
+
const replacementReceipt = error.receipt;
|
|
6784
|
+
if (replacementReceipt.status === 1) {
|
|
6785
|
+
console.log(
|
|
6786
|
+
`Replacement transaction succeeded in block ${replacementReceipt.blockNumber}`
|
|
6787
|
+
);
|
|
6788
|
+
return {
|
|
6789
|
+
completed: true
|
|
6790
|
+
};
|
|
6791
|
+
} else {
|
|
6792
|
+
const chainError2 = new TransactionRevertedError("evm", txHash);
|
|
6793
|
+
return {
|
|
6794
|
+
completed: false,
|
|
6795
|
+
error: chainError2.message
|
|
6796
|
+
};
|
|
6797
|
+
}
|
|
6798
|
+
}
|
|
6799
|
+
}
|
|
6713
6800
|
const chainError = toChainStrategyError(
|
|
6714
6801
|
error,
|
|
6715
6802
|
"evm",
|
|
@@ -6721,7 +6808,6 @@ class EvmChainStrategy {
|
|
|
6721
6808
|
};
|
|
6722
6809
|
}
|
|
6723
6810
|
}
|
|
6724
|
-
// ========== Private Helper Methods ==========
|
|
6725
6811
|
async executeTransaction(step) {
|
|
6726
6812
|
const provider = this.provider;
|
|
6727
6813
|
const signer = await provider?.getSigner();
|
|
@@ -6824,13 +6910,52 @@ class EvmChainStrategy {
|
|
|
6824
6910
|
return false;
|
|
6825
6911
|
}
|
|
6826
6912
|
}
|
|
6913
|
+
/**
|
|
6914
|
+
* Check if a block has reached finality status (optional, for critical transactions)
|
|
6915
|
+
* This is more stringent than confirmations and protects against long-range reorgs
|
|
6916
|
+
*
|
|
6917
|
+
* Usage: Call this method after waitForCompletion if you need additional security
|
|
6918
|
+
* for high-value transactions. The method checks if the block has been marked as
|
|
6919
|
+
* "finalized" by the Ethereum consensus layer (2/3 of validators).
|
|
6920
|
+
*
|
|
6921
|
+
* @param blockNumber - The block number to check for finality
|
|
6922
|
+
* @returns true if the block is finalized, false otherwise
|
|
6923
|
+
*/
|
|
6924
|
+
async checkFinality(blockNumber) {
|
|
6925
|
+
try {
|
|
6926
|
+
const provider = this.provider;
|
|
6927
|
+
if (!provider) {
|
|
6928
|
+
return false;
|
|
6929
|
+
}
|
|
6930
|
+
const finalizedBlock = await provider.getBlock("finalized");
|
|
6931
|
+
if (!finalizedBlock) {
|
|
6932
|
+
console.debug(
|
|
6933
|
+
"Finalized block not available (pre-merge or unsupported)"
|
|
6934
|
+
);
|
|
6935
|
+
return false;
|
|
6936
|
+
}
|
|
6937
|
+
const isFinalized = blockNumber <= finalizedBlock.number;
|
|
6938
|
+
if (isFinalized) {
|
|
6939
|
+
console.log(
|
|
6940
|
+
`Block ${blockNumber} has reached finality (finalized block: ${finalizedBlock.number})`
|
|
6941
|
+
);
|
|
6942
|
+
} else {
|
|
6943
|
+
console.debug(
|
|
6944
|
+
`Block ${blockNumber} not yet finalized (finalized block: ${finalizedBlock.number})`
|
|
6945
|
+
);
|
|
6946
|
+
}
|
|
6947
|
+
return isFinalized;
|
|
6948
|
+
} catch (error) {
|
|
6949
|
+
console.debug("Error checking finality:", error);
|
|
6950
|
+
return false;
|
|
6951
|
+
}
|
|
6952
|
+
}
|
|
6827
6953
|
}
|
|
6828
6954
|
class TonChainStrategy {
|
|
6829
6955
|
constructor(config) {
|
|
6830
6956
|
__publicField(this, "config");
|
|
6831
6957
|
this.config = config;
|
|
6832
6958
|
}
|
|
6833
|
-
// ========== Identity ==========
|
|
6834
6959
|
canHandle(chainKey) {
|
|
6835
6960
|
return chainKey.toLowerCase() === "ton";
|
|
6836
6961
|
}
|
|
@@ -6840,7 +6965,6 @@ class TonChainStrategy {
|
|
|
6840
6965
|
getName() {
|
|
6841
6966
|
return "TON Chain Strategy";
|
|
6842
6967
|
}
|
|
6843
|
-
// ========== Wallet Management ==========
|
|
6844
6968
|
async connect() {
|
|
6845
6969
|
await this.config.tonConnectUI?.openModal();
|
|
6846
6970
|
}
|
|
@@ -6865,17 +6989,26 @@ class TonChainStrategy {
|
|
|
6865
6989
|
getConnectLabel(t) {
|
|
6866
6990
|
return t("wallets.connectTonWallet");
|
|
6867
6991
|
}
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6992
|
+
async getBalances(address, tokens) {
|
|
6993
|
+
return await getTonBalances(
|
|
6994
|
+
address,
|
|
6995
|
+
tokens,
|
|
6996
|
+
this.config.tonClient,
|
|
6997
|
+
this.config.tonApiKey
|
|
6998
|
+
);
|
|
6871
6999
|
}
|
|
6872
7000
|
isAddressValid(address) {
|
|
6873
7001
|
if (!address) return false;
|
|
6874
7002
|
return true;
|
|
6875
7003
|
}
|
|
6876
|
-
// ========== Gas Estimation ==========
|
|
6877
7004
|
async estimateGasRequirement(params) {
|
|
6878
|
-
const {
|
|
7005
|
+
const {
|
|
7006
|
+
selectedToken,
|
|
7007
|
+
nativeTokenSymbol,
|
|
7008
|
+
amount,
|
|
7009
|
+
balances,
|
|
7010
|
+
reserveFallback
|
|
7011
|
+
} = params;
|
|
6879
7012
|
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
6880
7013
|
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
6881
7014
|
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
@@ -6897,7 +7030,6 @@ class TonChainStrategy {
|
|
|
6897
7030
|
isNativeSelected
|
|
6898
7031
|
};
|
|
6899
7032
|
}
|
|
6900
|
-
// ========== Transaction Execution ==========
|
|
6901
7033
|
validateSteps(steps) {
|
|
6902
7034
|
if (!steps || steps.length === 0) {
|
|
6903
7035
|
throw new InvalidStepsError("ton", "No transaction steps provided");
|
|
@@ -6950,19 +7082,17 @@ class TonChainStrategy {
|
|
|
6950
7082
|
const result = await this.config.tonConnectUI.sendTransaction(
|
|
6951
7083
|
transaction
|
|
6952
7084
|
);
|
|
6953
|
-
const hash = this.getTxHash(result.boc);
|
|
6954
7085
|
return {
|
|
6955
7086
|
chainKey: "ton",
|
|
6956
|
-
hash
|
|
7087
|
+
hash: result.boc
|
|
6957
7088
|
};
|
|
6958
7089
|
} catch (error) {
|
|
6959
7090
|
throw toChainStrategyError(error, "ton", "transaction");
|
|
6960
7091
|
}
|
|
6961
7092
|
}
|
|
6962
|
-
async waitForCompletion(txHash
|
|
7093
|
+
async waitForCompletion(txHash) {
|
|
6963
7094
|
try {
|
|
6964
7095
|
const confirmed = await this.checkTonTransaction(
|
|
6965
|
-
context.srcAddress,
|
|
6966
7096
|
txHash,
|
|
6967
7097
|
TON_CONFIG.timeout
|
|
6968
7098
|
);
|
|
@@ -6992,39 +7122,78 @@ class TonChainStrategy {
|
|
|
6992
7122
|
};
|
|
6993
7123
|
}
|
|
6994
7124
|
}
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7125
|
+
getNormalizedExtMessageHash(message) {
|
|
7126
|
+
if (message.info.type !== "external-in") {
|
|
7127
|
+
throw new Error(`Expected external-in message, got ${message.info.type}`);
|
|
7128
|
+
}
|
|
7129
|
+
const normalizedInfo = {
|
|
7130
|
+
...message.info,
|
|
7131
|
+
src: void 0,
|
|
7132
|
+
importFee: 0n
|
|
7133
|
+
};
|
|
7134
|
+
const normalizedMessage = {
|
|
7135
|
+
...message,
|
|
7136
|
+
info: normalizedInfo,
|
|
7137
|
+
init: null
|
|
7138
|
+
};
|
|
7139
|
+
return core.beginCell().store(core.storeMessage(normalizedMessage, { forceRef: true })).endCell().hash();
|
|
7000
7140
|
}
|
|
7001
|
-
async checkTonTransaction(
|
|
7141
|
+
async checkTonTransaction(bocBase64, timeoutMs = 36e4) {
|
|
7002
7142
|
const deadline = Date.now() + timeoutMs;
|
|
7003
|
-
const
|
|
7004
|
-
|
|
7005
|
-
|
|
7006
|
-
|
|
7007
|
-
|
|
7143
|
+
const client = getTonClient(this.config.tonClient, this.config.tonApiKey);
|
|
7144
|
+
try {
|
|
7145
|
+
const inMessage = core.loadMessage(core.Cell.fromBase64(bocBase64).beginParse());
|
|
7146
|
+
if (inMessage.info.type !== "external-in") {
|
|
7147
|
+
console.debug(
|
|
7148
|
+
"Expected external-in message, got:",
|
|
7149
|
+
inMessage.info.type
|
|
7008
7150
|
);
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
const
|
|
7018
|
-
|
|
7019
|
-
|
|
7151
|
+
return false;
|
|
7152
|
+
}
|
|
7153
|
+
const accountAddress = inMessage.info.dest;
|
|
7154
|
+
const targetMessageHash = this.getNormalizedExtMessageHash(inMessage);
|
|
7155
|
+
let lt = void 0;
|
|
7156
|
+
let hash = void 0;
|
|
7157
|
+
while (Date.now() < deadline) {
|
|
7158
|
+
try {
|
|
7159
|
+
const transactions = await client.getTransactions(accountAddress, {
|
|
7160
|
+
lt,
|
|
7161
|
+
hash,
|
|
7162
|
+
limit: 10,
|
|
7163
|
+
archival: true
|
|
7164
|
+
});
|
|
7165
|
+
if (transactions.length === 0) {
|
|
7166
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
7167
|
+
lt = void 0;
|
|
7168
|
+
hash = void 0;
|
|
7169
|
+
continue;
|
|
7170
|
+
}
|
|
7171
|
+
for (const tx of transactions) {
|
|
7172
|
+
if (tx.inMessage?.info.type === "external-in") {
|
|
7173
|
+
const txInMessageHash = this.getNormalizedExtMessageHash(
|
|
7174
|
+
tx.inMessage
|
|
7175
|
+
);
|
|
7176
|
+
if (txInMessageHash.equals(targetMessageHash)) {
|
|
7177
|
+
console.debug("Transaction found by in-message hash");
|
|
7178
|
+
return true;
|
|
7179
|
+
}
|
|
7180
|
+
}
|
|
7020
7181
|
}
|
|
7182
|
+
const lastTx = transactions[transactions.length - 1];
|
|
7183
|
+
lt = lastTx.lt.toString();
|
|
7184
|
+
hash = lastTx.hash().toString("base64");
|
|
7185
|
+
} catch (error) {
|
|
7186
|
+
console.debug("Error fetching transactions:", error);
|
|
7187
|
+
await new Promise((r) => setTimeout(r, 3e3));
|
|
7188
|
+
lt = void 0;
|
|
7189
|
+
hash = void 0;
|
|
7021
7190
|
}
|
|
7022
|
-
} catch (e) {
|
|
7023
|
-
console.debug("TonCenter polling error:", e);
|
|
7024
7191
|
}
|
|
7025
|
-
|
|
7192
|
+
return false;
|
|
7193
|
+
} catch (error) {
|
|
7194
|
+
console.debug("Error parsing BOC or checking transaction:", error);
|
|
7195
|
+
return false;
|
|
7026
7196
|
}
|
|
7027
|
-
return false;
|
|
7028
7197
|
}
|
|
7029
7198
|
}
|
|
7030
7199
|
class TronChainStrategy {
|
|
@@ -7085,7 +7254,13 @@ class TronChainStrategy {
|
|
|
7085
7254
|
}
|
|
7086
7255
|
// ========== Gas Estimation ==========
|
|
7087
7256
|
async estimateGasRequirement(params) {
|
|
7088
|
-
const {
|
|
7257
|
+
const {
|
|
7258
|
+
selectedToken,
|
|
7259
|
+
nativeTokenSymbol,
|
|
7260
|
+
amount,
|
|
7261
|
+
balances,
|
|
7262
|
+
reserveFallback
|
|
7263
|
+
} = params;
|
|
7089
7264
|
const nativeSym = nativeTokenSymbol.toUpperCase();
|
|
7090
7265
|
const isNativeSelected = nativeSym === (selectedToken?.symbol ?? "").toUpperCase();
|
|
7091
7266
|
const nativeBalance = Number(balances[nativeSym]?.balance ?? 0);
|
|
@@ -7107,7 +7282,6 @@ class TronChainStrategy {
|
|
|
7107
7282
|
isNativeSelected
|
|
7108
7283
|
};
|
|
7109
7284
|
}
|
|
7110
|
-
// ========== Transaction Execution ==========
|
|
7111
7285
|
validateSteps(steps) {
|
|
7112
7286
|
console.log("validateSteps");
|
|
7113
7287
|
if (!steps?.length) {
|
|
@@ -7142,7 +7316,10 @@ class TronChainStrategy {
|
|
|
7142
7316
|
if (String(step.chainKey).toLowerCase() !== "tron") continue;
|
|
7143
7317
|
const tx = step.transaction;
|
|
7144
7318
|
if (!tx) {
|
|
7145
|
-
throw new InvalidTransactionDataError(
|
|
7319
|
+
throw new InvalidTransactionDataError(
|
|
7320
|
+
"tron",
|
|
7321
|
+
"Missing transaction data"
|
|
7322
|
+
);
|
|
7146
7323
|
}
|
|
7147
7324
|
const hexData = typeof tx?.data === "string" ? tx.data : "";
|
|
7148
7325
|
const parsed = this.parseTronStep(step, tx, hexData);
|
|
@@ -7193,13 +7370,19 @@ class TronChainStrategy {
|
|
|
7193
7370
|
break;
|
|
7194
7371
|
}
|
|
7195
7372
|
default:
|
|
7196
|
-
throw new InvalidStepsError(
|
|
7373
|
+
throw new InvalidStepsError(
|
|
7374
|
+
"tron",
|
|
7375
|
+
"Unsupported TRON parsed tx kind"
|
|
7376
|
+
);
|
|
7197
7377
|
}
|
|
7198
7378
|
const { txid } = await this.signAndBroadcast(tronWeb, unsigned);
|
|
7199
7379
|
lastTxId = txid;
|
|
7200
7380
|
}
|
|
7201
7381
|
if (!lastTxId) {
|
|
7202
|
-
throw new TransactionFailedError(
|
|
7382
|
+
throw new TransactionFailedError(
|
|
7383
|
+
"tron",
|
|
7384
|
+
"No TRON transaction was executed"
|
|
7385
|
+
);
|
|
7203
7386
|
}
|
|
7204
7387
|
return { hash: lastTxId, chainKey: "tron" };
|
|
7205
7388
|
}
|
|
@@ -7209,47 +7392,84 @@ class TronChainStrategy {
|
|
|
7209
7392
|
if (!tronWeb) {
|
|
7210
7393
|
throw new ProviderNotAvailableError("tron");
|
|
7211
7394
|
}
|
|
7395
|
+
console.log(
|
|
7396
|
+
`Waiting for ${TRON_CONFIG.requiredConfirmations} confirmations for TRON tx: ${txHash}`
|
|
7397
|
+
);
|
|
7212
7398
|
const deadline = Date.now() + TRON_CONFIG.timeout;
|
|
7213
|
-
|
|
7399
|
+
let txBlockNumber = null;
|
|
7400
|
+
while (Date.now() < deadline && !txBlockNumber) {
|
|
7214
7401
|
try {
|
|
7215
7402
|
const info = await tronWeb.trx.getTransactionInfo(txHash);
|
|
7216
|
-
|
|
7217
|
-
if (info) {
|
|
7403
|
+
if (info && info.blockNumber) {
|
|
7218
7404
|
const result = info.receipt?.result;
|
|
7219
|
-
if (result
|
|
7220
|
-
|
|
7405
|
+
if (result !== "SUCCESS") {
|
|
7406
|
+
const msg = this.hexToAscii(info.resMessage) || null;
|
|
7407
|
+
let reason = msg;
|
|
7408
|
+
const cr = info.contractResult?.[0];
|
|
7409
|
+
if (cr && /^0x?08c379a0/i.test(cr)) {
|
|
7410
|
+
try {
|
|
7411
|
+
const clean = cr.replace(/^0x/, "");
|
|
7412
|
+
const len = parseInt(clean.slice(8 + 64, 8 + 64 + 64), 16);
|
|
7413
|
+
const strHex = clean.slice(
|
|
7414
|
+
8 + 64 + 64,
|
|
7415
|
+
8 + 64 + 64 + len * 2
|
|
7416
|
+
);
|
|
7417
|
+
const str = this.hexToAscii("0x" + strHex);
|
|
7418
|
+
if (str) reason = str;
|
|
7419
|
+
} catch (e) {
|
|
7420
|
+
console.debug("TRON revert string decode error", e);
|
|
7421
|
+
}
|
|
7422
|
+
}
|
|
7423
|
+
const error2 = new TransactionRevertedError(
|
|
7424
|
+
"tron",
|
|
7425
|
+
txHash,
|
|
7426
|
+
reason || "Unknown error"
|
|
7427
|
+
);
|
|
7221
7428
|
return {
|
|
7222
|
-
completed:
|
|
7429
|
+
completed: false,
|
|
7430
|
+
error: error2.message
|
|
7223
7431
|
};
|
|
7224
7432
|
}
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7433
|
+
txBlockNumber = info.blockNumber;
|
|
7434
|
+
console.log(`TRON transaction found in block ${txBlockNumber}`);
|
|
7435
|
+
}
|
|
7436
|
+
} catch (e) {
|
|
7437
|
+
console.debug("TRON getTransactionInfo error:", e);
|
|
7438
|
+
}
|
|
7439
|
+
if (!txBlockNumber) {
|
|
7440
|
+
await new Promise((r) => setTimeout(r, TRON_CONFIG.pollingInterval));
|
|
7441
|
+
}
|
|
7442
|
+
}
|
|
7443
|
+
if (!txBlockNumber) {
|
|
7444
|
+
const error2 = new TransactionTimeoutError("tron", txHash);
|
|
7445
|
+
return {
|
|
7446
|
+
completed: false,
|
|
7447
|
+
error: error2.message
|
|
7448
|
+
};
|
|
7449
|
+
}
|
|
7450
|
+
let confirmations = 0;
|
|
7451
|
+
while (Date.now() < deadline) {
|
|
7452
|
+
try {
|
|
7453
|
+
const currentBlock = await tronWeb.trx.getCurrentBlock();
|
|
7454
|
+
const currentBlockNumber = currentBlock?.block_header?.raw_data?.number;
|
|
7455
|
+
if (currentBlockNumber) {
|
|
7456
|
+
confirmations = currentBlockNumber - txBlockNumber;
|
|
7457
|
+
if (confirmations >= TRON_CONFIG.requiredConfirmations) {
|
|
7458
|
+
console.log(
|
|
7459
|
+
`TRON transaction confirmed in block ${txBlockNumber} with ${confirmations} confirmations`
|
|
7460
|
+
);
|
|
7461
|
+
return {
|
|
7462
|
+
completed: true
|
|
7463
|
+
};
|
|
7238
7464
|
}
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
txHash,
|
|
7242
|
-
reason || "Unknown error"
|
|
7465
|
+
console.log(
|
|
7466
|
+
`TRON transaction confirmations: ${confirmations}/${TRON_CONFIG.requiredConfirmations}`
|
|
7243
7467
|
);
|
|
7244
|
-
return {
|
|
7245
|
-
completed: false,
|
|
7246
|
-
error: error2.message
|
|
7247
|
-
};
|
|
7248
7468
|
}
|
|
7249
7469
|
} catch (e) {
|
|
7250
|
-
console.debug("TRON
|
|
7470
|
+
console.debug("TRON getCurrentBlock error:", e);
|
|
7251
7471
|
}
|
|
7252
|
-
await new Promise((r) => setTimeout(r,
|
|
7472
|
+
await new Promise((r) => setTimeout(r, TRON_CONFIG.pollingInterval));
|
|
7253
7473
|
}
|
|
7254
7474
|
const error = new TransactionTimeoutError("tron", txHash);
|
|
7255
7475
|
return {
|
|
@@ -7257,14 +7477,17 @@ class TronChainStrategy {
|
|
|
7257
7477
|
error: error.message
|
|
7258
7478
|
};
|
|
7259
7479
|
} catch (error) {
|
|
7260
|
-
const chainError = toChainStrategyError(
|
|
7480
|
+
const chainError = toChainStrategyError(
|
|
7481
|
+
error,
|
|
7482
|
+
"tron",
|
|
7483
|
+
"waitForCompletion"
|
|
7484
|
+
);
|
|
7261
7485
|
return {
|
|
7262
7486
|
completed: false,
|
|
7263
7487
|
error: chainError.message
|
|
7264
7488
|
};
|
|
7265
7489
|
}
|
|
7266
7490
|
}
|
|
7267
|
-
// ========== Private Helper Methods ==========
|
|
7268
7491
|
getTronWeb() {
|
|
7269
7492
|
return typeof window !== "undefined" ? window.tronWeb : void 0;
|
|
7270
7493
|
}
|
|
@@ -7317,7 +7540,10 @@ class TronChainStrategy {
|
|
|
7317
7540
|
amount: this.extractAmountFromTxData(tx.data)
|
|
7318
7541
|
};
|
|
7319
7542
|
}
|
|
7320
|
-
throw new InvalidTransactionDataError(
|
|
7543
|
+
throw new InvalidTransactionDataError(
|
|
7544
|
+
"tron",
|
|
7545
|
+
"Cannot parse approve transaction"
|
|
7546
|
+
);
|
|
7321
7547
|
}
|
|
7322
7548
|
if (step?.type === "transfer" || step?.type === "bridge") {
|
|
7323
7549
|
const s = step;
|
|
@@ -7347,7 +7573,10 @@ class TronChainStrategy {
|
|
|
7347
7573
|
feeLimit: raw2.feeLimit
|
|
7348
7574
|
};
|
|
7349
7575
|
}
|
|
7350
|
-
throw new InvalidTransactionDataError(
|
|
7576
|
+
throw new InvalidTransactionDataError(
|
|
7577
|
+
"tron",
|
|
7578
|
+
"Cannot parse transfer/bridge transaction"
|
|
7579
|
+
);
|
|
7351
7580
|
}
|
|
7352
7581
|
if (tx?.to && tx?.value && !tx?.data) {
|
|
7353
7582
|
const v = BigInt(tx.value.toString());
|
|
@@ -7367,19 +7596,28 @@ class TronChainStrategy {
|
|
|
7367
7596
|
feeLimit: raw.feeLimit
|
|
7368
7597
|
};
|
|
7369
7598
|
}
|
|
7370
|
-
throw new InvalidTransactionDataError(
|
|
7599
|
+
throw new InvalidTransactionDataError(
|
|
7600
|
+
"tron",
|
|
7601
|
+
`Unsupported TRON step type: ${step?.type ?? "unknown"}`
|
|
7602
|
+
);
|
|
7371
7603
|
}
|
|
7372
7604
|
extractSpenderFromTxData(data) {
|
|
7373
7605
|
const clean = data.replace(/^0x/, "");
|
|
7374
7606
|
if (clean.length < 74) {
|
|
7375
|
-
throw new InvalidTransactionDataError(
|
|
7607
|
+
throw new InvalidTransactionDataError(
|
|
7608
|
+
"tron",
|
|
7609
|
+
"Invalid transaction data length"
|
|
7610
|
+
);
|
|
7376
7611
|
}
|
|
7377
7612
|
return "41" + clean.slice(32, 72);
|
|
7378
7613
|
}
|
|
7379
7614
|
extractAmountFromTxData(data) {
|
|
7380
7615
|
const clean = data.replace(/^0x/, "");
|
|
7381
7616
|
if (clean.length < 138) {
|
|
7382
|
-
throw new InvalidTransactionDataError(
|
|
7617
|
+
throw new InvalidTransactionDataError(
|
|
7618
|
+
"tron",
|
|
7619
|
+
"Invalid transaction data length"
|
|
7620
|
+
);
|
|
7383
7621
|
}
|
|
7384
7622
|
const amountHex = clean.slice(72, 136);
|
|
7385
7623
|
return BigInt("0x" + amountHex).toString();
|
|
@@ -7387,7 +7625,10 @@ class TronChainStrategy {
|
|
|
7387
7625
|
extractRecipientFromTxData(data) {
|
|
7388
7626
|
const clean = data.replace(/^0x/, "");
|
|
7389
7627
|
if (clean.length < 74) {
|
|
7390
|
-
throw new InvalidTransactionDataError(
|
|
7628
|
+
throw new InvalidTransactionDataError(
|
|
7629
|
+
"tron",
|
|
7630
|
+
"Invalid transaction data length"
|
|
7631
|
+
);
|
|
7391
7632
|
}
|
|
7392
7633
|
return "41" + clean.slice(32, 72);
|
|
7393
7634
|
}
|
|
@@ -7462,7 +7703,6 @@ class TronChainStrategy {
|
|
|
7462
7703
|
return null;
|
|
7463
7704
|
}
|
|
7464
7705
|
}
|
|
7465
|
-
// ========== Transaction Builders ==========
|
|
7466
7706
|
async buildApprove(tronWeb, p) {
|
|
7467
7707
|
const res = await tronWeb.transactionBuilder.triggerSmartContract(
|
|
7468
7708
|
p.token,
|
|
@@ -7475,7 +7715,10 @@ class TronChainStrategy {
|
|
|
7475
7715
|
p.from
|
|
7476
7716
|
);
|
|
7477
7717
|
if (!res?.transaction) {
|
|
7478
|
-
throw new TransactionFailedError(
|
|
7718
|
+
throw new TransactionFailedError(
|
|
7719
|
+
"tron",
|
|
7720
|
+
"Failed to build approve transaction"
|
|
7721
|
+
);
|
|
7479
7722
|
}
|
|
7480
7723
|
return res.transaction;
|
|
7481
7724
|
}
|
|
@@ -7491,7 +7734,10 @@ class TronChainStrategy {
|
|
|
7491
7734
|
p.from
|
|
7492
7735
|
);
|
|
7493
7736
|
if (!res?.transaction) {
|
|
7494
|
-
throw new TransactionFailedError(
|
|
7737
|
+
throw new TransactionFailedError(
|
|
7738
|
+
"tron",
|
|
7739
|
+
"Failed to build transfer transaction"
|
|
7740
|
+
);
|
|
7495
7741
|
}
|
|
7496
7742
|
return res.transaction;
|
|
7497
7743
|
}
|
|
@@ -7511,12 +7757,13 @@ class TronChainStrategy {
|
|
|
7511
7757
|
p.from
|
|
7512
7758
|
);
|
|
7513
7759
|
if (!res?.transaction) {
|
|
7514
|
-
throw new TransactionFailedError(
|
|
7760
|
+
throw new TransactionFailedError(
|
|
7761
|
+
"tron",
|
|
7762
|
+
"Failed to build raw call transaction"
|
|
7763
|
+
);
|
|
7515
7764
|
}
|
|
7516
7765
|
return res.transaction;
|
|
7517
7766
|
}
|
|
7518
|
-
// ========== Transaction Sender ==========
|
|
7519
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
7520
7767
|
async signAndBroadcast(tronWeb, unsignedTx) {
|
|
7521
7768
|
const signed = await tronWeb.trx.sign(unsignedTx);
|
|
7522
7769
|
const sent = await tronWeb.trx.sendRawTransaction(signed);
|
|
@@ -7528,33 +7775,77 @@ class TronChainStrategy {
|
|
|
7528
7775
|
}
|
|
7529
7776
|
return { txid: sent.txid };
|
|
7530
7777
|
}
|
|
7778
|
+
/**
|
|
7779
|
+
* Check if a transaction has been solidified (confirmed by solidityNode)
|
|
7780
|
+
* This is an additional check for critical transactions to ensure they are
|
|
7781
|
+
* truly confirmed and available through the solidityNode API.
|
|
7782
|
+
*
|
|
7783
|
+
* Usage: Call this method after waitForCompletion for high-value transactions
|
|
7784
|
+
* to get additional assurance that the transaction is solidified.
|
|
7785
|
+
*
|
|
7786
|
+
* @param txHash - Transaction hash to check
|
|
7787
|
+
* @returns true if the transaction is solidified, false otherwise
|
|
7788
|
+
*/
|
|
7789
|
+
async checkSolidified(txHash) {
|
|
7790
|
+
try {
|
|
7791
|
+
const tronWeb = this.getTronWeb();
|
|
7792
|
+
if (!tronWeb) {
|
|
7793
|
+
return false;
|
|
7794
|
+
}
|
|
7795
|
+
const info = await tronWeb.trx.getTransactionInfo(txHash);
|
|
7796
|
+
if (!info || !info.blockNumber) {
|
|
7797
|
+
console.debug(
|
|
7798
|
+
"Transaction not yet solidified (no blockNumber in info)"
|
|
7799
|
+
);
|
|
7800
|
+
return false;
|
|
7801
|
+
}
|
|
7802
|
+
const result = info.receipt?.result;
|
|
7803
|
+
if (result === "SUCCESS") {
|
|
7804
|
+
console.log(
|
|
7805
|
+
`Transaction ${txHash} is solidified in block ${info.blockNumber}`
|
|
7806
|
+
);
|
|
7807
|
+
return true;
|
|
7808
|
+
}
|
|
7809
|
+
console.debug(`Transaction solidified but result is: ${result}`);
|
|
7810
|
+
return false;
|
|
7811
|
+
} catch (error) {
|
|
7812
|
+
console.debug("Error checking solidified status:", error);
|
|
7813
|
+
return false;
|
|
7814
|
+
}
|
|
7815
|
+
}
|
|
7531
7816
|
}
|
|
7532
7817
|
function ChainStrategyProvider({
|
|
7533
7818
|
children,
|
|
7534
7819
|
evmWallet,
|
|
7535
7820
|
tonWallet,
|
|
7536
|
-
tronWallet
|
|
7821
|
+
tronWallet,
|
|
7822
|
+
tonClient,
|
|
7823
|
+
tonApiKey
|
|
7537
7824
|
}) {
|
|
7538
7825
|
const evmStrategy = require$$0.useMemo(
|
|
7539
7826
|
() => new EvmChainStrategy({
|
|
7540
7827
|
evmAddress: evmWallet.address,
|
|
7541
7828
|
evmIsConnected: evmWallet.isConnected,
|
|
7542
7829
|
evmDisconnect: evmWallet.disconnect,
|
|
7543
|
-
walletClient: evmWallet.walletClient
|
|
7830
|
+
walletClient: evmWallet.walletClient,
|
|
7831
|
+
publicClient: evmWallet.publicClient
|
|
7544
7832
|
}),
|
|
7545
7833
|
[
|
|
7546
7834
|
evmWallet.address,
|
|
7547
7835
|
evmWallet.isConnected,
|
|
7548
7836
|
evmWallet.disconnect,
|
|
7549
|
-
evmWallet.walletClient
|
|
7837
|
+
evmWallet.walletClient,
|
|
7838
|
+
evmWallet.publicClient
|
|
7550
7839
|
]
|
|
7551
7840
|
);
|
|
7552
7841
|
const tonStrategy = require$$0.useMemo(
|
|
7553
7842
|
() => new TonChainStrategy({
|
|
7554
7843
|
tonConnectUI: tonWallet.tonConnectUI,
|
|
7555
|
-
tonAddress: tonWallet.address
|
|
7844
|
+
tonAddress: tonWallet.address,
|
|
7845
|
+
tonClient,
|
|
7846
|
+
tonApiKey
|
|
7556
7847
|
}),
|
|
7557
|
-
[tonWallet.tonConnectUI, tonWallet.address]
|
|
7848
|
+
[tonWallet.tonConnectUI, tonWallet.address, tonClient, tonApiKey]
|
|
7558
7849
|
);
|
|
7559
7850
|
const tronStrategy = require$$0.useMemo(
|
|
7560
7851
|
() => new TronChainStrategy({
|
|
@@ -7576,9 +7867,12 @@ function ChainStrategyProvider({
|
|
|
7576
7867
|
() => new ChainStrategyRegistry([evmStrategy, tonStrategy, tronStrategy]),
|
|
7577
7868
|
[evmStrategy, tonStrategy, tronStrategy]
|
|
7578
7869
|
);
|
|
7579
|
-
const value =
|
|
7580
|
-
|
|
7581
|
-
|
|
7870
|
+
const value = require$$0.useMemo(
|
|
7871
|
+
() => ({
|
|
7872
|
+
chainRegistry
|
|
7873
|
+
}),
|
|
7874
|
+
[chainRegistry]
|
|
7875
|
+
);
|
|
7582
7876
|
return /* @__PURE__ */ jsxRuntime.jsx(ChainStrategyContext.Provider, { value, children });
|
|
7583
7877
|
}
|
|
7584
7878
|
const EvaaBridgeWithProviders = (props) => {
|
|
@@ -7587,6 +7881,7 @@ const EvaaBridgeWithProviders = (props) => {
|
|
|
7587
7881
|
const { address: evmAddress, isConnected: evmIsConnected } = wagmi.useAccount();
|
|
7588
7882
|
const { disconnect: evmDisconnect } = wagmi.useDisconnect();
|
|
7589
7883
|
const { data: walletClient } = wagmi.useWalletClient();
|
|
7884
|
+
const publicClient = wagmi.usePublicClient();
|
|
7590
7885
|
const {
|
|
7591
7886
|
address: tronAddress,
|
|
7592
7887
|
connected: tronConnected,
|
|
@@ -7611,7 +7906,8 @@ const EvaaBridgeWithProviders = (props) => {
|
|
|
7611
7906
|
address: evmAddress,
|
|
7612
7907
|
isConnected: evmIsConnected,
|
|
7613
7908
|
disconnect: evmDisconnect,
|
|
7614
|
-
walletClient
|
|
7909
|
+
walletClient,
|
|
7910
|
+
publicClient
|
|
7615
7911
|
},
|
|
7616
7912
|
tonWallet: {
|
|
7617
7913
|
tonConnectUI,
|
|
@@ -7624,6 +7920,8 @@ const EvaaBridgeWithProviders = (props) => {
|
|
|
7624
7920
|
connect: tronConnect,
|
|
7625
7921
|
disconnect: tronDisconnect
|
|
7626
7922
|
},
|
|
7923
|
+
tonClient: props.tonClient,
|
|
7924
|
+
tonApiKey: props.tonApiKey,
|
|
7627
7925
|
children: /* @__PURE__ */ jsxRuntime.jsx(EvaaBridgeContent, { ...props })
|
|
7628
7926
|
}
|
|
7629
7927
|
);
|
|
@@ -7728,16 +8026,16 @@ const EvaaBridgeContent = ({
|
|
|
7728
8026
|
}, [chains, assetMatrix, allowedFromChains]);
|
|
7729
8027
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7730
8028
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
7731
|
-
|
|
8029
|
+
Card,
|
|
7732
8030
|
{
|
|
7733
8031
|
className: cn(
|
|
7734
|
-
"
|
|
8032
|
+
"max-w-md w-full mx-auto flex flex-col relative",
|
|
7735
8033
|
className
|
|
7736
8034
|
),
|
|
7737
8035
|
ref: modalContainerRef,
|
|
7738
8036
|
children: [
|
|
7739
8037
|
/* @__PURE__ */ jsxRuntime.jsx(FormHeader, { modalContainer: modalContainerRef.current }),
|
|
7740
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
8038
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "space-y-[1px]", children: [
|
|
7741
8039
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7742
8040
|
SwapSection,
|
|
7743
8041
|
{
|
|
@@ -7775,10 +8073,10 @@ const EvaaBridgeContent = ({
|
|
|
7775
8073
|
enabled: sendToAnother,
|
|
7776
8074
|
onToggle: () => setSendToAnother((v) => !v)
|
|
7777
8075
|
}
|
|
7778
|
-
)
|
|
8076
|
+
),
|
|
8077
|
+
/* @__PURE__ */ jsxRuntime.jsx(SubmitButton, {})
|
|
7779
8078
|
] }),
|
|
7780
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
7781
|
-
/* @__PURE__ */ jsxRuntime.jsx(ReceiveRow, {})
|
|
8079
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardFooter, { children: /* @__PURE__ */ jsxRuntime.jsx(ReceiveRow, {}) })
|
|
7782
8080
|
]
|
|
7783
8081
|
}
|
|
7784
8082
|
),
|
|
@@ -7866,8 +8164,8 @@ exports.getQuoteAmounts = getQuoteAmounts;
|
|
|
7866
8164
|
exports.getQuoteDetails = getQuoteDetails;
|
|
7867
8165
|
exports.getQuoteFees = getQuoteFees;
|
|
7868
8166
|
exports.getQuotesByPriority = getQuotesByPriority;
|
|
7869
|
-
exports.getSwapBalances = getSwapBalances;
|
|
7870
8167
|
exports.getTokens = getTokens;
|
|
8168
|
+
exports.getTonBalances = getTonBalances;
|
|
7871
8169
|
exports.getTronBalances = getTronBalances;
|
|
7872
8170
|
exports.isAddressValidForChain = isAddressValidForChain;
|
|
7873
8171
|
exports.isEvmAddress = isEvmAddress;
|
|
@@ -7876,7 +8174,7 @@ exports.isTronAddress = isTronAddress;
|
|
|
7876
8174
|
exports.isZeroAddr = isZeroAddr;
|
|
7877
8175
|
exports.listAssetsForSelect = listAssetsForSelect;
|
|
7878
8176
|
exports.lookupTokenMeta = lookupTokenMeta;
|
|
7879
|
-
exports.normalizeTickerSymbol = normalizeTickerSymbol;
|
|
8177
|
+
exports.normalizeTickerSymbol = normalizeTickerSymbol$1;
|
|
7880
8178
|
exports.pollUntilDelivered = pollUntilDelivered;
|
|
7881
8179
|
exports.resolveTokenOnChain = resolveTokenOnChain;
|
|
7882
8180
|
exports.resolveTokenOnChainFromMatrix = resolveTokenOnChainFromMatrix$2;
|