Compare commits

..

No commits in common. "main" and "extension-integration" have entirely different histories.

48 changed files with 1011 additions and 2770 deletions

View File

@ -1,7 +1,7 @@
{ {
"name": "ghost-dao-interface", "name": "ghost-dao-interface",
"private": true, "private": true,
"version": "0.4.0", "version": "0.2.15",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@ -14,7 +14,7 @@ import Sidebar from "./components/Sidebar/Sidebar";
import TopBar from "./components/TopBar/TopBar"; import TopBar from "./components/TopBar/TopBar";
import { shouldTriggerSafetyCheck } from "./helpers"; import { shouldTriggerSafetyCheck } from "./helpers";
import { isNetworkAvailable, isNetworkLegacy } from "./constants"; import { isNetworkAvailable } from "./constants";
import useTheme from "./hooks/useTheme"; import useTheme from "./hooks/useTheme";
import { useUnstableProvider } from "./hooks/ghost"; import { useUnstableProvider } from "./hooks/ghost";
import { dark as darkTheme } from "./themes/dark.js"; import { dark as darkTheme } from "./themes/dark.js";
@ -27,7 +27,6 @@ const BondModalContainer = lazy(() => import("./containers/Bond/BondModal"));
const StakeContainer = lazy(() => import("./containers/Stake/StakeContainer")); const StakeContainer = lazy(() => import("./containers/Stake/StakeContainer"));
const TreasuryDashboard = lazy(() => import("./containers/TreasuryDashboard/TreasuryDashboard")); const TreasuryDashboard = lazy(() => import("./containers/TreasuryDashboard/TreasuryDashboard"));
const Faucet = lazy(() => import("./containers/Faucet/Faucet")); const Faucet = lazy(() => import("./containers/Faucet/Faucet"));
const Wrapper = lazy(() => import("./containers/WethWrapper/WethWrapper"));
const Dex = lazy(() => import("./containers/Dex/Dex")); const Dex = lazy(() => import("./containers/Dex/Dex"));
const Bridge = lazy(() => import("./containers/Bridge/Bridge")); const Bridge = lazy(() => import("./containers/Bridge/Bridge"));
const NotFound = lazy(() => import("./containers/NotFound/NotFound")); const NotFound = lazy(() => import("./containers/NotFound/NotFound"));
@ -207,10 +206,7 @@ function App() {
<Route path="/bonds" element={<Bonds connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} /> <Route path="/bonds" element={<Bonds connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="/bonds/:id" element={<BondModalContainer connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} /> <Route path="/bonds/:id" element={<BondModalContainer connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="/stake" element={<StakeContainer connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId}/>} /> <Route path="/stake" element={<StakeContainer connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId}/>} />
{isNetworkLegacy(chainId) <Route path="/faucet" element={<Faucet config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
? <Route path="/faucet" element={<Faucet config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
: <Route path="/wrapper" element={<Wrapper config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
}
<Route path="/bridge" element={<Bridge config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} /> <Route path="/bridge" element={<Bridge config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="/dex/:name" element={<Dex connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} /> <Route path="/dex/:name" element={<Dex connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
</> </>

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
{"abi":[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"guy","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":true,"internalType":"address","name":"dst","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"src","type":"address"},{"indexed":false,"internalType":"uint256","name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guy","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deposit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"src","type":"address"},{"internalType":"address","name":"dst","type":"address"},{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]}

View File

@ -1,36 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="uuid-61b3c585-44d6-4fd8-a0e7-6a5e76478eb1" data-name="uuid-70ba15fb-c44e-4945-a02a-8d07f94a9abb" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 505 505">
<defs>
<style>
.uuid-6437a6f0-42e0-4c9c-b0ef-b632253e989a {
fill: #fff;
}
.uuid-f6980b42-338a-476b-b4b3-c6f8474bfe6a {
fill: #0b8311;
}
.uuid-cee5d69f-ecfa-41d4-b22b-62a3555cf216 {
fill: #3ab83a;
}
.uuid-8c3bab1c-21e8-4834-aebc-42a28aaec68e {
fill: #146714;
}
</style>
</defs>
<g fill="#222" data-name="uuid-3af91251-9afa-43cd-b331-1e6ceef8228b">
<g id="uuid-38146a20-6896-4919-8709-3b8aa9b9b9de" data-name="uuid-bca144dc-fff0-427e-b9b1-33b0d4491804">
<g id="uuid-01777163-9cb4-4f63-8018-462f63cda4ae" data-name="uuid-d1a1e453-da74-40e2-920d-7662cd9352f5">
<path d="m245.2,485.4c-64.14,0-124.44-24.99-169.8-70.36C30.05,369.68,5.05,309.36,5,245.2c0-64.15,24.99-124.46,70.36-169.84C120.73,29.98,181.05,5,245.2,5s124.47,24.99,169.84,70.36c45.37,45.37,70.36,105.69,70.36,169.84s-24.99,124.47-70.36,169.84-105.69,70.36-169.84,70.36h0Z"/>
<path class="uuid-6437a6f0-42e0-4c9c-b0ef-b632253e989a" d="m245.2,10c31.75,0,62.55,6.22,91.54,18.48,28.01,11.85,53.16,28.81,74.77,50.41,21.61,21.61,38.57,46.76,50.41,74.77,12.26,28.99,18.48,59.79,18.48,91.54s-6.22,62.55-18.48,91.54c-11.85,28.01-28.81,53.16-50.41,74.77-21.61,21.61-46.76,38.57-74.77,50.41-28.99,12.26-59.79,18.48-91.54,18.48s-62.54-6.22-91.52-18.48c-28-11.85-53.14-28.81-74.74-50.41s-38.56-46.76-50.41-74.77c-12.27-28.99-18.5-59.79-18.52-91.54,0-31.75,6.22-62.55,18.48-91.54,11.85-28.01,28.81-53.16,50.41-74.77,21.61-21.61,46.76-38.57,74.77-50.41,28.99-12.26,59.79-18.48,91.54-18.48M245.21,0C109.8,0,0,109.8,0,245.2c.1,135.4,109.8,245.2,245.2,245.2s245.2-109.8,245.2-245.2S380.6,0,245.2,0h0Z"/>
</g>
<g id="uuid-91c9d373-1cb9-4018-9a6a-66437f262f1c" data-name="uuid-863b3aee-71cf-488d-8b5a-a2d97fac93c4">
<path id="uuid-538e9787-13a3-4271-8587-11f8527f9a71" data-name="uuid-10bd56be-0db7-4e24-bb20-a24d1c3cbcb7" class="uuid-cee5d69f-ecfa-41d4-b22b-62a3555cf216" d="m144.29,259.73c35.56,18.89,72.67,38.65,101.33,53.95l100.5-53.95c-36.39,54.06-66.71,99.06-100.5,148.87-33.85-49.7-71.23-104.54-101.33-148.87Zm3.87-14.91l97.57-52.07,96.3,51.69-96.25,52.12-97.63-51.74h.01Zm97.46-68.75l-101.33,53.34,100.89-147.71,100.94,148.04-100.5-53.67h0Z"/>
<path id="uuid-4aa91baa-53f1-4f63-abbe-cf753f8525a0" data-name="uuid-4c5fd78b-71fb-4d56-83d4-a2022fef9f50" class="uuid-f6980b42-338a-476b-b4b3-c6f8474bfe6a" d="m245.61,313.68l100.5-53.95c-36.39,54.06-100.5,148.87-100.5,148.87v-94.92h0Zm.11-120.93l96.3,51.69-96.25,52.12-.05-103.81h0Zm-.11-16.68l-.44-94.37,100.94,148.04-100.5-53.67h0Z"/>
<path id="uuid-3febb173-485e-4b4e-92b6-b05cc7b0412b" data-name="uuid-3f7cae2c-d7bb-44d6-a6e9-cd80adc2aeac" class="uuid-f6980b42-338a-476b-b4b3-c6f8474bfe6a" d="m148.15,244.82l97.58,8.01,96.3-8.34-96.25,52.13-97.63-51.8h0Z"/>
<path id="uuid-df565d0e-a3cf-452f-80f6-2e6ea5ca4f56" data-name="uuid-e69b7837-4cf3-4793-a865-a26ad4121390" class="uuid-8c3bab1c-21e8-4834-aebc-42a28aaec68e" d="m245.72,252.83l96.3-8.34-96.25,52.13-.05-43.79h0Z"/>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -1,36 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?> <svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2500" viewBox="0 0 32 32">
<svg id="uuid-39ebc51d-db02-45d2-ad4b-e7ae31bb915b" data-name="Ethereum" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 250"> <g fill="none" fill-rule="evenodd">
<defs> <circle cx="16" cy="16" r="16" fill="#627EEA"/>
<style> <g fill="#FFF" fill-rule="nonzero">
.uuid-e080659c-91a4-4965-9905-603394020b2c { <path fill-opacity=".602" d="M16.498 4v8.87l7.497 3.35z"/>
fill: #fff; <path d="M16.498 4L9 16.22l7.498-3.35z"/>
} <path fill-opacity=".602" d="M16.498 21.968v6.027L24 17.616z"/>
<path d="M16.498 27.995v-6.028L9 17.616z"/>
.uuid-ca9afedd-40fd-4006-99f9-d2c3cf46bc21 { <path fill-opacity=".2" d="M16.498 20.573l7.497-4.353-7.497-3.348z"/>
fill: #627eea; <path fill-opacity=".602" d="M9 16.22l7.498 4.353v-7.701z"/>
}
.uuid-bb37a35d-b2ce-440e-86f6-0142a60ec718 {
fill: rgba(255, 255, 255, .2);
}
.uuid-933ff958-c1b5-4023-94a9-0cca9b4aab50 {
fill: rgba(255, 255, 255, .6);
}
</style>
</defs>
<g id="uuid-c49657cc-831e-4816-83c0-b3ceca5eead0" data-name="Ethereum">
<g id="uuid-559a2235-ac8a-4942-be49-ac3193ebd3cd" data-name="EthereumBackground">
<circle class="uuid-ca9afedd-40fd-4006-99f9-d2c3cf46bc21" cx="125" cy="125" r="122.5"/>
<path class="uuid-e080659c-91a4-4965-9905-603394020b2c" d="m125,5c32.05,0,62.19,12.48,84.85,35.15,22.67,22.66,35.15,52.8,35.15,84.85s-12.48,62.19-35.15,84.85-52.8,35.15-84.85,35.15-62.19-12.48-84.85-35.15C17.48,187.19,5,157.05,5,125s12.48-62.19,35.15-84.85C62.81,17.48,92.95,5,125,5m0-5C55.96,0,0,55.96,0,125s55.96,125,125,125,125-55.96,125-125S194.04,0,125,0h0Z"/>
</g>
<g id="uuid-0bd28738-f2c4-4934-8365-8668de7b64ac" data-name="EthereumIcon">
<path class="uuid-933ff958-c1b5-4023-94a9-0cca9b4aab50" d="m128.89,31.25v69.3l58.57,26.17-58.57-95.47Z"/>
<path class="uuid-e080659c-91a4-4965-9905-603394020b2c" d="m128.89,31.25l-58.58,95.47,58.58-26.17V31.25Z"/>
<path class="uuid-933ff958-c1b5-4023-94a9-0cca9b4aab50" d="m128.89,171.62v47.09l58.61-81.09-58.61,34Z"/>
<path class="uuid-e080659c-91a4-4965-9905-603394020b2c" d="m128.89,218.71v-47.09l-58.58-33.99,58.58,81.09Z"/>
<path class="uuid-bb37a35d-b2ce-440e-86f6-0142a60ec718" d="m128.89,160.73l58.57-34.01-58.57-26.16v60.16Z"/>
<path class="uuid-933ff958-c1b5-4023-94a9-0cca9b4aab50" d="m70.31,126.72l58.58,34.01v-60.16l-58.58,26.16Z"/>
</g> </g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 617 B

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="uuid-39ebc51d-db02-45d2-ad4b-e7ae31bb915b" data-name="Ethereum" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 250">
<defs>
<style>
.uuid-e080659c-91a4-4965-9905-603394020b2c {
fill: #fff;
}
.uuid-ca9afedd-40fd-4006-99f9-d2c3cf46bc21 {
fill: #627eea;
}
.uuid-bb37a35d-b2ce-440e-86f6-0142a60ec718 {
fill: rgba(255, 255, 255, .2);
}
.uuid-933ff958-c1b5-4023-94a9-0cca9b4aab50 {
fill: rgba(255, 255, 255, .6);
}
</style>
</defs>
<g id="uuid-c49657cc-831e-4816-83c0-b3ceca5eead0" data-name="Ethereum">
<g id="uuid-559a2235-ac8a-4942-be49-ac3193ebd3cd" data-name="EthereumBackground">
<circle class="uuid-ca9afedd-40fd-4006-99f9-d2c3cf46bc21" cx="125" cy="125" r="122.5"/>
<path class="uuid-e080659c-91a4-4965-9905-603394020b2c" d="m125,5c32.05,0,62.19,12.48,84.85,35.15,22.67,22.66,35.15,52.8,35.15,84.85s-12.48,62.19-35.15,84.85-52.8,35.15-84.85,35.15-62.19-12.48-84.85-35.15C17.48,187.19,5,157.05,5,125s12.48-62.19,35.15-84.85C62.81,17.48,92.95,5,125,5m0-5C55.96,0,0,55.96,0,125s55.96,125,125,125,125-55.96,125-125S194.04,0,125,0h0Z"/>
</g>
<g id="uuid-0bd28738-f2c4-4934-8365-8668de7b64ac" data-name="EthereumIcon">
<path class="uuid-933ff958-c1b5-4023-94a9-0cca9b4aab50" d="m128.89,31.25v69.3l58.57,26.17-58.57-95.47Z"/>
<path class="uuid-e080659c-91a4-4965-9905-603394020b2c" d="m128.89,31.25l-58.58,95.47,58.58-26.17V31.25Z"/>
<path class="uuid-933ff958-c1b5-4023-94a9-0cca9b4aab50" d="m128.89,171.62v47.09l58.61-81.09-58.61,34Z"/>
<path class="uuid-e080659c-91a4-4965-9905-603394020b2c" d="m128.89,218.71v-47.09l-58.58-33.99,58.58,81.09Z"/>
<path class="uuid-bb37a35d-b2ce-440e-86f6-0142a60ec718" d="m128.89,160.73l58.57-34.01-58.57-26.16v60.16Z"/>
<path class="uuid-933ff958-c1b5-4023-94a9-0cca9b4aab50" d="m70.31,126.72l58.58,34.01v-60.16l-58.58,26.16Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -37,7 +37,7 @@ import BondIcon from "../Icon/BondIcon";
import StakeIcon from "../Icon/StakeIcon"; import StakeIcon from "../Icon/StakeIcon";
import WrapIcon from "../Icon/WrapIcon"; import WrapIcon from "../Icon/WrapIcon";
import { isNetworkAvailable, isNetworkLegacy } from "../../constants"; import { isNetworkAvailable } from "../../constants";
import { AVAILABLE_DEXES } from "../../constants/dexes"; import { AVAILABLE_DEXES } from "../../constants/dexes";
import { ECOSYSTEM } from "../../constants/ecosystem"; import { ECOSYSTEM } from "../../constants/ecosystem";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
@ -46,7 +46,6 @@ import BondDiscount from "../../containers/Bond/components/BondDiscount";
import DashboardIcon from '@mui/icons-material/Dashboard'; import DashboardIcon from '@mui/icons-material/Dashboard';
import ShowerIcon from '@mui/icons-material/Shower'; import ShowerIcon from '@mui/icons-material/Shower';
import WifiProtectedSetupIcon from '@mui/icons-material/WifiProtectedSetup';
import { useTokenSymbol } from "../../hooks/tokens"; import { useTokenSymbol } from "../../hooks/tokens";
import { useFtsoPrice, useGhstPrice, useGhostedSupplyPrice } from "../../hooks/prices"; import { useFtsoPrice, useGhstPrice, useGhostedSupplyPrice } from "../../hooks/prices";
@ -112,10 +111,6 @@ const NavContent = ({ chainId, addressChainId }) => {
{isNetworkAvailable(chainId, addressChainId) && {isNetworkAvailable(chainId, addressChainId) &&
<> <>
<NavItem icon={DashboardIcon} label={`Dashboard`} to="/dashboard" /> <NavItem icon={DashboardIcon} label={`Dashboard`} to="/dashboard" />
{isNetworkLegacy(chainId)
? <NavItem icon={ShowerIcon} label={`Faucet`} to="/faucet" />
: <NavItem icon={WifiProtectedSetupIcon} label={`Wrapper`} to="/wrapper" />
}
<NavItem <NavItem
defaultExpanded defaultExpanded
icon={BondIcon} icon={BondIcon}
@ -152,6 +147,9 @@ const NavContent = ({ chainId, addressChainId }) => {
</AccordionDetails> </AccordionDetails>
} }
/> />
<NavItem icon={StakeIcon} label={`Stake`} to="/stake" />
<NavItem icon={ShowerIcon} label={`Faucet`} to="/faucet" />
<NavItem icon={PublicIcon} label={`Bridge`} to="/bridge" />
<NavItem <NavItem
icon={CurrencyExchangeIcon} icon={CurrencyExchangeIcon}
label={`Dex`} label={`Dex`}
@ -176,8 +174,6 @@ const NavContent = ({ chainId, addressChainId }) => {
</AccordionDetails> </AccordionDetails>
} }
/> />
<NavItem icon={StakeIcon} label={`Stake`} to="/stake" />
<NavItem icon={PublicIcon} label={`Bridge`} to="/bridge" />
<Box className="menu-divider"> <Box className="menu-divider">
<Divider /> <Divider />
</Box> </Box>

View File

@ -90,7 +90,7 @@ const SwapCard = ({
</Box> </Box>
)} )}
<Box display="flex" flexDirection="row" marginTop="12px" justifyContent="space-between" alignItems="center"> <Box display="flex" flexDirection="row" marginTop="12px" justifyContent="space-between" alignItems="center">
<Box display="flex" flexDirection="row" alignItems="center" width={!info && !endString && !usdValue ? "100%" : inputWidth ? inputWidth : "136px"}> <Box display="flex" flexDirection="row" alignItems="center">
<StyledInputBase <StyledInputBase
id={id} id={id}
sx={{ sx={{
@ -99,7 +99,6 @@ const SwapCard = ({
padding: 0, padding: 0,
height: "24px", height: "24px",
maxWidth: inputWidth || "136px", maxWidth: inputWidth || "136px",
width: !info && !endString && !usdValue ? "100%" : inputWidth ? inputWidth : "136px",
}} }}
placeholder={placeholder} placeholder={placeholder}
type={inputType} type={inputType}

View File

@ -28,11 +28,11 @@ const StyledArrow = styled(Box)(
}, },
); );
const SwapCollection = ({ UpperSwapCard, LowerSwapCard, arrowOnClick, iconNotNeeded, maxWidth}) => { const SwapCollection = ({ UpperSwapCard, LowerSwapCard, arrowOnClick, iconNotNeeded }) => {
const theme = useTheme(); const theme = useTheme();
return ( return (
<Box display="flex" flexDirection="column" maxWidth={maxWidth ? maxWidth : "476px"}> <Box display="flex" flexDirection="column" maxWidth="476px">
{UpperSwapCard} {UpperSwapCard}
<Box display="flex" flexDirection="row" justifyContent="center"> <Box display="flex" flexDirection="row" justifyContent="center">
{!iconNotNeeded && (<StyledArrow {!iconNotNeeded && (<StyledArrow

View File

@ -1,12 +1,11 @@
import { SvgIcon, Box } from "@mui/material"; import { SvgIcon } from "@mui/material";
import { styled } from "@mui/material/styles"; import { styled } from "@mui/material/styles";
import FtsoIcon from "../../assets/tokens/FTSO.svg?react"; import FtsoIcon from "../../assets/tokens/FTSO.svg?react";
import StnkIcon from "../../assets/tokens/STNK.svg?react"; import StnkIcon from "../../assets/tokens/STNK.svg?react";
import GhstIcon from "../../assets/tokens/GHST.svg?react"; import GhstIcon from "../../assets/tokens/GHST.svg?react";
import DaiIcon from "../../assets/tokens/DAI.svg?react"; import DaiIcon from "../../assets/tokens/DAI.svg?react";
import EthIcon from "../../assets/tokens/ETH.svg?react"; import WethIcon from "../../assets/tokens/wETH.svg?react";
import EtcIcon from "../../assets/tokens/ETC.svg?react";
import UnknownIcon from "../../assets/tokens/Unknown.svg?react"; import UnknownIcon from "../../assets/tokens/Unknown.svg?react";
const PREFIX = "Token"; const PREFIX = "Token";
@ -24,7 +23,8 @@ const StyledSvgIcon = styled(SvgIcon)(() => ({
}, },
})); }));
export const parseKnownToken = (name) => { const Token = ({ name, viewBox = "0 0 260 260", fontSize = "large", ...props }) => {
const parseKnownToken = (name) => {
let icon; let icon;
switch (name?.toUpperCase()) { switch (name?.toUpperCase()) {
case "FTSO": case "FTSO":
@ -52,48 +52,24 @@ export const parseKnownToken = (name) => {
icon = DaiIcon; icon = DaiIcon;
break; break;
case "ETH": case "ETH":
icon = EthIcon; icon = WethIcon;
break; break;
case "WETH": case "WETH":
icon = EthIcon; icon = WethIcon;
break;
case "METC":
icon = EtcIcon;
break;
case "WMETC":
icon = EtcIcon;
break; break;
default: default:
icon = UnknownIcon; icon = UnknownIcon;
} }
return icon; return icon;
} }
const Token = ({ chainTokenName, name, viewBox = "0 0 260 260", fontSize = "large", ...props }) => {
return ( return (
<Box display="flex" justifyContent="center" alignItems="center" position="relative">
<StyledSvgIcon <StyledSvgIcon
inheritViewBox viewBox={viewBox}
fontSize={fontSize} fontSize={fontSize}
component={parseKnownToken(name)} component={parseKnownToken(name)}
{...props} {...props}
></StyledSvgIcon> ></StyledSvgIcon>
{chainTokenName && (
<StyledSvgIcon
inheritViewBox
component={parseKnownToken(chainTokenName)}
style={{
position: "absolute",
marginLeft: "70%",
marginTop: "45%",
width: "65%",
height: "65%",
border: "1px solid #fff",
borderRadius: "100%"
}}
></StyledSvgIcon>
)}
</Box>
); );
}; };

View File

@ -9,7 +9,7 @@ import FormControl from '@mui/material/FormControl';
import Select from '@mui/material/Select'; import Select from '@mui/material/Select';
import { isNetworkAvailable } from "../../constants"; import { isNetworkAvailable } from "../../constants";
import { parseKnownToken } from "../../components/Token/Token"; import EthIcon from "../../assets/tokens/ETH.svg?react";
import { useSwitchChain } from 'wagmi'; import { useSwitchChain } from 'wagmi';
import toast from "react-hot-toast"; import toast from "react-hot-toast";
@ -53,11 +53,11 @@ function SelectNetwork({ chainId, wrongNetworkToastId, setWrongNetworkToastId, s
} }
}} }}
> >
{chains.map((chain, i) => { {chains.map(chain => {
return ( return (
<MenuItem key={chain.name} value={chain.id}> <MenuItem key={chain.name} value={chain.id}>
<Box gap="10px" display="flex" flexDirection="row" alignItems="center"> <Box gap="10px" display="flex" flexDirection="row" alignItems="center">
<SvgIcon component={parseKnownToken(chain?.nativeCurrency?.symbol)} inheritViewBox /> <SvgIcon component={EthIcon} viewBox="0 0 32 32" />
{!small && <Typography>{chain.name}</Typography>} {!small && <Typography>{chain.name}</Typography>}
</Box> </Box>
</MenuItem> </MenuItem>

View File

@ -22,7 +22,7 @@ import { PrimaryButton, SecondaryButton } from "../../Button";
import ArrowUpIcon from "../../../assets/icons/arrow-up.svg?react"; import ArrowUpIcon from "../../../assets/icons/arrow-up.svg?react";
import { formatCurrency, shorten } from "../../../helpers"; import { formatCurrency, shorten } from "../../../helpers";
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
import { RESERVE_ADDRESSES, FTSO_ADDRESSES } from "../../../constants/addresses"; import { DAI_ADDRESSES, FTSO_ADDRESSES } from "../../../constants/addresses";
import { useAccount, useDisconnect } from "wagmi"; import { useAccount, useDisconnect } from "wagmi";
@ -156,9 +156,9 @@ function InitialWalletView({ isWalletOpen, address, chainId, onClose }) {
> >
<SecondaryButton <SecondaryButton
fullWidth fullWidth
onClick={() => onBtnClick("uniswap", RESERVE_ADDRESSES[chainId], FTSO_ADDRESSES[chainId])} onClick={() => onBtnClick("uniswap", DAI_ADDRESSES[chainId], FTSO_ADDRESSES[chainId])}
> >
<Typography>{`${tokens?.ftso?.symbol}-${tokens?.reserve?.symbol} on Uniswap`}</Typography> <Typography>{`${tokens?.ftso?.symbol}-${tokens?.dai?.symbol} on Uniswap`}</Typography>
</SecondaryButton> </SecondaryButton>
</Box> </Box>

View File

@ -13,24 +13,15 @@ import { ChangeEvent, useState, useEffect } from "react";
import { useNavigate, createSearchParams } from "react-router-dom"; import { useNavigate, createSearchParams } from "react-router-dom";
import { useQuery } from "react-query"; import { useQuery } from "react-query";
import { formatCurrency, formatNumber } from "../../../helpers"; import { formatCurrency, formatNumber } from "../../../helpers";
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber"
import { tokenNameConverter } from "../../../helpers/tokenConverter";
import { isNetworkLegacy } from "../../../constants";
import GhostStyledIcon from "../../Icon/GhostIcon"; import GhostStyledIcon from "../../Icon/GhostIcon";
import TokenStack from "../../TokenStack/TokenStack"; import TokenStack from "../../TokenStack/TokenStack";
import { PrimaryButton, SecondaryButton } from "../../Button"; import { PrimaryButton, SecondaryButton } from "../../Button";
import { useBalance, useTokenSymbol } from "../../../hooks/tokens"; import { useBalance, useTokenSymbol } from "../../../hooks/tokens";
import { import { useDaiPrice, useFtsoPrice, useStnkPrice, useGhstPrice } from "../../../hooks/prices";
useNativePrice,
useReservePrice,
useFtsoPrice,
useStnkPrice,
useGhstPrice
} from "../../../hooks/prices";
import { useLpValuation } from "../../../hooks/treasury"; import { useLpValuation } from "../../../hooks/treasury";
import { useAccount, useBalance as useNativeBalance, useConfig } from "wagmi"; import { useAccount } from "wagmi";
const addTokenToWallet = async (token, userAddress) => { const addTokenToWallet = async (token, userAddress) => {
if (!window.ethereum) return; if (!window.ethereum) return;
@ -69,7 +60,6 @@ const BalanceValue = ({
export const Token = (props) => { export const Token = (props) => {
const { const {
isNative,
symbol, symbol,
icons, icons,
address, address,
@ -78,7 +68,7 @@ export const Token = (props) => {
onAddTokenToWallet, onAddTokenToWallet,
expanded, expanded,
onChangeExpanded, onChangeExpanded,
reserveAddress, daiAddress,
onClose, onClose,
isPool isPool
} = props; } = props;
@ -86,7 +76,7 @@ export const Token = (props) => {
const navigate = useNavigate(); const navigate = useNavigate();
const useLink = (symbol, fromAddress, toAddress, isPool) => { const useLink = (symbol, fromAddress, toAddress, isPool) => {
if (symbol.toUpperCase() === "RESERVE") { if (symbol.toUpperCase() === "GDAI") {
navigate({ pathname: "/faucet" }) navigate({ pathname: "/faucet" })
} else { } else {
navigate({ navigate({
@ -108,11 +98,8 @@ export const Token = (props) => {
} }
return ( return (
<Accordion expanded={isNative ? false : expanded} onChange={onChangeExpanded}> <Accordion expanded={expanded} onChange={onChangeExpanded}>
<AccordionSummary <AccordionSummary expandIcon={<GhostStyledIcon component={ExpandMoreIcon} color="disabled" />}>
sx={{ paddingRight: isNative ? "37.43px" : "" }}
expandIcon={isNative ? null : <GhostStyledIcon component={ExpandMoreIcon} color="disabled" />}
>
<Box sx={{ display: "flex", justifyContent: "space-between", width: "100%", marginRight: "10px" }}> <Box sx={{ display: "flex", justifyContent: "space-between", width: "100%", marginRight: "10px" }}>
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}> <Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
<TokenStack <TokenStack
@ -130,7 +117,7 @@ export const Token = (props) => {
/> />
</Box> </Box>
</AccordionSummary> </AccordionSummary>
{!isNative && <AccordionDetails style={{ margin: "auto", padding: theme.spacing(1, 0) }}> <AccordionDetails style={{ margin: "auto", padding: theme.spacing(1, 0) }}>
<Box <Box
sx={{ display: "flex", flexDirection: "column", flex: 1, mx: "32px", justifyContent: "center" }} sx={{ display: "flex", flexDirection: "column", flex: 1, mx: "32px", justifyContent: "center" }}
style={{ gap: theme.spacing(1) }} style={{ gap: theme.spacing(1) }}
@ -143,14 +130,14 @@ export const Token = (props) => {
<Typography>Add to Wallet</Typography> <Typography>Add to Wallet</Typography>
</PrimaryButton> </PrimaryButton>
<SecondaryButton <SecondaryButton
onClick={() => useLink(symbol, reserveAddress, address, isPool)} onClick={() => useLink(symbol, daiAddress, address, isPool)}
fullWidth fullWidth
> >
<Typography>Get on {symbol?.toUpperCase() === "RESERVE" ? "Faucet" : "Uniswap"}</Typography> <Typography>Get on {symbol.toUpperCase() === "GDAI" ? "Faucet" : "Uniswap"}</Typography>
</SecondaryButton> </SecondaryButton>
</Box> </Box>
</Box> </Box>
</AccordionDetails>} </AccordionDetails>
</Accordion> </Accordion>
); );
}; };
@ -160,15 +147,10 @@ const sumObjValues = (obj: Record<string, string> = {}) =>
export const useWallet = (chainId, userAddress) => { export const useWallet = (chainId, userAddress) => {
const { const {
data: nativeBalanceRaw, balance: daiBalance,
refetch: nativeBalanceRefetch refetch: daiRefetch,
} = useNativeBalance({ address: userAddress }); contractAddress: daiAddress,
const nativeBalance = new DecimalBigNumber(nativeBalanceRaw?.value ?? 0n, 18); } = useBalance(chainId, "GDAI", userAddress);
const {
balance: reserveBalance,
refetch: reserveRefetch,
contractAddress: reserveAddress,
} = useBalance(chainId, "RESERVE", userAddress);
const { const {
balance: ftsoBalance, balance: ftsoBalance,
refetch: ftsoRefetch, refetch: ftsoRefetch,
@ -185,45 +167,32 @@ export const useWallet = (chainId, userAddress) => {
contractAddress: ghstAddress, contractAddress: ghstAddress,
} = useBalance(chainId, "GHST", userAddress); } = useBalance(chainId, "GHST", userAddress);
const { const {
balance: lpReserveFtsoBalance, balance: lpDaiFtsoBalance,
refetch: lpReserveFtsoRefetch, refetch: lpDaiFtsoRefetch,
contractAddress: lpReserveFtsoBalanceAddress, contractAddress: lpDaiFtsoBalanceAddress,
} = useBalance(chainId, "RESERVE_FTSO", userAddress); } = useBalance(chainId, "GDAI_FTSO", userAddress);
const nativePrice = useNativePrice(chainId); const daiPrice = useDaiPrice(chainId);
const reservePrice = useReservePrice(chainId);
const ftsoPrice = useFtsoPrice(chainId); const ftsoPrice = useFtsoPrice(chainId);
const stnkPrice = useStnkPrice(chainId); const stnkPrice = useStnkPrice(chainId);
const ghstPrice = useGhstPrice(chainId); const ghstPrice = useGhstPrice(chainId);
const lpReserveFtsoPrice = useLpValuation(chainId, "RESERVE_FTSO", 1000000000000000000n); const lpDaiFtsoPrice = useLpValuation(chainId, "GDAI_FTSO", 1000000000000000000n);
const config = useConfig(); const { symbol: daiSymbol } = useTokenSymbol(chainId, "GDAI");
const nativeSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE");
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO"); const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK"); const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK");
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST"); const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
const { symbol: lpReserveFtsoSymbol } = useTokenSymbol(chainId, "RESERVE_FTSO"); const { symbol: lpDaiFtsoSymbol } = useTokenSymbol(chainId, "GDAI_FTSO");
const tokens = { const tokens = {
native: { dai: {
symbol: nativeSymbol, symbol: daiSymbol,
icons: [nativeSymbol], address: daiAddress,
balance: nativeBalance, balance: daiBalance,
price: nativePrice, price: daiPrice,
refetch: nativeBalanceRefetch icons: ["GDAI"],
}, externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/gDAI.svg",
reserve: { refetch: daiRefetch,
symbol: reserveSymbol,
address: reserveAddress,
balance: reserveBalance,
price: reservePrice,
icons: isNetworkLegacy(chainId) ? ["GDAI"] : [tokenNameConverter(chainId, reserveSymbol)],
externalUrl: isNetworkLegacy(chainId)
? "https://ghostchain.io/wp-content/uploads/2025/03/gDAI.svg"
: "https://ghostchain.io/wp-content/uploads/2025/11/6A-Classic-ETC-Token.svg",
refetch: reserveRefetch,
}, },
ftso: { ftso: {
symbol: ftsoSymbol, symbol: ftsoSymbol,
@ -252,15 +221,15 @@ export const useWallet = (chainId, userAddress) => {
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/GHST.svg", externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/GHST.svg",
refetch: ghstRefetch, refetch: ghstRefetch,
}, },
reserveFtso: { daiFtso: {
isPool: true, isPool: true,
symbol: lpReserveFtsoSymbol, symbol: lpDaiFtsoSymbol,
address: lpReserveFtsoBalanceAddress, address: lpDaiFtsoBalanceAddress,
balance: lpReserveFtsoBalance, balance: lpDaiFtsoBalance,
price: lpReserveFtsoPrice, price: lpDaiFtsoPrice,
icons: ["FTSO", isNetworkLegacy(chainId) ? "GDAI" : tokenNameConverter(chainId, reserveSymbol)], icons: ["GDAI", "FTSO"],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/uni-v2.svg", externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/uni-v2.svg",
refetch: lpReserveFtsoRefetch, refetch: lpDaiFtsoRefetch,
} }
}; };
@ -277,12 +246,12 @@ export const useWallet = (chainId, userAddress) => {
export const Tokens = ({ address, tokens, onClose }) => { export const Tokens = ({ address, tokens, onClose }) => {
const [expanded, setExpanded] = useState(null); const [expanded, setExpanded] = useState(null);
const alwaysShowTokens = [tokens.native, tokens.reserve, tokens.ftso, tokens.stnk, tokens.ghst, tokens.reserveFtso]; const alwaysShowTokens = [tokens.dai, tokens.ftso, tokens.stnk, tokens.ghst, tokens.daiFtso];
const tokenProps = (token) => ({ const tokenProps = (token) => ({
...token, ...token,
expanded: expanded === token.symbol, expanded: expanded === token.symbol,
reserveAddress: tokens.reserve.address, daiAddress: tokens.dai.address,
onChangeExpanded: (e, isExpanded) => setExpanded(isExpanded ? token.symbol : null), onChangeExpanded: (e, isExpanded) => setExpanded(isExpanded ? token.symbol : null),
onAddTokenToWallet: () => addTokenToWallet(token, address), onAddTokenToWallet: () => addTokenToWallet(token, address),
onClose: () => onClose(), onClose: () => onClose(),
@ -291,7 +260,7 @@ export const Tokens = ({ address, tokens, onClose }) => {
return ( return (
<> <>
{alwaysShowTokens.map((token, i) => ( {alwaysShowTokens.map((token, i) => (
<Token key={i} isNative={i === 0} {...tokenProps(token)} /> <Token key={i} {...tokenProps(token)} />
))} ))}
</> </>
); );

View File

@ -1,28 +1,8 @@
import { defineChain } from 'viem'
import { http, fallback, createConfig } from 'wagmi' import { http, fallback, createConfig } from 'wagmi'
import { sepolia, hoodi } from 'wagmi/chains' import { sepolia, hoodi } from 'wagmi/chains'
const mordor = defineChain({
id: 63,
name: 'Mordor',
nativeCurrency: {
decimals: 18,
name: 'METC',
symbol: 'mETC',
},
rpcUrls: {
default: { http: ['https://rpc.mordor.etccooperative.org'] },
},
blockExplorers: {
default: {
name: 'Blockscout',
url: 'https://etc-mordor.blockscout.com/'
},
},
})
export const config = createConfig({ export const config = createConfig({
chains: [sepolia, hoodi, mordor], chains: [sepolia, hoodi],
transports: { transports: {
[sepolia.id]: fallback([ [sepolia.id]: fallback([
http('https://ethereum-sepolia-rpc.publicnode.com'), http('https://ethereum-sepolia-rpc.publicnode.com'),
@ -36,10 +16,6 @@ export const config = createConfig({
[hoodi.id]: fallback([ [hoodi.id]: fallback([
http('https://rpc.hoodi.ethpandaops.io'), http('https://rpc.hoodi.ethpandaops.io'),
http('https://0xrpc.io/hoodi'), http('https://0xrpc.io/hoodi'),
]),
[mordor.id]: fallback([
http('https://rpc.mordor.etccooperative.org'),
http('https://geth-mordor.etc-network.info'),
]) ])
}, },
}) })

View File

@ -1,7 +1,6 @@
export enum NetworkId { export enum NetworkId {
TESTNET_SEPOLIA = 11155111, TESTNET_SEPOLIA = 11155111,
TESTNET_HOODI = 560048, TESTNET_HOODI = 560048,
TESTNET_MORDOR = 63,
} }
export const isNetworkAvailable = (chainId, addressChainId) => { export const isNetworkAvailable = (chainId, addressChainId) => {
@ -14,44 +13,8 @@ export const isNetworkAvailable = (chainId, addressChainId) => {
case 560048: case 560048:
exists = true exists = true
break; break;
case 63:
exists = true
break;
default: default:
break; break;
} }
return exists; return exists;
} }
export const isNetworkLegacy = (chainId) => {
let exists = false;
switch (chainId) {
case 11155111:
exists = true
break;
case 560048:
exists = true
break;
default:
break;
}
return exists;
}
export const networkAvgBlockSpeed = (chainId) => {
let blockSpeed = 12n;
switch (chainId) {
case 11155111:
blockSpeed = 12n
break;
case 560048:
blockSpeed = 12n
break;
case 63:
blockSpeed = 13n
break;
default:
break;
}
return blockSpeed
}

View File

@ -3,116 +3,78 @@ import { NetworkId } from "../constants";
export const STAKING_ADDRESSES = { export const STAKING_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0xd90E63E88282596E1ea33765b41Ba3d650f4aD52", [NetworkId.TESTNET_SEPOLIA]: "0xd90E63E88282596E1ea33765b41Ba3d650f4aD52",
[NetworkId.TESTNET_HOODI]: "0x25F62eDc6C89FF84E957C22336A35d2dfc861a86", [NetworkId.TESTNET_HOODI]: "0x25F62eDc6C89FF84E957C22336A35d2dfc861a86",
[NetworkId.TESTNET_MORDOR]: "0xC25C9C56a89ebd6ef291b415d00ACfa7913c55e7",
}; };
export const BOND_DEPOSITORY_ADDRESSES = { export const BOND_DEPOSITORY_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0xdcE486113280e49ca2fB200258E5Ee1B2D21D495", [NetworkId.TESTNET_SEPOLIA]: "0xdcE486113280e49ca2fB200258E5Ee1B2D21D495",
[NetworkId.TESTNET_HOODI]: "0x6Ad50B1E293E68B2fC230c576220a93A9D311571", [NetworkId.TESTNET_HOODI]: "0x6Ad50B1E293E68B2fC230c576220a93A9D311571",
[NetworkId.TESTNET_MORDOR]: "0x7C85cDEddBAd0f50453d373F7332BEa11ECa7BAf",
}; };
export const DAO_TREASURY_ADDRESSES = { export const DAO_TREASURY_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0x93dd30f819403710de7933B79A74C4A42438458D", [NetworkId.TESTNET_SEPOLIA]: "0x93dd30f819403710de7933B79A74C4A42438458D",
[NetworkId.TESTNET_HOODI]: "0x1a1b29b18f714fac9dDabEf530dFc4f85b56A6e8", [NetworkId.TESTNET_HOODI]: "0x1a1b29b18f714fac9dDabEf530dFc4f85b56A6e8",
[NetworkId.TESTNET_MORDOR]: "0x5883C8e2259556B534036c7fDF4555E09dE9f243",
}; };
export const FTSO_DAI_LP_ADDRESSES = { export const FTSO_DAI_LP_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0x1394dC3f7bABaa2F0CA80353648087DAB1BF3fd6", [NetworkId.TESTNET_SEPOLIA]: "0x1394dC3f7bABaa2F0CA80353648087DAB1BF3fd6",
[NetworkId.TESTNET_HOODI]: "0xf7B2d44209E70782d93A70F7D8eC50010dF7ae50", [NetworkId.TESTNET_HOODI]: "0xf7B2d44209E70782d93A70F7D8eC50010dF7ae50",
[NetworkId.TESTNET_MORDOR]: "0xE6546D12665dB5B22Cb92FB9e0221aE51A57aeaa",
}; };
export const FTSO_STNK_LP_ADDRESSES = { export const FTSO_STNK_LP_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0x0000000000000000000000000000000000000000", // TBD [NetworkId.TESTNET_SEPOLIA]: "0x0000000000000000000000000000000000000000", // TBD
[NetworkId.TESTNET_SEPOLIA]: "0x0000000000000000000000000000000000000000", [NetworkId.TESTNET_SEPOLIA]: "0x0000000000000000000000000000000000000000",
[NetworkId.TESTNET_MORDOR]: "0x0000000000000000000000000000000000000000",
} }
export const RESERVE_ADDRESSES = { export const DAI_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0x5f63a27a9214a0352F2EF8dAF1eD4974d713192B", [NetworkId.TESTNET_SEPOLIA]: "0x5f63a27a9214a0352F2EF8dAF1eD4974d713192B",
[NetworkId.TESTNET_HOODI]: "0x80c6676c334BCcE60b3CC852085B72143379CE58", [NetworkId.TESTNET_HOODI]: "0x80c6676c334BCcE60b3CC852085B72143379CE58",
[NetworkId.TESTNET_MORDOR]: "0x6af91B3763b5d020E0985f85555EB50e5852d7AC",
}; };
export const WETH_ADDRESSES = { export const WETH_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0xfff9976782d46cc05630d1f6ebab18b2324d6b14", [NetworkId.TESTNET_SEPOLIA]: "0xfff9976782d46cc05630d1f6ebab18b2324d6b14",
[NetworkId.TESTNET_HOODI]: "0xE69a5c6dd88cA798b93c3C92fc50c51Fd5305eB4", [NetworkId.TESTNET_HOODI]: "0xE69a5c6dd88cA798b93c3C92fc50c51Fd5305eB4",
[NetworkId.TESTNET_MORDOR]: "0x6af91B3763b5d020E0985f85555EB50e5852d7AC",
}; };
export const GHST_ADDRESSES = { export const GHST_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0xdf2e5306A3dCcfA4e21bbF4226C17Ff5B008dDC4", [NetworkId.TESTNET_SEPOLIA]: "0xdf2e5306A3dCcfA4e21bbF4226C17Ff5B008dDC4",
[NetworkId.TESTNET_HOODI]: "0xE98f7426457E6533B206e91B7EcA97aa8A258B46", [NetworkId.TESTNET_HOODI]: "0xE98f7426457E6533B206e91B7EcA97aa8A258B46",
[NetworkId.TESTNET_MORDOR]: "0x14b5787F8a1E62786F50A7998A9b14aa24298423",
}; };
export const STNK_ADDRESSES = { export const STNK_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0x02C296A27eA779d5a16F934337c12062C5E3c0D9", [NetworkId.TESTNET_SEPOLIA]: "0x02C296A27eA779d5a16F934337c12062C5E3c0D9",
[NetworkId.TESTNET_HOODI]: "0xF07e9303A9f16Afd82f4f57Fd6fca68Aa0AB6D7F", [NetworkId.TESTNET_HOODI]: "0xF07e9303A9f16Afd82f4f57Fd6fca68Aa0AB6D7F",
[NetworkId.TESTNET_MORDOR]: "0x137bA9403885D8ECEa95AaFBb8734F5a16121bAC",
}; };
export const FTSO_ADDRESSES = { export const FTSO_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0xcFedFFEB3FdeCd2196820Ba3b71f3F84A1255f93", [NetworkId.TESTNET_SEPOLIA]: "0xcFedFFEB3FdeCd2196820Ba3b71f3F84A1255f93",
[NetworkId.TESTNET_HOODI]: "0xb184e423811b644A1924334E63985c259F5D0033", [NetworkId.TESTNET_HOODI]: "0xb184e423811b644A1924334E63985c259F5D0033",
[NetworkId.TESTNET_MORDOR]: "0xeA170CC0faceC531a6a9e93a28C4330Ac50343a1",
}; };
export const DISTRIBUTOR_ADDRESSES = { export const DISTRIBUTOR_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0x8fbF8eB4Fcd451EF62Aee33508D46FE120963194", [NetworkId.TESTNET_SEPOLIA]: "0x8fbF8eB4Fcd451EF62Aee33508D46FE120963194",
[NetworkId.TESTNET_HOODI]: "0xdF49dC81c457c6f92e26cf6d686C7a8715255842", [NetworkId.TESTNET_HOODI]: "0xdF49dC81c457c6f92e26cf6d686C7a8715255842",
[NetworkId.TESTNET_MORDOR]: "0xaf5e76706520db7fb01096E322940206bf3fce57",
}; };
export const GHOST_GOVERNANCE_ADDRESSES = { export const GHOST_GOVERNANCE_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0xDab0c51918E6990d8763FAC8a04AE159e44e0c4f", [NetworkId.TESTNET_SEPOLIA]: "0xDab0c51918E6990d8763FAC8a04AE159e44e0c4f",
[NetworkId.TESTNET_HOODI]: "0x1B96B792840d4d19d5097ee007392Ed4d851e64F", [NetworkId.TESTNET_HOODI]: "0x1B96B792840d4d19d5097ee007392Ed4d851e64F",
[NetworkId.TESTNET_MORDOR]: "0x3dD438416D9593A58193fC52850E588efAa3D57E",
}; };
export const BONDING_CALCULATOR_ADDRESSES = { export const BONDING_CALCULATOR_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0x4896bFc6256A57Df826d7144E48c9633d51d6319", [NetworkId.TESTNET_SEPOLIA]: "0x4896bFc6256A57Df826d7144E48c9633d51d6319",
[NetworkId.TESTNET_HOODI]: "0x2635d526Ad24b98082563937f7b996075052c6Fd", [NetworkId.TESTNET_HOODI]: "0x2635d526Ad24b98082563937f7b996075052c6Fd",
[NetworkId.TESTNET_MORDOR]: "0x0c4C7C49a173E2a3f9Eed93125F3F146D8e17bCb",
} }
export const GATEKEEPER_ADDRESSES = { export const GATEKEEPER_ADDRESSES = {
[NetworkId.TESTNET_SEPOLIA]: "0xc85129A097773B7F8970a7364c928C05f265E6A1", [NetworkId.TESTNET_SEPOLIA]: "0xc85129A097773B7F8970a7364c928C05f265E6A1",
[NetworkId.TESTNET_MORDOR]: "0xA59cB4ff90bE2206121aE61eEB68d0AeC7BA095f",
} }
export const UNISWAP_V2_ROUTER = { export const UNISWAP_V2_ROUTER = {
[NetworkId.TESTNET_SEPOLIA]: "0xee567fe1712faf6149d80da1e6934e354124cfe3", [NetworkId.TESTNET_SEPOLIA]: "0xee567fe1712faf6149d80da1e6934e354124cfe3",
[NetworkId.TESTNET_HOODI]: "0xD41daF947c6FFEf344754B99ad09466FBCBb7583", [NetworkId.TESTNET_HOODI]: "0xD41daF947c6FFEf344754B99ad09466FBCBb7583",
[NetworkId.TESTNET_MORDOR]: "0x90ecf6a29798E3cf31EB7DCE64a372AC40d83F83",
}; };
export const UNISWAP_V2_FACTORY = { export const UNISWAP_V2_FACTORY = {
[NetworkId.TESTNET_SEPOLIA]: "0xF62c03E08ada871A0bEb309762E260a7a6a880E6", [NetworkId.TESTNET_SEPOLIA]: "0xF62c03E08ada871A0bEb309762E260a7a6a880E6",
[NetworkId.TESTNET_HOODI]: "0xF140342cB5C29C1468d91Aee408d7b7271C48b5A", [NetworkId.TESTNET_HOODI]: "0xF140342cB5C29C1468d91Aee408d7b7271C48b5A",
[NetworkId.TESTNET_MORDOR]: "0x909f96C1a436B3386E9962e30f3Ce753070ff524",
}; };
export const NATIVE_TICKERS = {
[NetworkId.TESTNET_SEPOLIA]: [
"https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDT",
"https://api.coinbase.com/v2/prices/ETH-USDT/spot",
],
[NetworkId.TESTNET_HOODI]: [
"https://api.binance.com/api/v3/ticker/price?symbol=ETHUSDT",
"https://api.coinbase.com/v2/prices/ETH-USDT/spot",
],
[NetworkId.TESTNET_MORDOR]: [
"https://api.binance.com/api/v3/ticker/price?symbol=ETCUSDT",
"https://api.coinbase.com/v2/prices/ETC-USDT/spot",
],
}
export const CEX_TICKERS = {
[NetworkId.TESTNET_MORDOR]: [
"https://api.binance.com/api/v3/ticker/price?symbol=ETCUSDT",
"https://api.coinbase.com/v2/prices/ETC-USDT/spot",
],
}

View File

@ -18,11 +18,4 @@ export const AVAILABLE_DEXES = {
viewBox: "0 0 195 230", viewBox: "0 0 195 230",
}, },
], ],
[NetworkId.TESTNET_MORDOR]: [
{
name: "Uniswap",
icon: UniswapIcon,
viewBox: "0 0 195 230",
},
],
}; };

File diff suppressed because it is too large Load Diff

View File

@ -1,366 +0,0 @@
import { useMemo, useState, useEffect } from "react";
import {
Box,
Typography,
Link,
Skeleton,
TableContainer,
Table,
Paper,
TableHead,
TableBody,
TableRow,
TableCell,
Modal,
useTheme,
} from "@mui/material";
import { useConfig } from "wagmi";
import { ss58Decode } from "@polkadot-labs/hdkd-helpers";
import { toHex } from "@polkadot-api/utils";
import { PrimaryButton } from "../../components/Button";
import GhostStyledIcon from "../../components/Icon/GhostIcon";
import SwapCard from "../../components/Swap/SwapCard";
import SwapCollection from "../../components/Swap/SwapCollection";
import InfoTooltip from "../../components/Tooltip/InfoTooltip";
import { ghost } from "../../hooks/staking";
import { useBalance } from "../../hooks/tokens";
import CheckIcon from '@mui/icons-material/Check';
import HourglassBottomIcon from '@mui/icons-material/HourglassBottom';
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { formatNumber, formatCurrency, timeConverter } from "../../helpers";
import { BridgeRoute } from "./BridgeRoute";
const sliceString = (string, first, second) => {
if (!string) return "";
return string.slice(0, first) + "..." + string.slice(second);
}
export const BridgeCardAction = ({
isVerySmallScreen,
isSemiSmallScreen,
chainId,
address,
ghstSymbol,
gatekeeperAddressEmpty,
gatekeeperAddress,
evmNetwork,
connect,
isConfirmed,
setIsConfirmed,
openBridgeModal,
storeTransactionHash,
}) => {
const [isPending, setIsPending] = useState(false);
const [receiver, setReceiver] = useState("");
const [convertedReceiver, setConvertedReceiver] = useState(undefined);
const [amount, setAmount] = useState("");
const config = useConfig();
const incomingFee = Number(evmNetwork?.incoming_fee ?? 0n) / 10000000;
const {
balance: ghstBalance,
refetch: ghstBalanceRefetch
} = useBalance(chainId, "GHST", address);
const chainName = useMemo(() => {
const client = config?.getClient();
return client?.chain?.name;
}, [config]);
const chainNativeCurrency = useMemo(() => {
const client = config?.getClient();
return client?.chain?.nativeCurrency?.symbol;
}, [config]);
const chainExplorerUrl = useMemo(() => {
const client = config?.getClient();
return client?.chain?.blockExplorers?.default?.url;
}, [config]);
const preparedAmount = useMemo(() => {
try {
const result = BigInt(parseFloat(amount) * Math.pow(10, 18));
if (result > ghstBalance._value) {
return ghstBalance._value;
}
return result;
} catch {
return 0n;
}
}, [amount]);
const amountAfterFee = useMemo(() => {
const convertedAmount = parseFloat(amount);
if (!convertedAmount) {
return 0;
}
return convertedAmount * (1 - incomingFee / 100);
}, [amount, incomingFee]);
const isDisabled = useMemo(() => {
let isDisabled = isPending || gatekeeperAddressEmpty;
if (address !== "") {
isDisabled = isDisabled
|| !convertedReceiver
|| preparedAmount === 0n
|| ghstBalance._value < preparedAmount;
}
return isDisabled;
}, [isPending, gatekeeperAddressEmpty, address, convertedReceiver, preparedAmount, ghstBalance]);
const ghostFunds = async () => {
setIsPending(true);
try {
const txHash = await ghost(chainId, address, convertedReceiver, preparedAmount);
if (txHash) {
storeTransactionHash(txHash, receiver, preparedAmount.toString());
}
} finally {
await ghstBalanceRefetch();
setReceiver("");
setAmount("");
setIsPending(false);
}
}
useEffect(() => {
if (isConfirmed) {
setIsConfirmed(false);
ghostFunds();
}
}, [isConfirmed]);
const ghostOrConnect = async () => {
if (address === "") {
connect();
} else if (!isConfirmed) {
openBridgeModal();
}
}
useEffect(() => {
try {
const [publicKey, prefix] = ss58Decode(receiver);
if (prefix !== 1995 && prefix !== 1996) {
throw new Error("bad prefix");
}
setConvertedReceiver(toHex(publicKey));
} catch {
setConvertedReceiver(undefined);
}
}, [receiver])
return (
<Box width="100%" height="320px" display="flex" flexDirection="column" justifyContent="space-between">
<SwapCollection
iconNotNeeded
UpperSwapCard={<SwapCard
id={`bridge-token-receiver`}
inputWidth={"100%"}
value={receiver}
onChange={event => setReceiver(event.currentTarget.value)}
inputProps={{ "data-testid": "fromInput" }}
placeholder="GHOST address (sf prefixed)"
type="text"
maxWidth="100%"
/>}
LowerSwapCard={<SwapCard
id={`bridge-token-amount`}
inputWidth={"100%"}
info={`${formatCurrency(ghstBalance.toString(), 4, ghstSymbol)}`}
value={amount}
onChange={event => setAmount(event.currentTarget.value)}
inputProps={{ "data-testid": "fromInput" }}
endString={"Max"}
endStringOnClick={() => setAmount(ghstBalance.toString())}
maxWidth="100%"
/>}
/>
<Box
flexDirection="column"
display="flex"
gap="10px"
justifyContent="space-between"
>
<Box display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{gatekeeperAddressEmpty && (
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
<Typography mr="10px" variant="body2" color="textSecondary">
<em>
There is no connected gatekeeper on {chainName} network. Propose gatekeeper on this network to make authorities listen to it.
</em>
</Typography>
</Box>
)}
{!gatekeeperAddressEmpty && (
<>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Gatekeeper:</Typography>}
<Link
fontSize="12px"
lineHeight="15px"
target="_blank"
rel="noopener noreferrer"
href={`${chainExplorerUrl}/token/${gatekeeperAddress}`}
>
{sliceString(gatekeeperAddress, 10, -8)}
</Link>
</>
)}
</Box>
<Box width="100%" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
<Box width="100%" display="flex" flexDirection="column" gap="0px">
<Box maxWidth="100%" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Bridge Fee:</Typography>}
{incomingFee
? <Typography fontSize="12px" lineHeight="15px">{`${incomingFee.toFixed(4)}%`}</Typography>
: <Skeleton height={15} width="80px" />
}
</Box>
<Box maxWidth="100%" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">You will get:</Typography>}
{incomingFee
? <Typography fontSize="12px" lineHeight="15px">{amountAfterFee.toFixed(4)} {ghstSymbol}</Typography>
: <Skeleton height={15} width="80px" />
}
</Box>
</Box>
</Box>
<BridgeRoute coinName={ghstSymbol} chainTokenName={chainNativeCurrency} tokens={[ghstSymbol]} />
</Box>
<PrimaryButton
fullWidth
disabled={isDisabled}
loading={isPending}
onClick={() => ghostOrConnect()}
>
{address === "" ? "Connect" : "Bridge" }
</PrimaryButton>
</Box>
)
}
export const BridgeCardHistory = ({
isSemiSmallScreen,
filteredStoredTransactions,
ghstSymbol,
blockNumber,
finalityDelay,
setActiveTxIndex
}) => {
const theme = useTheme();
const background = (index) => {
return index % 2 === 1 ? "" : theme.colors.gray[750];
}
return (
<Box height="320px">
<TableContainer
component={Paper}
className="custom-scrollbar"
sx={{
height: "320px",
overflowY: 'scroll',
msOverflowStyle: "thin !important",
scrollbarWidth: "thin !important",
}}
>
<Table sx={{ marginTop: "0px" }} stickyHeader aria-label="sticky available transactions">
<TableHead>
<TableRow sx={{ height: "40px" }}>
<TableCell align="left" style={{ padding: "0px", paddingLeft: "16px", fontSize: "12px", borderTopLeftRadius: "3px", background: theme.colors.paper.cardHover }}>
Transaction
</TableCell>
<TableCell align="center" style={{ padding: "0px", fontSize: "12px", background: theme.colors.paper.cardHover }}>
Datetime
</TableCell>
<TableCell align="center" style={{ padding: "0px", fontSize: "12px", borderTopRightRadius: "3px", background: theme.colors.paper.cardHover }}>
Status
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{filteredStoredTransactions?.map((obj, idx) => (
<TableRow
key={idx}
sx={{ cursor: "pointer", height: "30px" }}
id={idx + `--tx-history`}
data-testid={idx + `--tx-history`}
onClick={() => setActiveTxIndex(idx)}
>
<TableCell style={{ background: background(idx) }}>
<Box display="flex" flexDirection="column" justifyContent="center">
<Typography variant="caption">
{formatCurrency(
new DecimalBigNumber(BigInt(obj.amount), 18).toString(),
isSemiSmallScreen ? 3 : 8,
ghstSymbol
)}
</Typography>
<Typography variant="caption">
{sliceString(
obj.receiverAddress,
isSemiSmallScreen ? 5 : 10,
isSemiSmallScreen ? -3 : -8
)}
</Typography>
</Box>
</TableCell>
<TableCell style={{ background: background(idx) }}>
<Box display="flex" flexDirection="column" alignItems="center" paddingLeft="5px">
<Typography variant="caption">
{new Date(obj.timestamp).toLocaleDateString('en-US')}
</Typography>
<Typography variant="caption">
{new Date(obj.timestamp).toLocaleTimeString('en-US')}
</Typography>
</Box>
</TableCell>
<TableCell style={{ background: background(idx) }}>
<Box display="flex" justifyContent="center" alignItems="center">
<Box
display="flex"
justifyContent="center"
alignItems="center"
sx={{
width: "20px",
height: "20px",
background: Number(blockNumber) - (obj.blockNumber + finalityDelay) < 0
? theme.colors.feedback.warning
: theme.colors.feedback.success,
borderRadius: "100%",
boxShadow: "0px 0px 1px black"
}}
>
{Number(blockNumber) - (obj.blockNumber + finalityDelay) < 0 ?
<GhostStyledIcon
sx={{ width: "15px", height: "15px" }}
viewBox="0 0 25 25"
component={HourglassBottomIcon}
/>
:
<GhostStyledIcon
sx={{ width: "15px", height: "15px" }}
viewBox="0 0 25 25"
component={CheckIcon}
/>
}
</Box>
</Box>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Box>
)
}

View File

@ -1,133 +0,0 @@
import { useMemo } from "react";
import { Box, Paper, Grid, Typography, LinearProgress, useTheme } from "@mui/material"
import Metric from "../../components/Metric/Metric";
import Countdown from "../../components/Countdown/Countdown";
import { formatNumber } from "../../helpers";
export const BridgeHeader = ({
totalValidators,
disabledValidators,
bridgeStability,
transactionEta,
timeToNextEpoch,
isSmallScreen
}) => {
const theme = useTheme();
const disabledPercentage = useMemo(() => {
if (totalValidators === undefined || disabledValidators === undefined) {
return 0;
}
return ((totalValidators - disabledValidators) / totalValidators) * 100;
}, [totalValidators, disabledValidators]);
const validatorsColor = useMemo(() => {
if (disabledPercentage < 50) {
return theme.colors.validatorsColor.red;
}
return theme.colors.validatorsColor.green;
}, [disabledPercentage, theme]);
const stabilityColor = useMemo(() => {
if (bridgeStability > 80) {
return theme.colors.bridgeProgress.success;
} else if (bridgeStability > 50) {
return theme.colors.bridgeProgress.warning;
} else {
return theme.colors.bridgeProgress.error;
}
}, [bridgeStability, theme]);
const progressBarPostfix = useMemo(() => {
if (bridgeStability > 90) {
return "✅ Safe";
} else if (bridgeStability > 80) {
return "✅ Moderate Risk";
} else if (bridgeStability > 70) {
return "⚠️ High Risk";
} else if (bridgeStability > 50) {
return "⚠️ Critical Risk";
} else {
return "❌ Do NOT Bridge";
}
}, [bridgeStability]);
const formatTime = (totalSeconds) => {
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const secs = Math.floor(totalSeconds % 60);
if (hours > 0) {
return `${hours} hours ${minutes} mins`;
} else if (minutes > 0) {
return `${minutes} mins`;
} else {
return `${secs} secs`;
}
}
return (
<Grid container spacing={isSmallScreen ? 4 : 1}>
<Grid item xs={isSmallScreen ? 12 : 4}>
<Metric
isLoading={totalValidators === undefined || disabledValidators === undefined}
metric={
<Typography color={validatorsColor} fontSize="24px" fontWeight="700" lineHeight="33px">
{totalValidators} ({formatNumber(disabledPercentage, 0)}% active)
</Typography>
}
label="Total Validators"
tooltip="Active and disabled GHOST Validators in the current GHOST Epoch."
/>
</Grid>
<Grid item xs={isSmallScreen ? 12 : 4}>
<Metric
isLoading={timeToNextEpoch === undefined}
metric={formatTime(timeToNextEpoch)}
label="Rotation in"
tooltip="Bridge Stability Index refreshes every 10 minutes with new validator blocks; resets each Era when the validator set is updated."
/>
</Grid>
<Grid item xs={isSmallScreen ? 12 : 4}>
<Metric
isLoading={transactionEta === undefined}
metric={formatTime(transactionEta)}
label="Max Bridge ETA"
tooltip="Maximum estimated time for finalizing bridge transactions based on the latest update."
/>
</Grid>
<Grid item gap={2} xs={12} sx={{ marginTop: "20px" }}>
<Box position="relative" margin="4.5px 0">
<LinearProgress
variant="determinate"
value={bridgeStability ?? 0}
sx={{
borderRadius: "5px",
height: "40px",
'& .MuiLinearProgress-bar': {
backgroundColor: stabilityColor,
}
}}
/>
<Box sx={{
top: 0,
left: 0,
bottom: 0,
right: 0,
position: 'absolute',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
Bridge Stability {bridgeStability
? `${formatNumber(bridgeStability, 0)}% ${progressBarPostfix}`
: "Unknown"
}
</Typography>
</Box>
</Box>
</Grid>
</Grid>
)
}

View File

@ -1,410 +0,0 @@
import { useState, useEffect } from "react";
import { Box, Typography, Link, FormControlLabel, Checkbox, useTheme } from "@mui/material";
import { CheckBoxOutlineBlank, CheckBoxOutlined } from "@mui/icons-material";
import ThumbUpIcon from '@mui/icons-material/ThumbUp';
import ThumbDownAltIcon from '@mui/icons-material/ThumbDownAlt';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import CheckIcon from '@mui/icons-material/Check';
import AssuredWorkloadIcon from '@mui/icons-material/AssuredWorkload';
import HourglassBottomIcon from '@mui/icons-material/HourglassBottom';
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
import HandshakeIcon from '@mui/icons-material/Handshake';
import PendingIcon from '@mui/icons-material/Pending';
import ContentPasteIcon from '@mui/icons-material/ContentPaste';
import InfoTooltip from "../../components/Tooltip/InfoTooltip";
import Modal from "../../components/Modal/Modal";
import GhostStyledIcon from "../../components/Icon/GhostIcon";
import { PrimaryButton } from "../../components/Button";
import { formatCurrency } from "../../helpers";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
export const BridgeModal = ({
currentRecord,
activeTxIndex,
setActiveTxIndex,
authorities,
ghstSymbol,
hashedArguments,
chainExplorerUrl,
removeStoredRecord,
}) => {
const theme = useTheme();
const [copiedIndex, setCopiedIndex] = useState(null);
const sliceString = (string, first, second) => {
if (!string) return "";
return string.slice(0, first) + "..." + string.slice(second);
}
const copyToClipboard = (text, index) => {
navigator.clipboard.writeText(text).then(() => {
setCopiedIndex(index);
setTimeout(() => setCopiedIndex(null) , 800);
});
};
return (
<Modal
data-testid="transaction-details-modal"
maxWidth="476px"
headerContent={
<Box display="flex" flexDirection="row">
<Typography variant="h5">
TX Hash&nbsp;
<Link
sx={{
margin: "0px",
font: "inherit",
letterSpacing: "inherit",
textDecoration: "underline",
color: theme.colors.gray[10],
textUnderlineOffset: "0.23rem",
cursor: "pointer",
textDecorationThickness: "3px",
"&:hover": {
textDecoration: "underline",
}
}}
target="_blank"
rel="noopener noreferrer"
href={currentRecord
? `${chainExplorerUrl}/tx/${currentRecord ? currentRecord.transactionHash : ""}`
: ""
}
>
{currentRecord ? sliceString(currentRecord.transactionHash, 10, -8) : ""}
</Link>
</Typography>
</Box>
}
open={activeTxIndex >= 0}
onClose={() => setActiveTxIndex(-1)}
minHeight={"100px"}
>
<Box display="flex" gap="1.5rem" flexDirection="column" marginTop=".8rem">
<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center">
<Box
sx={{
transition: "all 0.8s ease",
transform: currentRecord?.step === 0 && "scale(1.2)",
color: currentRecord?.step > 0 && theme.colors.primary[300]
}}
width="120px"
display="flex"
flexDirection="column"
justifyContent="start"
alignItems="center"
>
<GhostStyledIcon
sx={{
width: "35px",
height: "35px",
animation: currentRecord?.step === 0 && 'rotateHourGlass 2s ease-in-out infinite',
'@keyframes rotateHourGlass': {
'0%': { transform: 'rotate(0deg)' },
'15%': { transform: 'rotate(0deg)' },
'85%': { transform: 'rotate(180deg)' },
'100%': { transform: 'rotate(180deg)' },
},
}}
viewBox="0 0 25 25"
component={HourglassBottomIcon}
/>
<Typography variant="caption">Finalization</Typography>
<Typography variant="caption">
{(currentRecord?.finalization ?? 0).toString()} blocks left
</Typography>
</Box>
<GhostStyledIcon
sx={{ transition: "all 0.3s ease", opacity: currentRecord?.step < 1 && "0.2" }}
component={ArrowRightIcon}
/>
<Box
sx={{
transition: "all 0.3s ease",
opacity: currentRecord?.step < 1 && "0.2",
transform: currentRecord?.step === 1 && "scale(1.2)",
color: currentRecord?.step > 1 && theme.colors.primary[300]
}}
width="120px"
display="flex"
flexDirection="column"
justifyContent="start"
alignItems="center"
>
<Box display="flex" flexDirection="row" justifyContent="center" alignItems="center">
{currentRecord?.step <= 1
? (
<>
<GhostStyledIcon
sx={{
width: "35px",
height: "35px",
animation: currentRecord?.step === 1 && 'rotateRightHand 2s ease-in-out infinite',
'@keyframes rotateRightHand': {
'0%': { transform: 'rotateX(360deg)' },
'15%': { transform: 'rotateX(360deg)' },
'50%': { transform: 'rotateX(180deg)' },
'85%': { transform: 'rotateX(0deg)' },
'100%': { transform: 'rotateX(0deg)' },
},
}}
viewBox="0 0 25 25"
component={ThumbUpIcon}
/>
<GhostStyledIcon
sx={{
width: "35px",
height: "35px",
animation: currentRecord?.step === 1 && 'rotateRightHand 2s ease-in-out infinite',
'@keyframes rotateRightHand': {
'0%': { transform: 'rotateX(0deg)' },
'15%': { transform: 'rotateX(0deg)' },
'50%': { transform: 'rotateX(180deg)' },
'85%': { transform: 'rotateX(360deg)' },
'100%': { transform: 'rotateX(360deg)' },
},
}}
viewBox="0 0 25 25"
component={ThumbDownAltIcon}
/>
</>
)
: (
<GhostStyledIcon
sx={{
width: "35px",
height: "35px",
}}
viewBox="0 0 25 25"
component={HandshakeIcon}
/>
)
}
</Box>
<Box display="flex" flexDirection="column" justifyContent="center" alignItems="center">
<Typography variant="caption">Slow Claps</Typography>
<Typography variant="caption">{currentRecord?.numberOfClaps ?? 0} / {authorities?.length ?? 0}</Typography>
</Box>
</Box>
<GhostStyledIcon
sx={{
transition: "all 0.3s ease",
opacity: currentRecord?.step < 2 && "0.2"
}}
component={ArrowRightIcon}
/>
<Box
sx={{
transition: "all 0.3s ease",
opacity: currentRecord?.step < 2 && "0.2",
transform: currentRecord?.step === 2 && "scale(1.2)",
color: currentRecord?.step === 2 && theme.colors.primary[300]
}}
width="120px"
display="flex"
flexDirection="column"
justifyContent="start"
alignItems="center"
>
{currentRecord?.applaused
? <>
<GhostStyledIcon
sx={{ width: "35px", height: "35px" }}
viewBox="0 0 25 25"
component={CheckCircleIcon}
/>
<Typography variant="caption">Applaused</Typography>
<Typography variant="caption">Check Receiver</Typography>
</>
: <>
<GhostStyledIcon
sx={{ width: "35px", height: "35px" }}
viewBox="0 0 25 25"
component={AssuredWorkloadIcon}
/>
<Typography variant="caption">Capital Backed</Typography>
<Typography variant="caption">
{(currentRecord?.clappedAmount ?? 0n) / 10n**18n} {ghstSymbol} ({currentRecord?.clappedPercentage ?? 0}%)
</Typography>
</>
}
</Box>
</Box>
<Box display="flex" flexDirection="column" gap="5px" padding="0.6rem 0">
<Box display="flex" flexDirection="row" justifyContent="space-between">
<Typography variant="body2">GHOST Epoch:</Typography>
<Typography variant="body2">{currentRecord?.sessionIndex}</Typography>
</Box>
<Box display="flex" flexDirection="row" justifyContent="space-between">
<Typography variant="body2">Accepted Bridge Risk:</Typography>
<Typography variant="body2">{currentRecord?.bridgeStability}%</Typography>
</Box>
<Box display="flex" flexDirection="row" justifyContent="space-between">
<Box display="flex" flexDirection="row">
<Typography variant="body2">Arguments Hash:</Typography>
<InfoTooltip message="A unique identifier for transaction parameters, represented as a hash generated by keccak256(receiver, amount, blockNumber, chainId)." />
</Box>
<Link
style={{ display: "flex", flexDirection: "row", justifyContent: "center", alignItems: "center" }}
onClick={() => copyToClipboard(hashedArguments ? hashedArguments : "", 2)}
>
<Typography variant="body2">
{hashedArguments ? sliceString(hashedArguments, 10, -8) : "0x"}
</Typography>
<GhostStyledIcon
sx={{ marginLeft: "5px", width: "12px", height: "12px" }}
viewBox="0 0 25 25"
component={copiedIndex === 2 ? CheckIcon : ContentPasteIcon}
/>
</Link>
</Box>
<hr style={{ width: "100%" }} />
<Box
display="flex"
flexDirection="row"
justifyContent="space-between"
>
<Typography variant="body2">Receiver Address:</Typography>
<Link
style={{ display: "flex", flexDirection: "row", justifyContent: "center", alignItems: "center" }}
onClick={() => copyToClipboard(currentRecord ? currentRecord.receiverAddress : "", 0)}
>
<Typography variant="body2">
{currentRecord ? sliceString(currentRecord.receiverAddress, 14, -5) : ""}
</Typography>
<GhostStyledIcon
sx={{ marginLeft: "5px", width: "12px", height: "12px" }}
viewBox="0 0 25 25"
component={copiedIndex === 0 ? CheckIcon : ContentPasteIcon}
/>
</Link>
</Box>
<Box display="flex" flexDirection="row" justifyContent="space-between">
<Typography variant="body2">Bridged Amount:</Typography>
<Typography variant="body2">{formatCurrency(
new DecimalBigNumber(
BigInt(currentRecord ? currentRecord.amount : "0"),
18
).toString(), 9, ghstSymbol)
}</Typography>
</Box>
<Box display="flex" flexDirection="row" justifyContent="space-between">
<Typography variant="body2">Executed at:</Typography>
<Typography variant="body2">{
new Date(currentRecord ? currentRecord.timestamp : 0).toLocaleString('en-US')
}</Typography>
</Box>
</Box>
<Box display="flex" flexDirection="column" gap="5px">
<PrimaryButton
fullWidth
loading={false}
onClick={() => removeStoredRecord()}
>
Erase Record
</PrimaryButton>
<Typography variant="body2" sx={{ fontStyle: "italic" }}>
This will permanently remove the bridge transaction record from the session storage, but it will not cancel the bridge transaction.
</Typography>
</Box>
</Box>
</Modal>
)
}
export const BridgeConfirmModal = ({
bridgeStability,
isOpen,
setClose,
handleButtonProceed
}) => {
const [isBridgingRiskChecked, setIsBridgingRiskChecked] = useState(false);
const [isBridgingRecipientChecked, setIsBridgingRecipientChecked] = useState(false);
const handleProceed = () => {
setIsBridgingRiskChecked(false);
setIsBridgingRecipientChecked(false);
handleButtonProceed();
}
return (
<Modal
maxWidth="450px"
minHeight="150px"
headerText="Bridge Confirmation"
open={isOpen}
onClose={setClose}
>
<Box gap="20px" display="flex" flexDirection="column" justifyContent="space-between" alignItems="center">
<Box width="100%" display="flex" flexDirection="column" alignItems="start">
<FormControlLabel
control={
<Checkbox
data-testid="acknowledge-bridge-stability"
checked={isBridgingRiskChecked}
onChange={event => setIsBridgingRiskChecked(event.target.checked)}
icon={<CheckBoxOutlineBlank viewBox="0 0 24 24" />}
checkedIcon={<CheckBoxOutlined viewBox="0 0 24 24" />}
/>
}
label={
<span>
{`I acknowledge bridging risk at ${bridgeStability}%.`}&nbsp;
<Link
sx={{
margin: "0px",
font: "inherit",
letterSpacing: "inherit",
textDecoration: "underline",
textUnderlineOffset: "0.23rem",
cursor: "pointer",
textDecorationThickness: "1px",
"&:hover": {
textDecoration: "underline",
}
}}
target="_blank"
rel="noopener noreferrer"
href="https://google.com"
>
Learn more.
</Link>
</span>
}
/>
<hr style={{ margin: "10px 0", width: "100%" }} />
<FormControlLabel
control={
<Checkbox
data-testid="acknowledge-bridge-stability"
checked={isBridgingRecipientChecked}
onChange={event => setIsBridgingRecipientChecked(event.target.checked)}
icon={<CheckBoxOutlineBlank viewBox="0 0 24 24" />}
checkedIcon={<CheckBoxOutlined viewBox="0 0 24 24" />}
/>
}
label="I confirm that recipient address is a self-custodial wallet, not an exchange, third party service, or smart-contract."
sx={{ '& .MuiFormControlLabel-label': { textAlign: "justify" } }}
/>
</Box>
<PrimaryButton fullWidth disabled={!isBridgingRiskChecked || !isBridgingRecipientChecked} onClick={handleProceed}>
Proceed Bridge
</PrimaryButton>
</Box>
</Modal>
)
}

View File

@ -1,45 +0,0 @@
import React from "react";
import { Box, Typography, useTheme } from "@mui/material";
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import GhostStyledIcon from "../../components/Icon/GhostIcon";
import Token from "../../components/Token/Token";
export const BridgeRoute = ({ coinName, chainTokenName, tokens }) => {
const theme = useTheme();
return (
<Box width="100%" display="flex" alignItems="center" flexDirection="row" flexWrap="wrap">
<Typography>Route:</Typography>
<Box display="flex" marginLeft="20px" gap="5px" alignItems="center">
{tokens?.map((token, index) => {
return (
<React.Fragment key={index}>
<RouteHop key={index} theme={theme} token={token} chainTokenName={chainTokenName} />
<GhostStyledIcon sx={{ width: "12px", height: "12px" }} component={ArrowForwardIosIcon} />
</React.Fragment>
)
})}
<RouteHop theme={theme} token={coinName} />
</Box>
</Box>
)
}
const RouteHop = ({ theme, token, chainTokenName, arrowNeeded }) => {
return (
<Box
display="inline-flex"
sx={{ backgroundColor: theme.colors.gray[600] }}
borderRadius="6px"
paddingX="9px"
paddingY="6.5px"
alignItems="center"
>
<Token chainTokenName={chainTokenName} name={token} sx={{ fontSize: "21px" }} />
<Typography fontSize="15px" lineHeight="24px" marginLeft="9px">
{token}
</Typography>
</Box>
)
}

View File

@ -1,242 +0,0 @@
import { useEffect, useState, useMemo } from "react";
import {
Box,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
LinearProgress,
} from "@mui/material";
import { useTheme } from "@mui/material/styles";
import WarningIcon from '@mui/icons-material/Warning';
import CancelIcon from '@mui/icons-material/Cancel';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import GhostStyledIcon from "../../components/Icon/GhostIcon";
import InfoTooltip from "../../components/Tooltip/InfoTooltip";
import { PrimaryButton } from "../../components/Button";
export const ValidatorTable = ({
currentTime,
currentBlock,
latestCommits,
isVerySmallScreen,
bridgeStability,
providerDetail,
}) => {
const theme = useTheme();
const stabilityColor = useMemo(() => {
const red = Math.round(255 * (1 - bridgeStability / 100));
const green = Math.round(255 * (bridgeStability / 100));
return `rgb(${red}, ${green}, 0)`;
}, [bridgeStability]);
return (
<Box width="100%" height="320px" display="flex" flexDirection="column" justifyContent="space-between">
{!providerDetail && <Box sx={{ borderRadius: "15px", background: theme.colors.paper.background, paddingTop: "40px" }} width="100%" height="100%" display="flex" justifyContent="center">
<Box padding="20px 30px" display="flex" flexDirection="column" justifyContent="space-around" alignItems="center">
<Typography sx={{ textAlign: "center" }} variant="h6">GHOST Wallet is not detected on your browser!</Typography>
<Typography sx={{ textAlign: "center" }}>Download GHOST Wallet Extension to see real-time validator stats for bridging transaction.</Typography>
<PrimaryButton onClick={() => window.open('https://git.ghostchain.io/ghostchain/ghost-extension-wallet/releases', '_blank', 'noopener,noreferrer')}>
Get GHOST Extension
</PrimaryButton>
</Box>
</Box>}
{providerDetail && <TableContainer
component={Paper}
className="custom-scrollbar"
sx={{
height: "320px",
overflowY: 'scroll',
msOverflowStyle: "thin !important",
scrollbarWidth: "thin !important",
}}
>
<Table sx={{ marginTop: "0px" }} stickyHeader aria-label="sticky available validators">
<TableHead>
<TableRow sx={{ height: "40px" }}>
<BridgeHeaderTableCell value="Validator" background={theme.colors.paper.cardHover} borderTopLeftRadius="3px" />
<BridgeHeaderTableCell value="Last Acitve" background={theme.colors.paper.cardHover} tooltip="GHOST Validators must submit block commitments every 10 minutes to remain active." />
<BridgeHeaderTableCell value="Block Height" background={theme.colors.paper.cardHover} tooltip="The latest EVM block height reported by the Validator." />
<BridgeHeaderTableCell value="Block Delayed" background={theme.colors.paper.cardHover} tooltip="Block delays under 4 hours are safe. Block delays over 4 hours risk bridge transaction failure." />
<BridgeHeaderTableCell value="Status" background={theme.colors.paper.cardHover} borderTopRightRadius="3px" tooltip="Active and disabled validators fore the current GHOST Epoch." />
</TableRow>
</TableHead>
<TableBody>
{latestCommits?.map((commit, index) => {
return (
<ValidatorRow
key={index}
colors={theme.colors}
currentTime={currentTime}
currentBlock={currentBlock}
index={index}
commit={commit}
/>
)
})}
</TableBody>
</Table>
</TableContainer>}
</Box>
)
}
const BridgeHeaderTableCell = ({
align="center",
borderTopRightRadius="0px",
borderTopLeftRadius="0px",
borderBottomRightRadius="0px",
borderBottomLeftRadius="0px",
background="transparent",
fontSize="12px",
padding="0px",
tooltip,
value
}) => {
return (
<TableCell
align={align}
style={{
borderTopRightRadius,
borderTopLeftRadius,
borderBottomRightRadius,
borderBottomLeftRadius,
background,
fontSize,
padding,
}}
>
<Box display="flex" justifyContent="center">
{value}
{tooltip && <InfoTooltip message={tooltip} />}
</Box>
</TableCell>
)
}
const BridgeTableCell = ({
align="center",
borderTopRightRadius="0px",
borderTopLeftRadius="0px",
borderBottomRightRadius="0px",
borderBottomLeftRadius="0px",
background="transparent",
fontSize="10px",
padding="0px",
value
}) => {
return (
<TableCell
align={align}
style={{
borderTopRightRadius,
borderTopLeftRadius,
borderBottomRightRadius,
borderBottomLeftRadius,
background,
fontSize,
padding,
}}
>
{value}
</TableCell>
)
}
const ValidatorRow = ({
colors,
index,
currentTime,
currentBlock,
commit,
}) => {
const background = index % 2 === 1 ? "" : colors.gray[750];
return (
<TableRow
sx={{ height: "30px" }}
id={index + `--vaidator`}
data-testid={index + `--vaidator`}
>
<BridgeTableCell align="center" value={sliceString(commit.validator, 10, 45)} background={background} />
<BridgeTableCell align="center" value={getTimeAgo(currentTime - (Number(commit?.lastUpdated) ?? 0))} background={background} />
<BridgeTableCell align="center" value={(commit?.lastStoredBlock ?? 0n).toLocaleString('en-US')} background={background} />
<BridgeTableCell align="center" value={blockDelayIcon(colors, currentBlock - (commit?.lastStoredBlock ?? 0n))} background={background} />
<BridgeTableCell align="center" value={statusIcon(colors, commit?.disabled)} background={background} />
</TableRow>
)
}
const sliceString = (string, first, second) => {
if (!string) return "";
return string.slice(0, first) + "..." + string.slice(second);
}
const getTimeAgo = (timestampDiff) => {
const seconds = Math.floor(timestampDiff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
if (seconds < 60) return `${seconds}s ago`;
if (minutes < 60) return `${minutes}m ago`;
if (hours < 24) return `${hours}h ago`;
return "long ago";
}
const blockDelayIcon = (colors, timestampDiff) => {
let color = colors.feedback.error;
let icon = CancelIcon;
if (timestampDiff < 900000n) {
color = colors.feedback.success;
icon = CheckCircleIcon;
} else if (timestampDiff < 2700000n) {
color = colors.feedback.warning;
icon = WarningIcon;
}
return (
<GhostStyledIcon
sx={{
marginLeft: "5px",
width: "16px",
height: "16px",
fill: color
}}
viewBox="0 0 25 25"
component={icon}
/>
)
}
const statusIcon = (colors, disabled) => {
let color = colors.feedback.success;
let icon = CheckCircleIcon;
if (disabled === true) {
color = colors.feedback.error;
icon = CancelIcon;
}
return (
<GhostStyledIcon
sx={{
marginLeft: "5px",
width: "16px",
height: "16px",
fill: color
}}
viewBox="0 0 25 25"
component={icon}
/>
)
}

View File

@ -26,7 +26,7 @@ import { Tab, Tabs } from "../../components/Tabs/Tabs";
import { import {
UNISWAP_V2_ROUTER, UNISWAP_V2_ROUTER,
UNISWAP_V2_FACTORY, UNISWAP_V2_FACTORY,
RESERVE_ADDRESSES, DAI_ADDRESSES,
FTSO_ADDRESSES, FTSO_ADDRESSES,
} from "../../constants/addresses"; } from "../../constants/addresses";
import { useTokenSymbol } from "../../hooks/tokens"; import { useTokenSymbol } from "../../hooks/tokens";
@ -56,7 +56,7 @@ const Dex = ({ chainId, address, connect }) => {
const [slippage, setSlippage] = useState(localStorage.getItem("dex-slippage") || "5"); const [slippage, setSlippage] = useState(localStorage.getItem("dex-slippage") || "5");
const [formatDecimals, setFormatDecimals] = useState(localStorage.getItem("dex-decimals") || "5"); const [formatDecimals, setFormatDecimals] = useState(localStorage.getItem("dex-decimals") || "5");
const [tokenAddressTop, setTokenAddressTop] = useState(RESERVE_ADDRESSES[chainId]); const [tokenAddressTop, setTokenAddressTop] = useState(DAI_ADDRESSES[chainId]);
const [tokenAddressBottom, setTokenAddressBottom] = useState(FTSO_ADDRESSES[chainId]); const [tokenAddressBottom, setTokenAddressBottom] = useState(FTSO_ADDRESSES[chainId]);
const { symbol: tokenNameTop } = useTokenSymbol(chainId, tokenAddressTop); const { symbol: tokenNameTop } = useTokenSymbol(chainId, tokenAddressTop);
@ -75,8 +75,8 @@ const Dex = ({ chainId, address, connect }) => {
setTokenAddressTop(currentQueryParameters.get("from")); setTokenAddressTop(currentQueryParameters.get("from"));
newQueryParameters.set("from", currentQueryParameters.get("from")); newQueryParameters.set("from", currentQueryParameters.get("from"));
} else { } else {
setTokenAddressTop(RESERVE_ADDRESSES[chainId]); setTokenAddressTop(DAI_ADDRESSES[chainId]);
newQueryParameters.set("from", RESERVE_ADDRESSES[chainId]); newQueryParameters.set("from", DAI_ADDRESSES[chainId]);
} }
if (currentQueryParameters.has("to")) { if (currentQueryParameters.has("to")) {

View File

@ -187,11 +187,11 @@ const SwapContainer = ({
> >
<Box width="100%" display="flex" justifyContent="space-between"> <Box width="100%" display="flex" justifyContent="space-between">
<Typography fontSize="12px" lineHeight="15px">Current price:</Typography> <Typography fontSize="12px" lineHeight="15px">Current price:</Typography>
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(currentPrice, formatDecimals, tokenNameTop)}</Typography> <Typography fontSize="12px" lineHeight="15px">{formatCurrency(currentPrice, formatDecimals)}</Typography>
</Box> </Box>
<Box width="100%" display="flex" justifyContent="space-between"> <Box width="100%" display="flex" justifyContent="space-between">
<Typography fontSize="12px" lineHeight="15px">Next price:</Typography> <Typography fontSize="12px" lineHeight="15px">Next price:</Typography>
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(nextPrice === "" ? currentPrice : nextPrice, formatDecimals, tokenNameTop)}</Typography> <Typography fontSize="12px" lineHeight="15px">{formatCurrency(nextPrice === "" ? currentPrice : nextPrice, formatDecimals)}</Typography>
</Box> </Box>
<Box width="100%" display="flex" justifyContent="space-between"> <Box width="100%" display="flex" justifyContent="space-between">
<Typography fontSize="12px" lineHeight="15px">Transaction deadline:</Typography> <Typography fontSize="12px" lineHeight="15px">Transaction deadline:</Typography>

View File

@ -1,5 +1,4 @@
import { useState, useEffect, useMemo } from "react"; import { useState, useEffect, useMemo } from "react";
import { useConfig } from "wagmi";
import { import {
Divider, Divider,
Typography, Typography,
@ -20,8 +19,7 @@ import TokenStack from "../../components/TokenStack/TokenStack";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { formatNumber } from "../../helpers/"; import { formatNumber } from "../../helpers/";
import { useBalance, useTokenSymbol } from "../../hooks/tokens"; import { useBalance, useTokenSymbol } from "../../hooks/tokens";
import { isNetworkLegacy } from "../../constants"; import { DAI_ADDRESSES, FTSO_ADDRESSES, STNK_ADDRESSES, GHST_ADDRESSES } from "../../constants/addresses";
import { RESERVE_ADDRESSES, FTSO_ADDRESSES, STNK_ADDRESSES, GHST_ADDRESSES } from "../../constants/addresses";
const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }) => { const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }) => {
const isSmallScreen = useMediaQuery("(max-width: 599px)"); const isSmallScreen = useMediaQuery("(max-width: 599px)");
@ -39,19 +37,16 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }
const { symbol: searchSymbol } = useTokenSymbol(chainId, address); const { symbol: searchSymbol } = useTokenSymbol(chainId, address);
const { balance: searchBalance } = useBalance(chainId, address, account); const { balance: searchBalance } = useBalance(chainId, address, account);
const { balance: reserveBalance } = useBalance(chainId, "RESERVE", account); const { balance: daiBalance } = useBalance(chainId, "GDAI", account);
const { balance: ftsoBalance } = useBalance(chainId, "FTSO", account); const { balance: ftsoBalance } = useBalance(chainId, "FTSO", account);
const { balance: stnkBalance } = useBalance(chainId, "STNK", account); const { balance: stnkBalance } = useBalance(chainId, "STNK", account);
const { balance: ghstBalance } = useBalance(chainId, "GHST", account); const { balance: ghstBalance } = useBalance(chainId, "GHST", account);
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE"); const { symbol: daiSymbol } = useTokenSymbol(chainId, "GDAI");
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO"); const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK"); const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK");
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST"); const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
const config = useConfig();
const nativeSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
const searchToken = useMemo(() => { const searchToken = useMemo(() => {
return [{ return [{
name: searchSymbol, name: searchSymbol,
@ -64,10 +59,10 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }
const knownTokens = useMemo(() => { const knownTokens = useMemo(() => {
return [ return [
{ {
name: reserveSymbol, name: daiSymbol,
icons: isNetworkLegacy(chainId) ? ["GDAI"] : [nativeSymbol], icons: ["GDAI"],
balance: reserveBalance, balance: daiBalance,
address: RESERVE_ADDRESSES[chainId] address: DAI_ADDRESSES[chainId]
}, },
{ {
name: ftsoSymbol, name: ftsoSymbol,
@ -88,7 +83,7 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }
address: GHST_ADDRESSES[chainId] address: GHST_ADDRESSES[chainId]
} }
] ]
}, [reserveSymbol, ftsoSymbol, stnkSymbol, ghstSymbol, reserveBalance, ftsoBalance, stnkBalance, ghstBalance]); }, [daiSymbol, ftsoSymbol, stnkSymbol, ghstSymbol, daiBalance, ftsoBalance, stnkBalance, ghstBalance]);
useEffect(() => { useEffect(() => {
if (isAddress(userInput)) { if (isAddress(userInput)) {

View File

@ -11,7 +11,7 @@ import TokenStack from "../../components/TokenStack/TokenStack";
import { PrimaryButton } from "../../components/Button"; import { PrimaryButton } from "../../components/Button";
import { Tab, Tabs } from "../../components/Tabs/Tabs"; import { Tab, Tabs } from "../../components/Tabs/Tabs";
import { RESERVE_ADDRESSES } from "../../constants/addresses"; import { DAI_ADDRESSES } from "../../constants/addresses";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { formatCurrency, formatNumber } from "../../helpers"; import { formatCurrency, formatNumber } from "../../helpers";
@ -44,14 +44,14 @@ const Faucet = ({ chainId, address, config, connect }) => {
symbol: "", symbol: "",
}) })
const reserveConversionRate = useConversionRate(chainId, "RESERVE"); const daiConversionRate = useConversionRate(chainId, "GDAI");
const accumulatedDonation = useAccumulatedDonation(chainId, "RESERVE"); const accumulatedDonation = useAccumulatedDonation(chainId, "GDAI");
const { balance: reserveBalance, refetch: reserveBalanceRefetch } = useTokenBalance(chainId, "RESERVE", address); const { balance: daiBalance, refetch: daiBalanceRefetch } = useTokenBalance(chainId, "GDAI", address);
const { data: nativeBalance, refetch: balanceRefetch } = useBalance({ address }); const { data: nativeBalance, refetch: balanceRefetch } = useBalance({ address });
const { data: contractBalance, refetch: contractBalanceRefetch } = useBalance({ address: RESERVE_ADDRESSES[chainId] }); const { data: contractBalance, refetch: contractBalanceRefetch } = useBalance({ address: DAI_ADDRESSES[chainId] });
const { totalSupply: reserveTotalSupply, refetch: refetchReserveTotalSupply } = useTotalSupply(chainId, "RESERVE"); const { totalSupply: reserveTotalSupply, refetch: refetchReserveTotalSupply } = useTotalSupply(chainId, "GDAI");
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE"); const { symbol: faucetSymbol } = useTokenSymbol(chainId, "GDAI");
useEffect(() => { useEffect(() => {
ReactGA.send({ hitType: "pageview", page: "/faucet" }); ReactGA.send({ hitType: "pageview", page: "/faucet" });
@ -93,10 +93,10 @@ const Faucet = ({ chainId, address, config, connect }) => {
}, [amount, balance, nativeInfo]) }, [amount, balance, nativeInfo])
const estimatedAmountIn = useMemo(() => { const estimatedAmountIn = useMemo(() => {
const rate = new DecimalBigNumber(reserveConversionRate.toString(), 0); const rate = new DecimalBigNumber(daiConversionRate.toString(), 0);
const value = new DecimalBigNumber(amount, nativeInfo.decimals); const value = new DecimalBigNumber(amount, nativeInfo.decimals);
return value.mul(rate); return value.mul(rate);
}, [amount, reserveConversionRate, nativeInfo]); }, [amount, daiConversionRate, nativeInfo]);
const contractBalanceFree = useMemo(() => { const contractBalanceFree = useMemo(() => {
const realContractBalance = contractBalance ? contractBalance.value : 0n; const realContractBalance = contractBalance ? contractBalance.value : 0n;
@ -129,7 +129,7 @@ const Faucet = ({ chainId, address, config, connect }) => {
} }
await balanceRefetch(); await balanceRefetch();
await reserveBalanceRefetch(); await daiBalanceRefetch();
await contractBalanceRefetch(); await contractBalanceRefetch();
await refetchReserveTotalSupply(); await refetchReserveTotalSupply();
setAmount(""); setAmount("");
@ -159,7 +159,7 @@ const Faucet = ({ chainId, address, config, connect }) => {
<meta name="twitter:image" content="https://ghostchain.io/wp-content/uploads/2025/03/ghostFaucet-Featured_Image.png" /> <meta name="twitter:image" content="https://ghostchain.io/wp-content/uploads/2025/03/ghostFaucet-Featured_Image.png" />
</Helmet> </Helmet>
<PageTitle name={`${reserveSymbol} Faucet`} subtitle={`Swap Sepolia ${nativeInfo.symbol} for ${reserveSymbol}.`} /> <PageTitle name={`${faucetSymbol} Faucet`} subtitle={`Swap Sepolia ${nativeInfo.symbol} for ${faucetSymbol}.`} />
<Container <Container
style={{ style={{
paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem", paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
@ -188,7 +188,7 @@ const Faucet = ({ chainId, address, config, connect }) => {
</Tabs> </Tabs>
{!isSemiSmallScreen && <PrimaryButton {!isSemiSmallScreen && <PrimaryButton
variant="text" variant="text"
href={`${scanInfo.url}/token/${RESERVE_ADDRESSES[chainId]}`} href={`${scanInfo.url}/token/${DAI_ADDRESSES[chainId]}`}
> >
Check on {scanInfo.name} Check on {scanInfo.name}
</PrimaryButton>} </PrimaryButton>}
@ -211,14 +211,14 @@ const Faucet = ({ chainId, address, config, connect }) => {
{!isMint && <SwapCard {!isMint && <SwapCard
id={`faucet-sepolia-eth`} id={`faucet-sepolia-eth`}
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"} inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
tokenName={reserveSymbol} tokenName={faucetSymbol}
token={<TokenStack tokens={[reserveSymbol]} sx={{ fontSize: "21px" }} />} token={<TokenStack tokens={[faucetSymbol]} sx={{ fontSize: "21px" }} />}
info={`${formatCurrency(reserveBalance.toString(), 4, reserveSymbol)}`} info={`${formatCurrency(daiBalance.toString(), 4, faucetSymbol)}`}
value={amount} value={amount}
onChange={event => setAmount(event.currentTarget.value)} onChange={event => setAmount(event.currentTarget.value)}
inputProps={{ "data-testid": "fromInput" }} inputProps={{ "data-testid": "fromInput" }}
endString={"Max"} endString={"Max"}
endStringOnClick={() => setAmount(reserveBalance.toString())} endStringOnClick={() => setAmount(daiBalance.toString())}
/>} />}
<Box <Box
mb="20px" mb="20px"
@ -231,15 +231,15 @@ const Faucet = ({ chainId, address, config, connect }) => {
<> <>
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}> <Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">{nativeInfo.symbol} multiplier:</Typography>} {!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">{nativeInfo.symbol} multiplier:</Typography>}
<Typography fontSize="12px" lineHeight="15px">{formatNumber(reserveConversionRate, 2)}</Typography> <Typography fontSize="12px" lineHeight="15px">{formatNumber(daiConversionRate, 2)}</Typography>
</Box> </Box>
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}> <Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">You will get:</Typography>} {!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">You will get:</Typography>}
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(estimatedAmountIn, 5, reserveSymbol)}</Typography> <Typography fontSize="12px" lineHeight="15px">{formatCurrency(estimatedAmountIn, 5, faucetSymbol)}</Typography>
</Box> </Box>
<Box display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}> <Box display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Your {reserveSymbol} balance:</Typography>} {!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Your {faucetSymbol} balance:</Typography>}
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(reserveBalance, 5, reserveSymbol)}</Typography> <Typography fontSize="12px" lineHeight="15px">{formatCurrency(daiBalance, 5, faucetSymbol)}</Typography>
</Box> </Box>
</> </>
)} )}
@ -267,7 +267,7 @@ const Faucet = ({ chainId, address, config, connect }) => {
preparedAmount?._value === 0n || preparedAmount?._value === 0n ||
isPending || isPending ||
(isMint && balance?.lt(preparedAmount)) || (isMint && balance?.lt(preparedAmount)) ||
(!isMint && reserveBalance?.lt(preparedAmount)) (!isMint && daiBalance?.lt(preparedAmount))
) )
} }
loading={isPending} loading={isPending}

View File

@ -16,33 +16,34 @@ import { SecondaryButton } from "../../../components/Button";
import TokenStack from "../../../components/TokenStack/TokenStack"; import TokenStack from "../../../components/TokenStack/TokenStack";
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
import { formatCurrency } from "../../../helpers"; import { formatCurrency } from "../../../helpers";
import { tokenNameConverter } from "../../../helpers/tokenConverter";
import { isNetworkLegacy } from "../../../constants";
import { useLpValuation } from "../../../hooks/treasury"; import { useLpValuation } from "../../../hooks/treasury";
import { useTotalSupply, useTokenSymbol } from "../../../hooks/tokens"; import { useTotalSupply, useTokenSymbol } from "../../../hooks/tokens";
import { RESERVE_ADDRESSES, FTSO_ADDRESSES } from "../../../constants/addresses"; import {
DAI_ADDRESSES,
FTSO_ADDRESSES,
} from "../../../constants/addresses";
const FarmPools = ({ chainId }) => { const FarmPools = ({ chainId }) => {
const isSmallScreen = useMediaQuery("(max-width: 775px)"); const isSmallScreen = useMediaQuery("(max-width: 775px)");
const { totalSupply: reserveFtsoUniTotalSupply } = useTotalSupply(chainId, "RESERVE_FTSO"); const { totalSupply: daiFtsoUniTotalSupply } = useTotalSupply(chainId, "GDAI_FTSO");
const reserveFtsoUniValuation = useLpValuation(chainId, "RESERVE_FTSO", reserveFtsoUniTotalSupply._value); const daiFtsoUniValuation = useLpValuation(chainId, "GDAI_FTSO", daiFtsoUniTotalSupply._value);
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE"); const { symbol: daiSymbol } = useTokenSymbol(chainId, "GDAI");
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO"); const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const pools = [ const pools = [
{ {
icons: ["FTSO", isNetworkLegacy(chainId) ? "GDAI" : tokenNameConverter(chainId, reserveSymbol)], icons: ["FTSO", "GDAI"],
name: `${ftsoSymbol}-${reserveSymbol}`, name: `${ftsoSymbol}-${daiSymbol}`,
dex: "Uniswap V2", dex: "Uniswap V2",
url: "/dex/uniswap", url: "/dex/uniswap",
tvl: reserveFtsoUniValuation, tvl: daiFtsoUniValuation,
params: createSearchParams({ params: createSearchParams({
pool: "true", pool: "true",
from: `${RESERVE_ADDRESSES[chainId]}`, from: `${DAI_ADDRESSES[chainId]}`,
to: `${FTSO_ADDRESSES[chainId]}`, to: `${FTSO_ADDRESSES[chainId]}`,
}) })
}, },

View File

@ -1,22 +1,19 @@
import { Grid, Box, Typography, useTheme } from "@mui/material"; import { Grid, Box, Typography, useTheme } from "@mui/material";
import { useAccount, useConfig, useBalance as useBalanceNative } from "wagmi"; import { useAccount } from "wagmi";
import { useNavigate, createSearchParams } from "react-router-dom"; import { useNavigate, createSearchParams } from "react-router-dom";
import Token from "../../../components/Token/Token"; import Token from "../../../components/Token/Token";
import { SecondaryButton } from "../../../components/Button"; import { SecondaryButton } from "../../../components/Button";
import { formatNumber, formatCurrency } from "../../../helpers"; import { formatNumber, formatCurrency } from "../../../helpers";
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
import { isNetworkLegacy } from "../../../constants"
import { useBalance, useTokenSymbol } from "../../../hooks/tokens"; import { useBalance, useTokenSymbol } from "../../../hooks/tokens";
import { import {
useFtsoPrice, useFtsoPrice,
useStnkPrice, useStnkPrice,
useGhstPrice, useGhstPrice,
useReservePrice, useDaiPrice,
useNativePrice,
} from "../../../hooks/prices"; } from "../../../hooks/prices";
import { tokenNameConverter } from "../../../helpers/tokenConverter";
const TokenTab = ({ isMobileScreen, theme, tokenName, tokenUrl, tokenUrlParams, balance, price, description }) => { const TokenTab = ({ isMobileScreen, theme, tokenName, tokenUrl, tokenUrlParams, balance, price, description }) => {
const navigate = useNavigate(); const navigate = useNavigate();
@ -77,26 +74,20 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
const theme = useTheme(); const theme = useTheme();
const { address } = useAccount(); const { address } = useAccount();
const config = useConfig();
const nativeSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
const networkName = config?.getClient()?.chain?.name;
const nativePrice = useNativePrice(chainId);
const ftsoPrice = useFtsoPrice(chainId); const ftsoPrice = useFtsoPrice(chainId);
const stnkPrice = useStnkPrice(chainId); const stnkPrice = useStnkPrice(chainId);
const ghstPrice = useGhstPrice(chainId); const ghstPrice = useGhstPrice(chainId);
const reservePrice = useReservePrice(chainId); const daiPrice = useDaiPrice(chainId);
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE"); const { symbol: daiSymbol } = useTokenSymbol(chainId, "GDAI");
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO"); const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK"); const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK");
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST"); const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
const { data: nativeBalance } = useBalanceNative({ address });
const { balance: ftsoBalance, contractAddress: ftsoAddress } = useBalance(chainId, "FTSO", address); const { balance: ftsoBalance, contractAddress: ftsoAddress } = useBalance(chainId, "FTSO", address);
const { balance: stnkBalance, contractAddress: stnkAddress } = useBalance(chainId, "STNK", address); const { balance: stnkBalance, contractAddress: stnkAddress } = useBalance(chainId, "STNK", address);
const { balance: ghstBalance, contractAddress: ghstAddress } = useBalance(chainId, "GHST", address); const { balance: ghstBalance, contractAddress: ghstAddress } = useBalance(chainId, "GHST", address);
const { balance: reserveBalance, contractAddress: reserveAddress } = useBalance(chainId, "RESERVE", address); const { balance: daiBalance, contractAddress: daiAddress } = useBalance(chainId, "GDAI", address);
return ( return (
<Grid container spacing={0} justifyContent={"center"}> <Grid container spacing={0} justifyContent={"center"}>
@ -105,7 +96,7 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
isMobileScreen={isMobileScreen} isMobileScreen={isMobileScreen}
tokenUrl="/dex/uniswap" tokenUrl="/dex/uniswap"
tokenUrlParams={createSearchParams({ tokenUrlParams={createSearchParams({
from: `${reserveAddress}`, from: `${daiAddress}`,
to: `${ftsoAddress}`, to: `${ftsoAddress}`,
})} })}
theme={theme} theme={theme}
@ -118,7 +109,7 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
isMobileScreen={isMobileScreen} isMobileScreen={isMobileScreen}
tokenUrl="/dex/uniswap" tokenUrl="/dex/uniswap"
tokenUrlParams={createSearchParams({ tokenUrlParams={createSearchParams({
from: `${reserveAddress}`, from: `${daiAddress}`,
to: `${stnkAddress}`, to: `${stnkAddress}`,
})} })}
theme={theme} theme={theme}
@ -131,7 +122,7 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
isMobileScreen={isMobileScreen} isMobileScreen={isMobileScreen}
tokenUrl="/dex/uniswap" tokenUrl="/dex/uniswap"
tokenUrlParams={createSearchParams({ tokenUrlParams={createSearchParams({
from: `${reserveAddress}`, from: `${daiAddress}`,
to: `${ghstAddress}`, to: `${ghstAddress}`,
})} })}
theme={theme} theme={theme}
@ -142,32 +133,15 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
/> />
<TokenTab <TokenTab
isMobileScreen={isMobileScreen} isMobileScreen={isMobileScreen}
tokenUrl={isNetworkLegacy(chainId) ? "/faucet" : "/wrapper"} tokenUrl="/faucet"
tokenUrlParams="" tokenUrlParams=""
theme={theme} theme={theme}
tokenName={reserveSymbol} tokenName={daiSymbol}
balance={reserveBalance} balance={daiBalance}
price={reservePrice} price={daiPrice}
description={isNetworkLegacy(chainId) description={`${ftsoSymbol} is backed by a treasury reserve of crypto assets, with ${daiSymbol} being the primary and most liquid asset.`}
? `${ftsoSymbol} is backed by a treasury reserve of crypto assets, with ${reserveSymbol} being the primary and most liquid asset.`
: `${reserveSymbol} (Wrapped ${nativeSymbol}) is an ERC-20 token that represents ${nativeSymbol} and is pegged 1:1 to the value of ${nativeSymbol}.`
}
/> />
</Box> </Box>
{!isNetworkLegacy(chainId) && (
<Box width="100%" mt="25px">
<TokenTab
isMobileScreen={true}
tokenUrl={isNetworkLegacy(chainId) ? "/faucet" : "/wrapper"}
tokenUrlParams=""
theme={theme}
tokenName={nativeSymbol}
balance={new DecimalBigNumber(nativeBalance?.value ?? 0n, 18)}
price={reservePrice}
description={`${nativeSymbol} is the native currency of the ${networkName} Network, functioning as the backing asset for the ghostDAO.`}
/>
</Box>
)}
</Grid> </Grid>
) )
} }

View File

@ -1,260 +0,0 @@
import { useState, useEffect, useMemo } from "react";
import { Box, Container, Typography, useMediaQuery } from "@mui/material";
import { useConfig, useBalance } from "wagmi";
import { Helmet } from "react-helmet";
import ReactGA from "react-ga4";
import PageTitle from "../../components/PageTitle/PageTitle";
import Paper from "../../components/Paper/Paper";
import SwapCard from "../../components/Swap/SwapCard";
import TokenStack from "../../components/TokenStack/TokenStack";
import { PrimaryButton } from "../../components/Button";
import { Tab, Tabs } from "../../components/Tabs/Tabs";
import { RESERVE_ADDRESSES } from "../../constants/addresses";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { formatCurrency, formatNumber } from "../../helpers";
import {
useBalance as useTokenBalance,
useTokenSymbol,
useTotalSupply,
useConversionRate,
useAccumulatedDonation,
depositNative,
withdrawWeth
} from "../../hooks/tokens";
const WethWrapper = ({ chainId, address, config, connect }) => {
const isSmallScreen = useMediaQuery("(max-width: 650px)");
const isSemiSmallScreen = useMediaQuery("(max-width: 480px)");
const isVerySmallScreen = useMediaQuery("(max-width: 379px)");
const [chainName, setChainName] = useState("");
const [isMint, setIsMint] = useState(true);
const [isPending, setIsPending] = useState(false);
const [balance, setBalance] = useState(new DecimalBigNumber(0, 0));
const [amount, setAmount] = useState("");
const [scanInfo, setScanInfo] = useState({
name: "",
url: "",
});
const [nativeInfo, setNativeInfo] = useState({
decimals: 18,
name: "",
symbol: "",
})
const { balance: reserveBalance, refetch: reserveBalanceRefetch } = useTokenBalance(chainId, "RESERVE", address);
const { data: nativeBalance, refetch: balanceRefetch } = useBalance({ address });
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE");
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: "/wrapper" });
}, [])
useEffect(() => {
const value = nativeBalance ? nativeBalance.value : 0n;
const decimals = nativeBalance ? nativeBalance.decimals : 18;
setBalance(new DecimalBigNumber(value, decimals));
}, [nativeBalance]);
useEffect(() => {
let scanName = "";
let scanUrl = "";
const client = config?.getClient();
scanName = client?.chain?.blockExplorers?.default?.name;
scanUrl = client?.chain?.blockExplorers?.default?.url;
setScanInfo({
name: scanName,
url: scanUrl,
})
setChainName(client?.chain?.name);
setNativeInfo(client?.chain?.nativeCurrency)
}, [chainId]);
const changeIsMinted = (value) => {
setAmount("");
setIsMint(value);
}
const preparedAmount = useMemo(() => {
if (address === "") new DecimalBigNumber("0", 0);
const decimals = isMint ? nativeInfo.decimals : 18;
return new DecimalBigNumber(amount, decimals);
}, [amount, balance, nativeInfo])
const estimatedAmountIn = useMemo(() => {
return new DecimalBigNumber(amount, nativeInfo.decimals);
}, [amount, nativeInfo]);
const estimatedAmountOut = useMemo(() => {
return new DecimalBigNumber(amount, nativeInfo.decimals);
}, [amount, nativeInfo]);
const actionOrConnect = async () => {
if (address === "") {
connect();
} else {
setIsPending(true);
if (isMint) {
await depositNative(chainId, address, preparedAmount._value.toString());
} else {
await withdrawWeth(chainId, address, preparedAmount._value.toString());
}
await balanceRefetch();
await reserveBalanceRefetch();
setAmount("");
setIsPending(false);
}
}
return (
<Box height="calc(100vh - 43px)">
<Helmet>
<title>ghostWrapper | WETH9</title>
<meta name="description" content="Standard WETH9 wrapper. Convert native coin to WETH9 representation directly to your wallet." />
<meta name="keywords" content="weth, weth9, ghostFaucet, web3 faucet, ethereum, sepolia, polygon, bnb, bsc, AVAX" />
<meta property="og:image" content="https://ghostchain.io/wp-content/uploads/2025/03/ghostFaucet-Featured_Image.png" />
<meta property="og:title" content="ghostDAO | The DeFi 2.0 cross-chain reserve currency" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:type" content="website" />
<meta property="og:description" content="Standard WETH9 wrapper. Convert native coin to WETH9 representation directly to your wallet." />
<meta name="twitter:card" content="summary" />
<meta name="twitter:site" content="@realGhostChain" />
<meta name="twitter:title" content="ghostDAO | The DeFi 2.0 cross-chain reserve currency" />
<meta name="twitter:description" content="Standard WETH9 wrapper. Convert native coin to WETH9 representation directly to your wallet." />
<meta name="twitter:image" content="https://ghostchain.io/wp-content/uploads/2025/03/ghostFaucet-Featured_Image.png" />
</Helmet>
<PageTitle name={`${reserveSymbol} Wrapper`} subtitle={`Wrap ${nativeInfo.symbol} into ${reserveSymbol}.`} />
<Container
style={{
paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
paddingRight: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
display: "flex",
justifyContent: "center",
alignItems: "center",
height: "calc(100vh - 153px)"
}}
>
<Box width="100%" maxWidth="476px" display="flex" alignItems="center" justifyContent="center" flexDirection="column">
<Paper
headerContent={
<Box alignItems="center" justifyContent="space-between" display="flex" width="100%">
<Tabs
centered
textColor="primary"
indicatorColor="primary"
value={isMint ? 0 : 1}
aria-label="Faucet menu"
onChange={(_, view) => changeIsMinted(view === 0)}
TabIndicatorProps={{ style: { display: "none" } }}
>
<Tab aria-label="faucet-mint-button" label="Wrap" style={{ fontSize: "1.5rem" }} />
<Tab aria-label="faucet-burn-button" label="Unwrap" style={{ fontSize: "1.5rem" }} />
</Tabs>
{!isSemiSmallScreen && <PrimaryButton
variant="text"
href={`${scanInfo.url}/token/${RESERVE_ADDRESSES[chainId]}`}
>
Check on {scanInfo.name}
</PrimaryButton>}
</Box>
}
enableBackground
fullWidth
>
<Box>
{isMint && <SwapCard
id={`faucet-sepolia-eth`}
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
tokenName={nativeInfo.symbol}
token={<TokenStack tokens={[nativeInfo.symbol]} sx={{ fontSize: "21px" }} />}
info={`${isSemiSmallScreen ? "" : "Balance: "}${formatCurrency(balance.toString(), 4, nativeInfo.symbol)}`}
value={amount}
onChange={event => setAmount(event.currentTarget.value)}
inputProps={{ "data-testid": "fromInput" }}
/>}
{!isMint && <SwapCard
id={`faucet-sepolia-eth`}
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
tokenName={reserveSymbol}
token={<TokenStack tokens={[reserveSymbol]} sx={{ fontSize: "21px" }} />}
info={`${formatCurrency(reserveBalance.toString(), 4, reserveSymbol)}`}
value={amount}
onChange={event => setAmount(event.currentTarget.value)}
inputProps={{ "data-testid": "fromInput" }}
endString={"Max"}
endStringOnClick={() => setAmount(reserveBalance.toString())}
/>}
<Box
mb="20px"
mt="20px"
flexDirection="column"
display="flex"
justifyContent="space-between"
>
{isMint && (
<>
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">You will get:</Typography>}
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(estimatedAmountIn, 5, reserveSymbol)}</Typography>
</Box>
<Box display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Your {reserveSymbol} balance:</Typography>}
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(reserveBalance, 5, reserveSymbol)}</Typography>
</Box>
</>
)}
{!isMint && (
<>
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">You will get:</Typography>}
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(estimatedAmountOut, 5, nativeInfo.symbol)}</Typography>
</Box>
<Box display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Your {nativeInfo.symbol} balance:</Typography>}
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(balance, 5, nativeInfo.symbol)}</Typography>
</Box>
</>
)}
</Box>
<PrimaryButton
fullWidth
disabled={
address !== "" && (
preparedAmount?._value === 0n ||
isPending ||
(isMint && balance?.lt(preparedAmount)) ||
(!isMint && reserveBalance?.lt(preparedAmount))
)
}
loading={isPending}
onClick={() => actionOrConnect()}
>
{address === "" ?
"Connect"
:
isMint ? "Wrap" : "Unwrap"
}
</PrimaryButton>
</Box>
</Paper>
</Box>
</Container>
</Box>
)
}
export default WethWrapper;

View File

@ -41,10 +41,3 @@ export const formatNumber = (number, precision = 0) => {
export const sortBondsByDiscount = (bonds) => { export const sortBondsByDiscount = (bonds) => {
return Array.from(bonds).filter((bond) => !bond.isSoldOut).sort((a, b) => (a.discount.gt(b.discount) ? -1 : 1)); return Array.from(bonds).filter((bond) => !bond.isSoldOut).sort((a, b) => (a.discount.gt(b.discount) ? -1 : 1));
}; };
export const timeConverter = (time) => {
const seconds = Number(time);
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return `${mins}m ${secs < 10 ? '0' : ''}${secs}s`;
}

View File

@ -1,10 +0,0 @@
export const tokenNameConverter = (chainId, name) => {
if (name?.toUpperCase() === "WETH") {
switch (chainId) {
case 63:
name = "wmETC"
break;
}
}
return name;
}

View File

@ -4,7 +4,7 @@ import { createClient } from "@polkadot-api/substrate-client"
import { getObservableClient } from "@polkadot-api/observable-client" import { getObservableClient } from "@polkadot-api/observable-client"
import useSWR from "swr" import useSWR from "swr"
const DEFAULT_CHAIN_ID = "0x5e1190682f1a6409cdfd691c0b23a6db792864d8994e591e9c19a31d8163989f" const DEFAULT_CHAIN_ID = "0xa217f4ee58a944470e9633ca5bd6d28a428ed64cd9b6f3e413565f359f89af90"
const UnstableProvider = createContext(null) const UnstableProvider = createContext(null)
export const useUnstableProvider = () => useContext(UnstableProvider) export const useUnstableProvider = () => useContext(UnstableProvider)

View File

@ -6,9 +6,4 @@ export * from "./useClapsInSession";
export * from "./useApplauseThreshold"; export * from "./useApplauseThreshold";
export * from "./useReceivedClaps"; export * from "./useReceivedClaps";
export * from "./useAuthorities"; export * from "./useAuthorities";
export * from "./useValidators"; export * from "./useApplausesForTransaction";
export * from "./useDisabledValidators";
export * from "./useBlockCommitments";
export * from "./useApplauseDetails";
export * from "./useBabeSlots";
export * from "./useErasTotalStaked";

View File

@ -6,41 +6,42 @@ import { fromHex } from "@polkadot-api/utils";
import { useUnstableProvider } from "./UnstableProvider" import { useUnstableProvider } from "./UnstableProvider"
import { useMetadata } from "./MetadataProvider" import { useMetadata } from "./MetadataProvider"
export const useApplauseDetails = ({ currentSession, argsHash }) => { export const useApplausesForTransaction = ({ currentSession, txHash, argsHash }) => {
const { chainHead$, chainId } = useUnstableProvider() const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata() const metadata = useMetadata()
const { data: applauseDetails } = useSWRSubscription( const { data: applausesForTransaction } = useSWRSubscription(
chainHead$ && argsHash && currentSession && chainId && metadata chainHead$ && txHash && argsHash && currentSession && chainId && metadata
? ["applauseDetails", chainHead$, argsHash, currentSession, chainId, metadata] ? ["applausesForTransaction", chainHead$, txHash, argsHash, currentSession, chainId, metadata]
: null, : null,
([_, chainHead$, argsHash, currentSession, chainId, metadata], { next }) => { ([_, chainHead$, txHash, argsHash, currentSession, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$ const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe( const subscription = finalized$.pipe(
filter(Boolean), filter(Boolean),
mergeMap((blockInfo) => { mergeMap((blockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata)) const builder = getDynamicBuilder(getLookupFn(metadata))
const applauseDetails = builder.buildStorage("GhostSlowClaps", "ApplauseDetails") const applausesForTransaction = builder.buildStorage("GhostSlowClaps", "ApplausesForTransaction")
return storage$(blockInfo?.hash, "value", () => return storage$(blockInfo?.hash, "value", () =>
applauseDetails?.keys.enc( applausesForTransaction?.keys.enc(
currentSession, currentSession,
{ asBytes: () => fromHex(txHash) },
{ asBytes: () => fromHex(argsHash) }, { asBytes: () => fromHex(argsHash) },
) )
).pipe( ).pipe(
filter(Boolean), filter(Boolean),
distinct(), distinct(),
map((value) => applauseDetails?.value.dec(value)) map((value) => applausesForTransaction?.value.dec(value))
) )
}), }),
) )
.subscribe({ .subscribe({
next(applauseDetails) { next(applausesForTransaction) {
next(null, applauseDetails) next(null, applausesForTransaction)
}, },
error: next, error: next,
}) })
return () => subscription.unsubscribe() return () => subscription.unsubscribe()
} }
) )
return applauseDetails return applausesForTransaction
} }

View File

@ -1,76 +0,0 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import { distinct, filter, map, mergeMap } from "rxjs"
import { useUnstableProvider } from "./UnstableProvider"
import { useMetadata } from "./MetadataProvider"
export const useGenesisSlot = () => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: genesisSlot } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["genesisSlot", chainHead$, chainId, metadata]
: null,
([_, chainHead$, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const genesisSlot = builder.buildStorage("Babe", "GenesisSlot")
return storage$(blockInfo?.hash, "value", () =>
genesisSlot?.keys.enc()
).pipe(
filter(Boolean),
distinct(),
map((value) => genesisSlot?.value.dec(value))
)
}),
)
.subscribe({
next(genesisSlot) {
next(null, genesisSlot)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return genesisSlot
}
export const useCurrentSlot = () => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: currentSlot } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["currentSlot", chainHead$, chainId, metadata]
: null,
([_, chainHead$, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const currentSlot = builder.buildStorage("Babe", "CurrentSlot")
return storage$(blockInfo?.hash, "value", () =>
currentSlot?.keys.enc()
).pipe(
filter(Boolean),
distinct(),
map((value) => currentSlot?.value.dec(value))
)
}),
)
.subscribe({
next(currentSlot) {
next(null, currentSlot)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return currentSlot
}

View File

@ -1,44 +0,0 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import { distinct, filter, map, mergeMap } from "rxjs"
import { useUnstableProvider } from "./UnstableProvider"
import { useMetadata } from "./MetadataProvider"
export const useBlockCommitments = ({ evmChainId }) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: blockCommitments } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["blockCommitments", chainHead$, evmChainId, chainId, metadata]
: null,
([_, chainHead$, evmChainId, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const blockCommitments = builder.buildStorage("GhostSlowClaps", "BlockCommitments")
return storage$(blockInfo?.hash, "value", () =>
blockCommitments?.keys.enc(BigInt(evmChainId))
).pipe(
filter(Boolean),
distinct(),
map((value) => blockCommitments?.value.dec(value))
)
}),
)
.subscribe({
next(blockCommitments) {
next(null, blockCommitments)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return blockCommitments?.reduce((acc, [index, obj]) => {
acc[index] = obj;
return acc;
}, []);
}

View File

@ -1,41 +0,0 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import { distinct, filter, map, mergeMap } from "rxjs"
import { useUnstableProvider } from "./UnstableProvider"
import { useMetadata } from "./MetadataProvider"
export const useDisabledValidators = () => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: disabledIndexes, error } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["disabledIndexes", chainHead$, chainId, metadata]
: null,
([_, chainHead$, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const disabledIndexes = builder.buildStorage("Session", "DisabledValidators")
return storage$(blockInfo?.hash, "value", () =>
disabledIndexes?.keys.enc()
).pipe(
filter(Boolean),
distinct(),
map((value) => disabledIndexes?.value.dec(value))
)
}),
)
.subscribe({
next(disabledIndexes) {
next(null, disabledIndexes)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return disabledIndexes ? disabledIndexes : []
}

View File

@ -1,41 +0,0 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import { distinct, filter, map, mergeMap } from "rxjs"
import { useUnstableProvider } from "./UnstableProvider"
import { useMetadata } from "./MetadataProvider"
export const useErasTotalStake = ({ eraIndex }) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: eraTotalStake } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["eraTotalStake", chainHead$, eraIndex, chainId, metadata]
: null,
([_, chainHead$, eraIndex, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const eraTotalStake = builder.buildStorage("Staking", "ErasTotalStake")
return storage$(blockInfo?.hash, "value", () =>
eraTotalStake?.keys.enc(eraIndex)
).pipe(
filter(Boolean),
distinct(),
map((value) => eraTotalStake?.value.dec(value))
)
}),
)
.subscribe({
next(eraTotalStake) {
next(null, eraTotalStake)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return eraTotalStake;
}

View File

@ -1,41 +0,0 @@
import useSWRSubscription from "swr/subscription"
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
import { distinct, filter, map, mergeMap } from "rxjs"
import { useUnstableProvider } from "./UnstableProvider"
import { useMetadata } from "./MetadataProvider"
export const useValidators = ({ currentSession }) => {
const { chainHead$, chainId } = useUnstableProvider()
const metadata = useMetadata()
const { data: slowClapValidators } = useSWRSubscription(
chainHead$ && chainId && metadata
? ["slowClapValidators", chainHead$, currentSession, chainId, metadata]
: null,
([_, chainHead$, currentSession, chainId, metadata], { next }) => {
const { finalized$, storage$ } = chainHead$
const subscription = finalized$.pipe(
filter(Boolean),
mergeMap((blockInfo) => {
const builder = getDynamicBuilder(getLookupFn(metadata))
const slowClapValidators = builder.buildStorage("GhostSlowClaps", "Validators")
return storage$(blockInfo?.hash, "value", () =>
slowClapValidators?.keys.enc(currentSession)
).pipe(
filter(Boolean),
distinct(),
map((value) => slowClapValidators?.value.dec(value))
)
}),
)
.subscribe({
next(slowClapValidators) {
next(null, slowClapValidators)
},
error: next,
})
return () => subscription.unsubscribe()
}
)
return slowClapValidators
}

View File

@ -1,10 +1,9 @@
import { import {
RESERVE_ADDRESSES, DAI_ADDRESSES,
FTSO_ADDRESSES, FTSO_ADDRESSES,
STNK_ADDRESSES, STNK_ADDRESSES,
GHST_ADDRESSES, GHST_ADDRESSES,
FTSO_DAI_LP_ADDRESSES, FTSO_DAI_LP_ADDRESSES,
WETH_ADDRESSES,
} from "../constants/addresses"; } from "../constants/addresses";
import { abi as DaiAbi } from "../abi/Reserve.json"; import { abi as DaiAbi } from "../abi/Reserve.json";
@ -12,7 +11,6 @@ import { abi as FatsoAbi } from "../abi/Fatso.json";
import { abi as StinkyAbi } from "../abi/Stinky.json"; import { abi as StinkyAbi } from "../abi/Stinky.json";
import { abi as GhostAbi } from "../abi/Ghost.json"; import { abi as GhostAbi } from "../abi/Ghost.json";
import { abi as Erc20Abi } from "../abi/ERC20.json"; import { abi as Erc20Abi } from "../abi/ERC20.json";
import { abi as WethAbi } from "../abi/WETH9.json";
// TBD: should be extended on new tokens // TBD: should be extended on new tokens
export const getTokenAbi = (name) => { export const getTokenAbi = (name) => {
@ -24,9 +22,6 @@ export const getTokenAbi = (name) => {
case "GDAI": case "GDAI":
abi = DaiAbi; abi = DaiAbi;
break; break;
case "RESERVE":
abi = DaiAbi;
break;
case "FTSO": case "FTSO":
abi = FatsoAbi; abi = FatsoAbi;
break; break;
@ -45,9 +40,6 @@ export const getTokenAbi = (name) => {
case "CSPR": case "CSPR":
abi = GhostAbi; abi = GhostAbi;
break; break;
case "WETH":
abi = WethAbi;
break;
} }
return abi; return abi;
} }
@ -62,9 +54,6 @@ export const getTokenDecimals = (name) => {
case "GDAI": case "GDAI":
decimals = 18; decimals = 18;
break; break;
case "RESERVE":
decimals = 18;
break;
case "FTSO": case "FTSO":
decimals = 9; decimals = 9;
break; break;
@ -83,9 +72,6 @@ export const getTokenDecimals = (name) => {
case "CSPR": case "CSPR":
decimals = 18; decimals = 18;
break; break;
case "WETH":
decimals = 18;
break;
} }
return decimals; return decimals;
} }
@ -95,13 +81,10 @@ export const getTokenAddress = (chainId, name) => {
let address = name; let address = name;
switch (name?.toUpperCase()) { switch (name?.toUpperCase()) {
case "DAI": case "DAI":
address = RESERVE_ADDRESSES[chainId]; address = DAI_ADDRESSES[chainId];
break; break;
case "GDAI": case "GDAI":
address = RESERVE_ADDRESSES[chainId]; address = DAI_ADDRESSES[chainId];
break;
case "RESERVE":
address = RESERVE_ADDRESSES[chainId];
break; break;
case "FTSO": case "FTSO":
address = FTSO_ADDRESSES[chainId]; address = FTSO_ADDRESSES[chainId];
@ -124,15 +107,6 @@ export const getTokenAddress = (chainId, name) => {
case "GDAI_FTSO": case "GDAI_FTSO":
address = FTSO_DAI_LP_ADDRESSES[chainId]; address = FTSO_DAI_LP_ADDRESSES[chainId];
break; break;
case "RESERVE_FTSO":
address = FTSO_DAI_LP_ADDRESSES[chainId];
break;
case "WETH":
address = WETH_ADDRESSES[chainId];
break;
case "WETC":
address = WETH_ADDRESSES[chainId];
break;
} }
return address; return address;
} }
@ -141,8 +115,8 @@ export const getTokenAddress = (chainId, name) => {
export const getTokenIcons = (chainId, address) => { export const getTokenIcons = (chainId, address) => {
let icons = [""]; let icons = [""];
switch (address) { switch (address) {
case RESERVE_ADDRESSES[chainId]: case DAI_ADDRESSES[chainId]:
icons = ["RESERVE"]; icons = ["GDAI"];
break; break;
case FTSO_ADDRESSES[chainId]: case FTSO_ADDRESSES[chainId]:
icons = ["FTSO"]; icons = ["FTSO"];
@ -154,7 +128,7 @@ export const getTokenIcons = (chainId, address) => {
icons = ["GHST"]; icons = ["GHST"];
break; break;
case FTSO_DAI_LP_ADDRESSES[chainId]: case FTSO_DAI_LP_ADDRESSES[chainId]:
icons = ["FTSO", "RESERVE"]; icons = ["FTSO", "GDAI"];
break; break;
} }
return icons; return icons;
@ -170,14 +144,11 @@ export const getBondNameDisplayName = (chainId, stringValue, tokenAddress) => {
export const getTokenPurchaseLink = (chainId, tokenAddress) => { export const getTokenPurchaseLink = (chainId, tokenAddress) => {
let purchaseUrl = "https://app.dao.ghostchain.io/#/dex/uniswap"; let purchaseUrl = "https://app.dao.ghostchain.io/#/dex/uniswap";
switch (tokenAddress) { switch (tokenAddress) {
case RESERVE_ADDRESSES[chainId]: case DAI_ADDRESSES[chainId]:
purchaseUrl = "https://app.dao.ghostchain.io/#/faucet"; purchaseUrl = "https://app.dao.ghostchain.io/#/faucet";
if (chainId == 63) {
purchaseUrl = "https://app.dao.ghostchain.io/#/wrapper";
}
break; break;
case FTSO_DAI_LP_ADDRESSES[chainId]: case FTSO_DAI_LP_ADDRESSES[chainId]:
purchaseUrl += `?pool=true&from=${RESERVE_ADDRESSES[chainId]}&to=${FTSO_ADDRESSES[chainId]}`; purchaseUrl += `?pool=true&from=${DAI_ADDRESSES[chainId]}&to=${FTSO_ADDRESSES[chainId]}`;
break; break;
} }
return purchaseUrl; return purchaseUrl;

View File

@ -1,144 +1,26 @@
import { useState, useEffect } from "react";
import { useReadContract } from "wagmi"; import { useReadContract } from "wagmi";
import { useCurrentIndex, useGhostedSupply } from "../staking"; import { useCurrentIndex, useGhostedSupply } from "../staking";
import { useUniswapV2PairReserves } from "../uniswapv2"; import { useUniswapV2PairReserves } from "../uniswapv2";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { import { FTSO_DAI_LP_ADDRESSES, DAI_ADDRESSES, FTSO_ADDRESSES } from "../../constants/addresses";
FTSO_DAI_LP_ADDRESSES,
RESERVE_ADDRESSES,
FTSO_ADDRESSES,
CEX_TICKERS,
NATIVE_TICKERS,
} from "../../constants/addresses";
const cexPriceGetters = new Map(); export const useDaiPrice = (chainId) => {
const daiPrice = new DecimalBigNumber(1000000000000000000n, 18);
function callWithCacheTTL(fn, ttlMs = 10000) { return daiPrice;
let lastFetchTime = 0;
let cachedValue;
let inFlight = null;
return function(...args) {
const now = Date.now();
if ((now - lastFetchTime) < ttlMs) {
return inFlight ? inFlight : Promise.resolve(cachedValue);
}
lastFetchTime = now;
inFlight = Promise.resolve(fn(...args))
.then(res => {
cachedValue = res;
inFlight = null;
return res;
})
.catch(err => {
inFlight = null;
throw err;
});
return inFlight;
}
}
export const useNativePrice = (chainId) => {
const [nativePrice, setNativePrice] = useState(new DecimalBigNumber(1000000000000000000n, 18));
const cexApis = NATIVE_TICKERS[chainId];
useEffect(() => {
if (!cexApis) {
setNativePrice(new DecimalBigNumber(1000000000000000000n, 18));
return;
}
let getCexPriceCached = cexPriceGetters.get(chainId);
if (!getCexPriceCached) {
getCexPriceCached = callWithCacheTTL(() => {
const fetchPromises = cexApis.map(url => fetch(url)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
)
return Promise.any(fetchPromises);
}, 5000);
cexPriceGetters.set(chainId, getCexPriceCached);
}
getCexPriceCached()
.then(response => {
if ('data' in response) {
const coinPrice = Number(response?.data?.amount ?? 0.0);
const priceInWei = Math.floor(coinPrice * 1e18 / 0.99);
setNativePrice(new DecimalBigNumber(BigInt(priceInWei), 18));
} else if ('price' in response) {
const coinPrice = Number(response?.price ?? 0.0);
const priceInWei = Math.floor(coinPrice * 1e18)
setNativePrice(new DecimalBigNumber(BigInt(priceInWei), 18));
} else {
throw Error("Unexpected json in response.");
}
})
.catch(error => {
setNativePrice(new DecimalBigNumber(0n, 18));
});
}, [chainId, cexApis])
return nativePrice;
}
export const useReservePrice = (chainId) => {
const [reservePrice, setReservePrice] = useState(new DecimalBigNumber(1000000000000000000n, 18));
const cexApis = CEX_TICKERS[chainId];
useEffect(() => {
if (!cexApis) {
setReservePrice(new DecimalBigNumber(1000000000000000000n, 18));
return;
}
let getCexPriceCached = cexPriceGetters.get(chainId);
if (!getCexPriceCached) {
getCexPriceCached = callWithCacheTTL(() => {
const fetchPromises = cexApis.map(url => fetch(url)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
})
)
return Promise.any(fetchPromises);
}, 5000);
cexPriceGetters.set(chainId, getCexPriceCached);
}
getCexPriceCached()
.then(response => {
if ('data' in response) {
const coinPrice = Number(response?.data?.amount ?? 0.0);
const priceInWei = Math.floor(coinPrice * 1e18 / 0.99);
setReservePrice(new DecimalBigNumber(BigInt(priceInWei), 18));
} else if ('price' in response) {
const coinPrice = Number(response?.price ?? 0.0);
const priceInWei = Math.floor(coinPrice * 1e18)
setReservePrice(new DecimalBigNumber(BigInt(priceInWei), 18));
} else {
throw Error("Unexpected json in response.");
}
})
.catch(error => {
setReservePrice(new DecimalBigNumber(0n, 18));
});
}, [chainId, cexApis])
return reservePrice;
}; };
export const useFtsoPrice = (chainId) => { export const useFtsoPrice = (chainId) => {
const { reserves, tokens, refetch } = useUniswapV2PairReserves(chainId, FTSO_DAI_LP_ADDRESSES[chainId]); const { reserves, tokens, refetch } = useUniswapV2PairReserves(
chainId,
FTSO_DAI_LP_ADDRESSES[chainId],
9,
18,
"FTSO",
"GDAI",
);
const reservePrice = useReservePrice(chainId); const reserveAddress = DAI_ADDRESSES[chainId];
const reserveAddress = RESERVE_ADDRESSES[chainId];
const ftsoAddress = FTSO_ADDRESSES[chainId]; const ftsoAddress = FTSO_ADDRESSES[chainId];
if (!reserveAddress || !ftsoAddress) { if (!reserveAddress || !ftsoAddress) {
return new DecimalBigNumber(0n, 9); return new DecimalBigNumber(0n, 9);
@ -155,9 +37,8 @@ export const useFtsoPrice = (chainId) => {
let price = 0n let price = 0n
if (ftsoReserves > 0n) if (ftsoReserves > 0n)
price = stableReserves / ftsoReserves; price = stableReserves / ftsoReserves;
price = price * reservePrice._value;
return new DecimalBigNumber(price, 27); return new DecimalBigNumber(price, 9);
}; };
export const useStnkPrice = (chainId) => { export const useStnkPrice = (chainId) => {

View File

@ -6,7 +6,6 @@ import { getTokenAbi, getTokenAddress, getTokenDecimals } from "../helpers";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { shorten } from "../../helpers"; import { shorten } from "../../helpers";
import { tokenNameConverter } from "../../helpers/tokenConverter";
import { config } from "../../config"; import { config } from "../../config";
export const useTotalSupply = (chainId, name) => { export const useTotalSupply = (chainId, name) => {
@ -69,9 +68,7 @@ export const useTokenSymbol = (chainId, name) => {
chainId: chainId, chainId: chainId,
}); });
let symbol = data ? data : ""; const symbol = data ? data : "";
symbol = tokenNameConverter(chainId, symbol);
return { symbol, refetch }; return { symbol, refetch };
} }
@ -232,53 +229,3 @@ export const burnDai = async (chainId, account, value) => {
toast.error("Burning gDAI from the faucet failed. Check logs for error detalization.") toast.error("Burning gDAI from the faucet failed. Check logs for error detalization.")
} }
} }
export const depositNative = async (chainId, account, value) => {
try {
const { request } = await simulateContract(config, {
abi: getTokenAbi("WETH"),
address: getTokenAddress(chainId, "WETH"),
functionName: 'deposit',
account: account,
chainId: chainId,
value: value
});
const txHash = await writeContract(config, request);
await waitForTransactionReceipt(config, {
hash: txHash,
onReplaced: () => toast("WETH9 deposit transaction was replaced. Wait for inclusion please."),
chainId
});
toast.success("WETH9 successfully minted to your wallet! Check your wallet balance.");
} catch (err) {
console.error(err);
toast.error("WETH9 wrapping failed. Check logs for error detalization.")
}
}
export const withdrawWeth = async (chainId, account, value) => {
try {
const { request } = await simulateContract(config, {
abi: getTokenAbi("WETH"),
address: getTokenAddress(chainId, "WETH"),
functionName: 'withdraw',
args: [value],
account: account,
chainId: chainId
});
const txHash = await writeContract(config, request);
await waitForTransactionReceipt(config, {
hash: txHash,
onReplaced: () => toast("WETH9 withdraw transaction was replaced. Wait for inclusion please."),
chainId
});
toast.success("WETH9 successfully burned for native coins! Check your wallet balance.");
} catch (err) {
console.error(err);
toast.error("WETH9 unwrapping failed. Check logs for error detalization.")
}
}

View File

@ -3,23 +3,9 @@ import { useReadContract } from "wagmi";
import { DAO_TREASURY_ADDRESSES } from "../../constants/addresses"; import { DAO_TREASURY_ADDRESSES } from "../../constants/addresses";
import { abi as TreasuryAbi } from "../../abi/GhostTreasury.json"; import { abi as TreasuryAbi } from "../../abi/GhostTreasury.json";
import { useReservePrice } from "../prices/index";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { getTokenAddress } from "../helpers"; import { getTokenAddress } from "../helpers";
export const useOrinalCoefficient = (chainId) => {
const { data: original } = useReadContract({
abi: TreasuryAbi,
address: DAO_TREASURY_ADDRESSES[chainId],
functionName: "originalCoefficient",
scopeKey: `originalCoefficient-${chainId}`,
chainId: chainId
});
return new DecimalBigNumber(original ? original : 1000000000000000000n, 18);
}
export const useTotalReserves = (chainId) => { export const useTotalReserves = (chainId) => {
const { data: totalReservesRaw } = useReadContract({ const { data: totalReservesRaw } = useReadContract({
abi: TreasuryAbi, abi: TreasuryAbi,
@ -29,13 +15,10 @@ export const useTotalReserves = (chainId) => {
chainId: chainId, chainId: chainId,
}); });
const original = useOrinalCoefficient(chainId);
const price = useReservePrice(chainId);
const totalReservesPrepared = totalReservesRaw ? totalReservesRaw : 0n; const totalReservesPrepared = totalReservesRaw ? totalReservesRaw : 0n;
const totalReserves = new DecimalBigNumber(totalReservesPrepared, 9); const totalReserves = new DecimalBigNumber(totalReservesPrepared, 9);
return totalReserves.mul(price).div(original); return totalReserves;
}; };
export const useLpValuation = (chainId, pairAddress, pairSupply) => { export const useLpValuation = (chainId, pairAddress, pairSupply) => {

View File

@ -128,48 +128,3 @@ a:hover svg {
.tooltip { .tooltip {
z-index: 9999999; z-index: 9999999;
} }
.custom-scrollbar {
overflow: auto;
max-height: 400px;
/* For Chrome, Safari, Edge */
&::-webkit-scrollbar {
width: 8px;
height: 8px;
}
&::-webkit-scrollbar-track {
background: transparent; /* Hide track */
}
&::-webkit-scrollbar-thumb {
background: #888; /* Only visible part */
border-radius: 4px;
}
&::-webkit-scrollbar-thumb:hover {
background: #fff;
}
/* This definitely hides arrows in Chrome/Safari/Edge */
&::-webkit-scrollbar-button {
display: none; /* ← THIS WORKS */
}
&::-webkit-scrollbar-corner {
background: transparent; /* Hide corner */
}
/* For Firefox */
scrollbar-width: thin; /* auto | thin | none */
scrollbar-color: #fff transparent; /* thumb track */
}
input:-webkit-autofill {
-webkit-box-shadow: 0 0 0 1000px transparent inset !important;
box-shadow: 0 0 0 1000px transparent inset !important;
transition: background-color 5000s ease-in-out 0s;
color: #ffffff !important;
-webkit-text-fill-color: #ffffff !important;
}

View File

@ -18,18 +18,9 @@ export const darkPalette = {
success: "#60C45B", // idk where this is - done success: "#60C45B", // idk where this is - done
userFeedback: "#49A1F2", // idk where this is userFeedback: "#49A1F2", // idk where this is
error: "#F06F73", // red negative % - done error: "#F06F73", // red negative % - done
warning: "#ed6c02", // idk where this is - done warning: "#49A1F2", // idk where this is - done
pnlGain: "#60C45B", // green positive % - done pnlGain: "#60C45B", // green positive % - done
}, },
bridgeProgress: {
error: "#F06F73",
warning: "#ed6c02",
success: "#60C45B",
},
validatorsColor: {
red: "#fd9b9e",
green: "#60c45b",
},
gray: { gray: {
800: "#1F4671", // active menu - done 800: "#1F4671", // active menu - done
700: "#50759E", // menu background color - done 700: "#50759E", // menu background color - done

View File

@ -6,20 +6,11 @@ export const lightPalette = {
}, },
background: "linear-gradient(180.37deg, #B3BFC5 0.49%, #D1D5D4 26.3%, #EEEAE3 99.85%)", background: "linear-gradient(180.37deg, #B3BFC5 0.49%, #D1D5D4 26.3%, #EEEAE3 99.85%)",
feedback: { feedback: {
success: "#60C45B", // idk where this is - done success: "#94B9A1",
userFeedback: "#49A1F2", // idk where this is userFeedback: "#49A1F2",
error: "#F06F73", // red negative % - done error: "#FF6767",
warning: "#ed6c02", // idk where this is - done warning: "#FC8E5F",
pnlGain: "#60C45B", // green positive % - done pnlGain: "#3D9C70",
},
bridgeProgress: {
error: "#F06F73",
warning: "#ed6c02",
success: "#60C45B",
},
validatorsColor: {
red: "#fd9b9e",
green: "#60c45b",
}, },
gray: { gray: {
700: "#FAFAFB", 700: "#FAFAFB",