Compare commits
No commits in common. "main" and "extension-integration" have entirely different histories.
main
...
extension-
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ghost-dao-interface",
|
||||
"private": true,
|
||||
"version": "0.4.0",
|
||||
"version": "0.2.15",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@ -14,7 +14,7 @@ import Sidebar from "./components/Sidebar/Sidebar";
|
||||
import TopBar from "./components/TopBar/TopBar";
|
||||
|
||||
import { shouldTriggerSafetyCheck } from "./helpers";
|
||||
import { isNetworkAvailable, isNetworkLegacy } from "./constants";
|
||||
import { isNetworkAvailable } from "./constants";
|
||||
import useTheme from "./hooks/useTheme";
|
||||
import { useUnstableProvider } from "./hooks/ghost";
|
||||
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 TreasuryDashboard = lazy(() => import("./containers/TreasuryDashboard/TreasuryDashboard"));
|
||||
const Faucet = lazy(() => import("./containers/Faucet/Faucet"));
|
||||
const Wrapper = lazy(() => import("./containers/WethWrapper/WethWrapper"));
|
||||
const Dex = lazy(() => import("./containers/Dex/Dex"));
|
||||
const Bridge = lazy(() => import("./containers/Bridge/Bridge"));
|
||||
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/:id" element={<BondModalContainer 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="/wrapper" element={<Wrapper 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="/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} />} />
|
||||
</>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -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"}]}
|
||||
@ -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 |
@ -1,36 +1,13 @@
|
||||
<?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"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="2500" height="2500" viewBox="0 0 32 32">
|
||||
<g fill="none" fill-rule="evenodd">
|
||||
<circle cx="16" cy="16" r="16" fill="#627EEA"/>
|
||||
<g fill="#FFF" fill-rule="nonzero">
|
||||
<path fill-opacity=".602" d="M16.498 4v8.87l7.497 3.35z"/>
|
||||
<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"/>
|
||||
<path fill-opacity=".2" d="M16.498 20.573l7.497-4.353-7.497-3.348z"/>
|
||||
<path fill-opacity=".602" d="M9 16.22l7.498 4.353v-7.701z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 617 B |
36
src/assets/tokens/wETH.svg
Normal file
36
src/assets/tokens/wETH.svg
Normal 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 |
@ -37,7 +37,7 @@ import BondIcon from "../Icon/BondIcon";
|
||||
import StakeIcon from "../Icon/StakeIcon";
|
||||
import WrapIcon from "../Icon/WrapIcon";
|
||||
|
||||
import { isNetworkAvailable, isNetworkLegacy } from "../../constants";
|
||||
import { isNetworkAvailable } from "../../constants";
|
||||
import { AVAILABLE_DEXES } from "../../constants/dexes";
|
||||
import { ECOSYSTEM } from "../../constants/ecosystem";
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
@ -46,7 +46,6 @@ import BondDiscount from "../../containers/Bond/components/BondDiscount";
|
||||
|
||||
import DashboardIcon from '@mui/icons-material/Dashboard';
|
||||
import ShowerIcon from '@mui/icons-material/Shower';
|
||||
import WifiProtectedSetupIcon from '@mui/icons-material/WifiProtectedSetup';
|
||||
|
||||
import { useTokenSymbol } from "../../hooks/tokens";
|
||||
import { useFtsoPrice, useGhstPrice, useGhostedSupplyPrice } from "../../hooks/prices";
|
||||
@ -112,10 +111,6 @@ const NavContent = ({ chainId, addressChainId }) => {
|
||||
{isNetworkAvailable(chainId, addressChainId) &&
|
||||
<>
|
||||
<NavItem icon={DashboardIcon} label={`Dashboard`} to="/dashboard" />
|
||||
{isNetworkLegacy(chainId)
|
||||
? <NavItem icon={ShowerIcon} label={`Faucet`} to="/faucet" />
|
||||
: <NavItem icon={WifiProtectedSetupIcon} label={`Wrapper`} to="/wrapper" />
|
||||
}
|
||||
<NavItem
|
||||
defaultExpanded
|
||||
icon={BondIcon}
|
||||
@ -152,6 +147,9 @@ const NavContent = ({ chainId, addressChainId }) => {
|
||||
</AccordionDetails>
|
||||
}
|
||||
/>
|
||||
<NavItem icon={StakeIcon} label={`Stake`} to="/stake" />
|
||||
<NavItem icon={ShowerIcon} label={`Faucet`} to="/faucet" />
|
||||
<NavItem icon={PublicIcon} label={`Bridge`} to="/bridge" />
|
||||
<NavItem
|
||||
icon={CurrencyExchangeIcon}
|
||||
label={`Dex`}
|
||||
@ -176,8 +174,6 @@ const NavContent = ({ chainId, addressChainId }) => {
|
||||
</AccordionDetails>
|
||||
}
|
||||
/>
|
||||
<NavItem icon={StakeIcon} label={`Stake`} to="/stake" />
|
||||
<NavItem icon={PublicIcon} label={`Bridge`} to="/bridge" />
|
||||
<Box className="menu-divider">
|
||||
<Divider />
|
||||
</Box>
|
||||
|
||||
@ -90,7 +90,7 @@ const SwapCard = ({
|
||||
</Box>
|
||||
)}
|
||||
<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
|
||||
id={id}
|
||||
sx={{
|
||||
@ -99,7 +99,6 @@ const SwapCard = ({
|
||||
padding: 0,
|
||||
height: "24px",
|
||||
maxWidth: inputWidth || "136px",
|
||||
width: !info && !endString && !usdValue ? "100%" : inputWidth ? inputWidth : "136px",
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
type={inputType}
|
||||
|
||||
@ -28,11 +28,11 @@ const StyledArrow = styled(Box)(
|
||||
},
|
||||
);
|
||||
|
||||
const SwapCollection = ({ UpperSwapCard, LowerSwapCard, arrowOnClick, iconNotNeeded, maxWidth}) => {
|
||||
const SwapCollection = ({ UpperSwapCard, LowerSwapCard, arrowOnClick, iconNotNeeded }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" maxWidth={maxWidth ? maxWidth : "476px"}>
|
||||
<Box display="flex" flexDirection="column" maxWidth="476px">
|
||||
{UpperSwapCard}
|
||||
<Box display="flex" flexDirection="row" justifyContent="center">
|
||||
{!iconNotNeeded && (<StyledArrow
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
import { SvgIcon, Box } from "@mui/material";
|
||||
import { SvgIcon } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
import FtsoIcon from "../../assets/tokens/FTSO.svg?react";
|
||||
import StnkIcon from "../../assets/tokens/STNK.svg?react";
|
||||
import GhstIcon from "../../assets/tokens/GHST.svg?react";
|
||||
import DaiIcon from "../../assets/tokens/DAI.svg?react";
|
||||
import EthIcon from "../../assets/tokens/ETH.svg?react";
|
||||
import EtcIcon from "../../assets/tokens/ETC.svg?react";
|
||||
import WethIcon from "../../assets/tokens/wETH.svg?react";
|
||||
import UnknownIcon from "../../assets/tokens/Unknown.svg?react";
|
||||
|
||||
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;
|
||||
switch (name?.toUpperCase()) {
|
||||
case "FTSO":
|
||||
@ -52,16 +52,10 @@ export const parseKnownToken = (name) => {
|
||||
icon = DaiIcon;
|
||||
break;
|
||||
case "ETH":
|
||||
icon = EthIcon;
|
||||
icon = WethIcon;
|
||||
break;
|
||||
case "WETH":
|
||||
icon = EthIcon;
|
||||
break;
|
||||
case "METC":
|
||||
icon = EtcIcon;
|
||||
break;
|
||||
case "WMETC":
|
||||
icon = EtcIcon;
|
||||
icon = WethIcon;
|
||||
break;
|
||||
default:
|
||||
icon = UnknownIcon;
|
||||
@ -69,31 +63,13 @@ export const parseKnownToken = (name) => {
|
||||
return icon;
|
||||
}
|
||||
|
||||
const Token = ({ chainTokenName, name, viewBox = "0 0 260 260", fontSize = "large", ...props }) => {
|
||||
return (
|
||||
<Box display="flex" justifyContent="center" alignItems="center" position="relative">
|
||||
<StyledSvgIcon
|
||||
inheritViewBox
|
||||
viewBox={viewBox}
|
||||
fontSize={fontSize}
|
||||
component={parseKnownToken(name)}
|
||||
{...props}
|
||||
></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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import FormControl from '@mui/material/FormControl';
|
||||
import Select from '@mui/material/Select';
|
||||
|
||||
import { isNetworkAvailable } from "../../constants";
|
||||
import { parseKnownToken } from "../../components/Token/Token";
|
||||
import EthIcon from "../../assets/tokens/ETH.svg?react";
|
||||
|
||||
import { useSwitchChain } from 'wagmi';
|
||||
import toast from "react-hot-toast";
|
||||
@ -53,11 +53,11 @@ function SelectNetwork({ chainId, wrongNetworkToastId, setWrongNetworkToastId, s
|
||||
}
|
||||
}}
|
||||
>
|
||||
{chains.map((chain, i) => {
|
||||
{chains.map(chain => {
|
||||
return (
|
||||
<MenuItem key={chain.name} value={chain.id}>
|
||||
<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>}
|
||||
</Box>
|
||||
</MenuItem>
|
||||
|
||||
@ -22,7 +22,7 @@ import { PrimaryButton, SecondaryButton } from "../../Button";
|
||||
import ArrowUpIcon from "../../../assets/icons/arrow-up.svg?react";
|
||||
import { formatCurrency, shorten } from "../../../helpers";
|
||||
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";
|
||||
|
||||
@ -156,9 +156,9 @@ function InitialWalletView({ isWalletOpen, address, chainId, onClose }) {
|
||||
>
|
||||
<SecondaryButton
|
||||
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>
|
||||
</Box>
|
||||
|
||||
|
||||
@ -13,24 +13,15 @@ import { ChangeEvent, useState, useEffect } from "react";
|
||||
import { useNavigate, createSearchParams } from "react-router-dom";
|
||||
import { useQuery } from "react-query";
|
||||
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 TokenStack from "../../TokenStack/TokenStack";
|
||||
import { PrimaryButton, SecondaryButton } from "../../Button";
|
||||
|
||||
import { useBalance, useTokenSymbol } from "../../../hooks/tokens";
|
||||
import {
|
||||
useNativePrice,
|
||||
useReservePrice,
|
||||
useFtsoPrice,
|
||||
useStnkPrice,
|
||||
useGhstPrice
|
||||
} from "../../../hooks/prices";
|
||||
import { useDaiPrice, useFtsoPrice, useStnkPrice, useGhstPrice } from "../../../hooks/prices";
|
||||
import { useLpValuation } from "../../../hooks/treasury";
|
||||
import { useAccount, useBalance as useNativeBalance, useConfig } from "wagmi";
|
||||
import { useAccount } from "wagmi";
|
||||
|
||||
const addTokenToWallet = async (token, userAddress) => {
|
||||
if (!window.ethereum) return;
|
||||
@ -69,7 +60,6 @@ const BalanceValue = ({
|
||||
|
||||
export const Token = (props) => {
|
||||
const {
|
||||
isNative,
|
||||
symbol,
|
||||
icons,
|
||||
address,
|
||||
@ -78,7 +68,7 @@ export const Token = (props) => {
|
||||
onAddTokenToWallet,
|
||||
expanded,
|
||||
onChangeExpanded,
|
||||
reserveAddress,
|
||||
daiAddress,
|
||||
onClose,
|
||||
isPool
|
||||
} = props;
|
||||
@ -86,7 +76,7 @@ export const Token = (props) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const useLink = (symbol, fromAddress, toAddress, isPool) => {
|
||||
if (symbol.toUpperCase() === "RESERVE") {
|
||||
if (symbol.toUpperCase() === "GDAI") {
|
||||
navigate({ pathname: "/faucet" })
|
||||
} else {
|
||||
navigate({
|
||||
@ -108,11 +98,8 @@ export const Token = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion expanded={isNative ? false : expanded} onChange={onChangeExpanded}>
|
||||
<AccordionSummary
|
||||
sx={{ paddingRight: isNative ? "37.43px" : "" }}
|
||||
expandIcon={isNative ? null : <GhostStyledIcon component={ExpandMoreIcon} color="disabled" />}
|
||||
>
|
||||
<Accordion expanded={expanded} onChange={onChangeExpanded}>
|
||||
<AccordionSummary expandIcon={<GhostStyledIcon component={ExpandMoreIcon} color="disabled" />}>
|
||||
<Box sx={{ display: "flex", justifyContent: "space-between", width: "100%", marginRight: "10px" }}>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "15px" }}>
|
||||
<TokenStack
|
||||
@ -130,7 +117,7 @@ export const Token = (props) => {
|
||||
/>
|
||||
</Box>
|
||||
</AccordionSummary>
|
||||
{!isNative && <AccordionDetails style={{ margin: "auto", padding: theme.spacing(1, 0) }}>
|
||||
<AccordionDetails style={{ margin: "auto", padding: theme.spacing(1, 0) }}>
|
||||
<Box
|
||||
sx={{ display: "flex", flexDirection: "column", flex: 1, mx: "32px", justifyContent: "center" }}
|
||||
style={{ gap: theme.spacing(1) }}
|
||||
@ -143,14 +130,14 @@ export const Token = (props) => {
|
||||
<Typography>Add to Wallet</Typography>
|
||||
</PrimaryButton>
|
||||
<SecondaryButton
|
||||
onClick={() => useLink(symbol, reserveAddress, address, isPool)}
|
||||
onClick={() => useLink(symbol, daiAddress, address, isPool)}
|
||||
fullWidth
|
||||
>
|
||||
<Typography>Get on {symbol?.toUpperCase() === "RESERVE" ? "Faucet" : "Uniswap"}</Typography>
|
||||
<Typography>Get on {symbol.toUpperCase() === "GDAI" ? "Faucet" : "Uniswap"}</Typography>
|
||||
</SecondaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</AccordionDetails>}
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
@ -160,15 +147,10 @@ const sumObjValues = (obj: Record<string, string> = {}) =>
|
||||
|
||||
export const useWallet = (chainId, userAddress) => {
|
||||
const {
|
||||
data: nativeBalanceRaw,
|
||||
refetch: nativeBalanceRefetch
|
||||
} = useNativeBalance({ address: userAddress });
|
||||
const nativeBalance = new DecimalBigNumber(nativeBalanceRaw?.value ?? 0n, 18);
|
||||
const {
|
||||
balance: reserveBalance,
|
||||
refetch: reserveRefetch,
|
||||
contractAddress: reserveAddress,
|
||||
} = useBalance(chainId, "RESERVE", userAddress);
|
||||
balance: daiBalance,
|
||||
refetch: daiRefetch,
|
||||
contractAddress: daiAddress,
|
||||
} = useBalance(chainId, "GDAI", userAddress);
|
||||
const {
|
||||
balance: ftsoBalance,
|
||||
refetch: ftsoRefetch,
|
||||
@ -185,45 +167,32 @@ export const useWallet = (chainId, userAddress) => {
|
||||
contractAddress: ghstAddress,
|
||||
} = useBalance(chainId, "GHST", userAddress);
|
||||
const {
|
||||
balance: lpReserveFtsoBalance,
|
||||
refetch: lpReserveFtsoRefetch,
|
||||
contractAddress: lpReserveFtsoBalanceAddress,
|
||||
} = useBalance(chainId, "RESERVE_FTSO", userAddress);
|
||||
balance: lpDaiFtsoBalance,
|
||||
refetch: lpDaiFtsoRefetch,
|
||||
contractAddress: lpDaiFtsoBalanceAddress,
|
||||
} = useBalance(chainId, "GDAI_FTSO", userAddress);
|
||||
|
||||
const nativePrice = useNativePrice(chainId);
|
||||
const reservePrice = useReservePrice(chainId);
|
||||
const daiPrice = useDaiPrice(chainId);
|
||||
const ftsoPrice = useFtsoPrice(chainId);
|
||||
const stnkPrice = useStnkPrice(chainId);
|
||||
const ghstPrice = useGhstPrice(chainId);
|
||||
const lpReserveFtsoPrice = useLpValuation(chainId, "RESERVE_FTSO", 1000000000000000000n);
|
||||
const lpDaiFtsoPrice = useLpValuation(chainId, "GDAI_FTSO", 1000000000000000000n);
|
||||
|
||||
const config = useConfig();
|
||||
|
||||
const nativeSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
|
||||
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE");
|
||||
const { symbol: daiSymbol } = useTokenSymbol(chainId, "GDAI");
|
||||
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
|
||||
const { symbol: stnkSymbol } = useTokenSymbol(chainId, "STNK");
|
||||
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
|
||||
const { symbol: lpReserveFtsoSymbol } = useTokenSymbol(chainId, "RESERVE_FTSO");
|
||||
const { symbol: lpDaiFtsoSymbol } = useTokenSymbol(chainId, "GDAI_FTSO");
|
||||
|
||||
const tokens = {
|
||||
native: {
|
||||
symbol: nativeSymbol,
|
||||
icons: [nativeSymbol],
|
||||
balance: nativeBalance,
|
||||
price: nativePrice,
|
||||
refetch: nativeBalanceRefetch
|
||||
},
|
||||
reserve: {
|
||||
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,
|
||||
dai: {
|
||||
symbol: daiSymbol,
|
||||
address: daiAddress,
|
||||
balance: daiBalance,
|
||||
price: daiPrice,
|
||||
icons: ["GDAI"],
|
||||
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/gDAI.svg",
|
||||
refetch: daiRefetch,
|
||||
},
|
||||
ftso: {
|
||||
symbol: ftsoSymbol,
|
||||
@ -252,15 +221,15 @@ export const useWallet = (chainId, userAddress) => {
|
||||
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/GHST.svg",
|
||||
refetch: ghstRefetch,
|
||||
},
|
||||
reserveFtso: {
|
||||
daiFtso: {
|
||||
isPool: true,
|
||||
symbol: lpReserveFtsoSymbol,
|
||||
address: lpReserveFtsoBalanceAddress,
|
||||
balance: lpReserveFtsoBalance,
|
||||
price: lpReserveFtsoPrice,
|
||||
icons: ["FTSO", isNetworkLegacy(chainId) ? "GDAI" : tokenNameConverter(chainId, reserveSymbol)],
|
||||
symbol: lpDaiFtsoSymbol,
|
||||
address: lpDaiFtsoBalanceAddress,
|
||||
balance: lpDaiFtsoBalance,
|
||||
price: lpDaiFtsoPrice,
|
||||
icons: ["GDAI", "FTSO"],
|
||||
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 }) => {
|
||||
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) => ({
|
||||
...token,
|
||||
expanded: expanded === token.symbol,
|
||||
reserveAddress: tokens.reserve.address,
|
||||
daiAddress: tokens.dai.address,
|
||||
onChangeExpanded: (e, isExpanded) => setExpanded(isExpanded ? token.symbol : null),
|
||||
onAddTokenToWallet: () => addTokenToWallet(token, address),
|
||||
onClose: () => onClose(),
|
||||
@ -291,7 +260,7 @@ export const Tokens = ({ address, tokens, onClose }) => {
|
||||
return (
|
||||
<>
|
||||
{alwaysShowTokens.map((token, i) => (
|
||||
<Token key={i} isNative={i === 0} {...tokenProps(token)} />
|
||||
<Token key={i} {...tokenProps(token)} />
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
@ -1,28 +1,8 @@
|
||||
import { defineChain } from 'viem'
|
||||
import { http, fallback, createConfig } from 'wagmi'
|
||||
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({
|
||||
chains: [sepolia, hoodi, mordor],
|
||||
chains: [sepolia, hoodi],
|
||||
transports: {
|
||||
[sepolia.id]: fallback([
|
||||
http('https://ethereum-sepolia-rpc.publicnode.com'),
|
||||
@ -36,10 +16,6 @@ export const config = createConfig({
|
||||
[hoodi.id]: fallback([
|
||||
http('https://rpc.hoodi.ethpandaops.io'),
|
||||
http('https://0xrpc.io/hoodi'),
|
||||
]),
|
||||
[mordor.id]: fallback([
|
||||
http('https://rpc.mordor.etccooperative.org'),
|
||||
http('https://geth-mordor.etc-network.info'),
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
export enum NetworkId {
|
||||
TESTNET_SEPOLIA = 11155111,
|
||||
TESTNET_HOODI = 560048,
|
||||
TESTNET_MORDOR = 63,
|
||||
}
|
||||
|
||||
export const isNetworkAvailable = (chainId, addressChainId) => {
|
||||
@ -14,44 +13,8 @@ export const isNetworkAvailable = (chainId, addressChainId) => {
|
||||
case 560048:
|
||||
exists = true
|
||||
break;
|
||||
case 63:
|
||||
exists = true
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@ -3,116 +3,78 @@ import { NetworkId } from "../constants";
|
||||
export const STAKING_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xd90E63E88282596E1ea33765b41Ba3d650f4aD52",
|
||||
[NetworkId.TESTNET_HOODI]: "0x25F62eDc6C89FF84E957C22336A35d2dfc861a86",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xC25C9C56a89ebd6ef291b415d00ACfa7913c55e7",
|
||||
};
|
||||
|
||||
export const BOND_DEPOSITORY_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xdcE486113280e49ca2fB200258E5Ee1B2D21D495",
|
||||
[NetworkId.TESTNET_HOODI]: "0x6Ad50B1E293E68B2fC230c576220a93A9D311571",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x7C85cDEddBAd0f50453d373F7332BEa11ECa7BAf",
|
||||
};
|
||||
|
||||
export const DAO_TREASURY_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x93dd30f819403710de7933B79A74C4A42438458D",
|
||||
[NetworkId.TESTNET_HOODI]: "0x1a1b29b18f714fac9dDabEf530dFc4f85b56A6e8",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x5883C8e2259556B534036c7fDF4555E09dE9f243",
|
||||
};
|
||||
|
||||
export const FTSO_DAI_LP_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x1394dC3f7bABaa2F0CA80353648087DAB1BF3fd6",
|
||||
[NetworkId.TESTNET_HOODI]: "0xf7B2d44209E70782d93A70F7D8eC50010dF7ae50",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xE6546D12665dB5B22Cb92FB9e0221aE51A57aeaa",
|
||||
};
|
||||
|
||||
export const FTSO_STNK_LP_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x0000000000000000000000000000000000000000", // TBD
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x0000000000000000000000000000000000000000",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x0000000000000000000000000000000000000000",
|
||||
}
|
||||
|
||||
export const RESERVE_ADDRESSES = {
|
||||
export const DAI_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x5f63a27a9214a0352F2EF8dAF1eD4974d713192B",
|
||||
[NetworkId.TESTNET_HOODI]: "0x80c6676c334BCcE60b3CC852085B72143379CE58",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x6af91B3763b5d020E0985f85555EB50e5852d7AC",
|
||||
};
|
||||
|
||||
export const WETH_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xfff9976782d46cc05630d1f6ebab18b2324d6b14",
|
||||
[NetworkId.TESTNET_HOODI]: "0xE69a5c6dd88cA798b93c3C92fc50c51Fd5305eB4",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x6af91B3763b5d020E0985f85555EB50e5852d7AC",
|
||||
};
|
||||
|
||||
export const GHST_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xdf2e5306A3dCcfA4e21bbF4226C17Ff5B008dDC4",
|
||||
[NetworkId.TESTNET_HOODI]: "0xE98f7426457E6533B206e91B7EcA97aa8A258B46",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x14b5787F8a1E62786F50A7998A9b14aa24298423",
|
||||
};
|
||||
|
||||
export const STNK_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x02C296A27eA779d5a16F934337c12062C5E3c0D9",
|
||||
[NetworkId.TESTNET_HOODI]: "0xF07e9303A9f16Afd82f4f57Fd6fca68Aa0AB6D7F",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x137bA9403885D8ECEa95AaFBb8734F5a16121bAC",
|
||||
};
|
||||
|
||||
export const FTSO_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xcFedFFEB3FdeCd2196820Ba3b71f3F84A1255f93",
|
||||
[NetworkId.TESTNET_HOODI]: "0xb184e423811b644A1924334E63985c259F5D0033",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xeA170CC0faceC531a6a9e93a28C4330Ac50343a1",
|
||||
};
|
||||
|
||||
export const DISTRIBUTOR_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x8fbF8eB4Fcd451EF62Aee33508D46FE120963194",
|
||||
[NetworkId.TESTNET_HOODI]: "0xdF49dC81c457c6f92e26cf6d686C7a8715255842",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xaf5e76706520db7fb01096E322940206bf3fce57",
|
||||
};
|
||||
|
||||
export const GHOST_GOVERNANCE_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xDab0c51918E6990d8763FAC8a04AE159e44e0c4f",
|
||||
[NetworkId.TESTNET_HOODI]: "0x1B96B792840d4d19d5097ee007392Ed4d851e64F",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x3dD438416D9593A58193fC52850E588efAa3D57E",
|
||||
};
|
||||
|
||||
export const BONDING_CALCULATOR_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x4896bFc6256A57Df826d7144E48c9633d51d6319",
|
||||
[NetworkId.TESTNET_HOODI]: "0x2635d526Ad24b98082563937f7b996075052c6Fd",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x0c4C7C49a173E2a3f9Eed93125F3F146D8e17bCb",
|
||||
}
|
||||
|
||||
export const GATEKEEPER_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xc85129A097773B7F8970a7364c928C05f265E6A1",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xA59cB4ff90bE2206121aE61eEB68d0AeC7BA095f",
|
||||
}
|
||||
|
||||
export const UNISWAP_V2_ROUTER = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xee567fe1712faf6149d80da1e6934e354124cfe3",
|
||||
[NetworkId.TESTNET_HOODI]: "0xD41daF947c6FFEf344754B99ad09466FBCBb7583",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x90ecf6a29798E3cf31EB7DCE64a372AC40d83F83",
|
||||
};
|
||||
|
||||
export const UNISWAP_V2_FACTORY = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xF62c03E08ada871A0bEb309762E260a7a6a880E6",
|
||||
[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",
|
||||
],
|
||||
}
|
||||
|
||||
@ -18,11 +18,4 @@ export const AVAILABLE_DEXES = {
|
||||
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
@ -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>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
@ -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
|
||||
<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}%.`}
|
||||
<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>
|
||||
)
|
||||
}
|
||||
@ -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>
|
||||
)
|
||||
}
|
||||
@ -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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@ -26,7 +26,7 @@ import { Tab, Tabs } from "../../components/Tabs/Tabs";
|
||||
import {
|
||||
UNISWAP_V2_ROUTER,
|
||||
UNISWAP_V2_FACTORY,
|
||||
RESERVE_ADDRESSES,
|
||||
DAI_ADDRESSES,
|
||||
FTSO_ADDRESSES,
|
||||
} from "../../constants/addresses";
|
||||
import { useTokenSymbol } from "../../hooks/tokens";
|
||||
@ -56,7 +56,7 @@ const Dex = ({ chainId, address, connect }) => {
|
||||
const [slippage, setSlippage] = useState(localStorage.getItem("dex-slippage") || "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 { symbol: tokenNameTop } = useTokenSymbol(chainId, tokenAddressTop);
|
||||
@ -75,8 +75,8 @@ const Dex = ({ chainId, address, connect }) => {
|
||||
setTokenAddressTop(currentQueryParameters.get("from"));
|
||||
newQueryParameters.set("from", currentQueryParameters.get("from"));
|
||||
} else {
|
||||
setTokenAddressTop(RESERVE_ADDRESSES[chainId]);
|
||||
newQueryParameters.set("from", RESERVE_ADDRESSES[chainId]);
|
||||
setTokenAddressTop(DAI_ADDRESSES[chainId]);
|
||||
newQueryParameters.set("from", DAI_ADDRESSES[chainId]);
|
||||
}
|
||||
|
||||
if (currentQueryParameters.has("to")) {
|
||||
|
||||
@ -187,11 +187,11 @@ const SwapContainer = ({
|
||||
>
|
||||
<Box width="100%" display="flex" justifyContent="space-between">
|
||||
<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 width="100%" display="flex" justifyContent="space-between">
|
||||
<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 width="100%" display="flex" justifyContent="space-between">
|
||||
<Typography fontSize="12px" lineHeight="15px">Transaction deadline:</Typography>
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { useState, useEffect, useMemo } from "react";
|
||||
import { useConfig } from "wagmi";
|
||||
import {
|
||||
Divider,
|
||||
Typography,
|
||||
@ -20,8 +19,7 @@ import TokenStack from "../../components/TokenStack/TokenStack";
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { formatNumber } from "../../helpers/";
|
||||
import { useBalance, useTokenSymbol } from "../../hooks/tokens";
|
||||
import { isNetworkLegacy } from "../../constants";
|
||||
import { RESERVE_ADDRESSES, FTSO_ADDRESSES, STNK_ADDRESSES, GHST_ADDRESSES } from "../../constants/addresses";
|
||||
import { DAI_ADDRESSES, FTSO_ADDRESSES, STNK_ADDRESSES, GHST_ADDRESSES } from "../../constants/addresses";
|
||||
|
||||
const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }) => {
|
||||
const isSmallScreen = useMediaQuery("(max-width: 599px)");
|
||||
@ -39,19 +37,16 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }
|
||||
const { symbol: searchSymbol } = useTokenSymbol(chainId, address);
|
||||
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: stnkBalance } = useBalance(chainId, "STNK", 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: stnkSymbol } = useTokenSymbol(chainId, "STNK");
|
||||
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
|
||||
|
||||
const config = useConfig();
|
||||
const nativeSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
|
||||
|
||||
const searchToken = useMemo(() => {
|
||||
return [{
|
||||
name: searchSymbol,
|
||||
@ -64,10 +59,10 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }
|
||||
const knownTokens = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
name: reserveSymbol,
|
||||
icons: isNetworkLegacy(chainId) ? ["GDAI"] : [nativeSymbol],
|
||||
balance: reserveBalance,
|
||||
address: RESERVE_ADDRESSES[chainId]
|
||||
name: daiSymbol,
|
||||
icons: ["GDAI"],
|
||||
balance: daiBalance,
|
||||
address: DAI_ADDRESSES[chainId]
|
||||
},
|
||||
{
|
||||
name: ftsoSymbol,
|
||||
@ -88,7 +83,7 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }
|
||||
address: GHST_ADDRESSES[chainId]
|
||||
}
|
||||
]
|
||||
}, [reserveSymbol, ftsoSymbol, stnkSymbol, ghstSymbol, reserveBalance, ftsoBalance, stnkBalance, ghstBalance]);
|
||||
}, [daiSymbol, ftsoSymbol, stnkSymbol, ghstSymbol, daiBalance, ftsoBalance, stnkBalance, ghstBalance]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isAddress(userInput)) {
|
||||
|
||||
@ -11,7 +11,7 @@ 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 { DAI_ADDRESSES } from "../../constants/addresses";
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { formatCurrency, formatNumber } from "../../helpers";
|
||||
|
||||
@ -44,14 +44,14 @@ const Faucet = ({ chainId, address, config, connect }) => {
|
||||
symbol: "",
|
||||
})
|
||||
|
||||
const reserveConversionRate = useConversionRate(chainId, "RESERVE");
|
||||
const accumulatedDonation = useAccumulatedDonation(chainId, "RESERVE");
|
||||
const daiConversionRate = useConversionRate(chainId, "GDAI");
|
||||
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: contractBalance, refetch: contractBalanceRefetch } = useBalance({ address: RESERVE_ADDRESSES[chainId] });
|
||||
const { totalSupply: reserveTotalSupply, refetch: refetchReserveTotalSupply } = useTotalSupply(chainId, "RESERVE");
|
||||
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE");
|
||||
const { data: contractBalance, refetch: contractBalanceRefetch } = useBalance({ address: DAI_ADDRESSES[chainId] });
|
||||
const { totalSupply: reserveTotalSupply, refetch: refetchReserveTotalSupply } = useTotalSupply(chainId, "GDAI");
|
||||
const { symbol: faucetSymbol } = useTokenSymbol(chainId, "GDAI");
|
||||
|
||||
useEffect(() => {
|
||||
ReactGA.send({ hitType: "pageview", page: "/faucet" });
|
||||
@ -93,10 +93,10 @@ const Faucet = ({ chainId, address, config, connect }) => {
|
||||
}, [amount, balance, nativeInfo])
|
||||
|
||||
const estimatedAmountIn = useMemo(() => {
|
||||
const rate = new DecimalBigNumber(reserveConversionRate.toString(), 0);
|
||||
const rate = new DecimalBigNumber(daiConversionRate.toString(), 0);
|
||||
const value = new DecimalBigNumber(amount, nativeInfo.decimals);
|
||||
return value.mul(rate);
|
||||
}, [amount, reserveConversionRate, nativeInfo]);
|
||||
}, [amount, daiConversionRate, nativeInfo]);
|
||||
|
||||
const contractBalanceFree = useMemo(() => {
|
||||
const realContractBalance = contractBalance ? contractBalance.value : 0n;
|
||||
@ -129,7 +129,7 @@ const Faucet = ({ chainId, address, config, connect }) => {
|
||||
}
|
||||
|
||||
await balanceRefetch();
|
||||
await reserveBalanceRefetch();
|
||||
await daiBalanceRefetch();
|
||||
await contractBalanceRefetch();
|
||||
await refetchReserveTotalSupply();
|
||||
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" />
|
||||
</Helmet>
|
||||
|
||||
<PageTitle name={`${reserveSymbol} Faucet`} subtitle={`Swap Sepolia ${nativeInfo.symbol} for ${reserveSymbol}.`} />
|
||||
<PageTitle name={`${faucetSymbol} Faucet`} subtitle={`Swap Sepolia ${nativeInfo.symbol} for ${faucetSymbol}.`} />
|
||||
<Container
|
||||
style={{
|
||||
paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
|
||||
@ -188,7 +188,7 @@ const Faucet = ({ chainId, address, config, connect }) => {
|
||||
</Tabs>
|
||||
{!isSemiSmallScreen && <PrimaryButton
|
||||
variant="text"
|
||||
href={`${scanInfo.url}/token/${RESERVE_ADDRESSES[chainId]}`}
|
||||
href={`${scanInfo.url}/token/${DAI_ADDRESSES[chainId]}`}
|
||||
>
|
||||
Check on {scanInfo.name}
|
||||
</PrimaryButton>}
|
||||
@ -211,14 +211,14 @@ const Faucet = ({ chainId, address, config, connect }) => {
|
||||
{!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)}`}
|
||||
tokenName={faucetSymbol}
|
||||
token={<TokenStack tokens={[faucetSymbol]} sx={{ fontSize: "21px" }} />}
|
||||
info={`${formatCurrency(daiBalance.toString(), 4, faucetSymbol)}`}
|
||||
value={amount}
|
||||
onChange={event => setAmount(event.currentTarget.value)}
|
||||
inputProps={{ "data-testid": "fromInput" }}
|
||||
endString={"Max"}
|
||||
endStringOnClick={() => setAmount(reserveBalance.toString())}
|
||||
endStringOnClick={() => setAmount(daiBalance.toString())}
|
||||
/>}
|
||||
<Box
|
||||
mb="20px"
|
||||
@ -231,15 +231,15 @@ const Faucet = ({ chainId, address, config, connect }) => {
|
||||
<>
|
||||
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
||||
{!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 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>
|
||||
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(estimatedAmountIn, 5, faucetSymbol)}</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>
|
||||
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Your {faucetSymbol} balance:</Typography>}
|
||||
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(daiBalance, 5, faucetSymbol)}</Typography>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
@ -267,7 +267,7 @@ const Faucet = ({ chainId, address, config, connect }) => {
|
||||
preparedAmount?._value === 0n ||
|
||||
isPending ||
|
||||
(isMint && balance?.lt(preparedAmount)) ||
|
||||
(!isMint && reserveBalance?.lt(preparedAmount))
|
||||
(!isMint && daiBalance?.lt(preparedAmount))
|
||||
)
|
||||
}
|
||||
loading={isPending}
|
||||
|
||||
@ -16,33 +16,34 @@ import { SecondaryButton } from "../../../components/Button";
|
||||
import TokenStack from "../../../components/TokenStack/TokenStack";
|
||||
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
|
||||
import { formatCurrency } from "../../../helpers";
|
||||
import { tokenNameConverter } from "../../../helpers/tokenConverter";
|
||||
import { isNetworkLegacy } from "../../../constants";
|
||||
|
||||
import { useLpValuation } from "../../../hooks/treasury";
|
||||
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 isSmallScreen = useMediaQuery("(max-width: 775px)");
|
||||
|
||||
const { totalSupply: reserveFtsoUniTotalSupply } = useTotalSupply(chainId, "RESERVE_FTSO");
|
||||
const reserveFtsoUniValuation = useLpValuation(chainId, "RESERVE_FTSO", reserveFtsoUniTotalSupply._value);
|
||||
const { totalSupply: daiFtsoUniTotalSupply } = useTotalSupply(chainId, "GDAI_FTSO");
|
||||
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 pools = [
|
||||
{
|
||||
icons: ["FTSO", isNetworkLegacy(chainId) ? "GDAI" : tokenNameConverter(chainId, reserveSymbol)],
|
||||
name: `${ftsoSymbol}-${reserveSymbol}`,
|
||||
icons: ["FTSO", "GDAI"],
|
||||
name: `${ftsoSymbol}-${daiSymbol}`,
|
||||
dex: "Uniswap V2",
|
||||
url: "/dex/uniswap",
|
||||
tvl: reserveFtsoUniValuation,
|
||||
tvl: daiFtsoUniValuation,
|
||||
params: createSearchParams({
|
||||
pool: "true",
|
||||
from: `${RESERVE_ADDRESSES[chainId]}`,
|
||||
from: `${DAI_ADDRESSES[chainId]}`,
|
||||
to: `${FTSO_ADDRESSES[chainId]}`,
|
||||
})
|
||||
},
|
||||
|
||||
@ -1,22 +1,19 @@
|
||||
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 Token from "../../../components/Token/Token";
|
||||
import { SecondaryButton } from "../../../components/Button";
|
||||
import { formatNumber, formatCurrency } from "../../../helpers";
|
||||
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
|
||||
import { isNetworkLegacy } from "../../../constants"
|
||||
|
||||
import { useBalance, useTokenSymbol } from "../../../hooks/tokens";
|
||||
import {
|
||||
useFtsoPrice,
|
||||
useStnkPrice,
|
||||
useGhstPrice,
|
||||
useReservePrice,
|
||||
useNativePrice,
|
||||
useDaiPrice,
|
||||
} from "../../../hooks/prices";
|
||||
import { tokenNameConverter } from "../../../helpers/tokenConverter";
|
||||
|
||||
const TokenTab = ({ isMobileScreen, theme, tokenName, tokenUrl, tokenUrlParams, balance, price, description }) => {
|
||||
const navigate = useNavigate();
|
||||
@ -77,26 +74,20 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
|
||||
const theme = useTheme();
|
||||
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 stnkPrice = useStnkPrice(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: stnkSymbol } = useTokenSymbol(chainId, "STNK");
|
||||
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
|
||||
|
||||
const { data: nativeBalance } = useBalanceNative({ address });
|
||||
const { balance: ftsoBalance, contractAddress: ftsoAddress } = useBalance(chainId, "FTSO", address);
|
||||
const { balance: stnkBalance, contractAddress: stnkAddress } = useBalance(chainId, "STNK", 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 (
|
||||
<Grid container spacing={0} justifyContent={"center"}>
|
||||
@ -105,7 +96,7 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
|
||||
isMobileScreen={isMobileScreen}
|
||||
tokenUrl="/dex/uniswap"
|
||||
tokenUrlParams={createSearchParams({
|
||||
from: `${reserveAddress}`,
|
||||
from: `${daiAddress}`,
|
||||
to: `${ftsoAddress}`,
|
||||
})}
|
||||
theme={theme}
|
||||
@ -118,7 +109,7 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
|
||||
isMobileScreen={isMobileScreen}
|
||||
tokenUrl="/dex/uniswap"
|
||||
tokenUrlParams={createSearchParams({
|
||||
from: `${reserveAddress}`,
|
||||
from: `${daiAddress}`,
|
||||
to: `${stnkAddress}`,
|
||||
})}
|
||||
theme={theme}
|
||||
@ -131,7 +122,7 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
|
||||
isMobileScreen={isMobileScreen}
|
||||
tokenUrl="/dex/uniswap"
|
||||
tokenUrlParams={createSearchParams({
|
||||
from: `${reserveAddress}`,
|
||||
from: `${daiAddress}`,
|
||||
to: `${ghstAddress}`,
|
||||
})}
|
||||
theme={theme}
|
||||
@ -142,32 +133,15 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
|
||||
/>
|
||||
<TokenTab
|
||||
isMobileScreen={isMobileScreen}
|
||||
tokenUrl={isNetworkLegacy(chainId) ? "/faucet" : "/wrapper"}
|
||||
tokenUrl="/faucet"
|
||||
tokenUrlParams=""
|
||||
theme={theme}
|
||||
tokenName={reserveSymbol}
|
||||
balance={reserveBalance}
|
||||
price={reservePrice}
|
||||
description={isNetworkLegacy(chainId)
|
||||
? `${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}.`
|
||||
}
|
||||
tokenName={daiSymbol}
|
||||
balance={daiBalance}
|
||||
price={daiPrice}
|
||||
description={`${ftsoSymbol} is backed by a treasury reserve of crypto assets, with ${daiSymbol} being the primary and most liquid asset.`}
|
||||
/>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -41,10 +41,3 @@ export const formatNumber = (number, precision = 0) => {
|
||||
export const sortBondsByDiscount = (bonds) => {
|
||||
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`;
|
||||
}
|
||||
|
||||
@ -1,10 +0,0 @@
|
||||
export const tokenNameConverter = (chainId, name) => {
|
||||
if (name?.toUpperCase() === "WETH") {
|
||||
switch (chainId) {
|
||||
case 63:
|
||||
name = "wmETC"
|
||||
break;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
@ -4,7 +4,7 @@ import { createClient } from "@polkadot-api/substrate-client"
|
||||
import { getObservableClient } from "@polkadot-api/observable-client"
|
||||
import useSWR from "swr"
|
||||
|
||||
const DEFAULT_CHAIN_ID = "0x5e1190682f1a6409cdfd691c0b23a6db792864d8994e591e9c19a31d8163989f"
|
||||
const DEFAULT_CHAIN_ID = "0xa217f4ee58a944470e9633ca5bd6d28a428ed64cd9b6f3e413565f359f89af90"
|
||||
const UnstableProvider = createContext(null)
|
||||
export const useUnstableProvider = () => useContext(UnstableProvider)
|
||||
|
||||
|
||||
@ -6,9 +6,4 @@ export * from "./useClapsInSession";
|
||||
export * from "./useApplauseThreshold";
|
||||
export * from "./useReceivedClaps";
|
||||
export * from "./useAuthorities";
|
||||
export * from "./useValidators";
|
||||
export * from "./useDisabledValidators";
|
||||
export * from "./useBlockCommitments";
|
||||
export * from "./useApplauseDetails";
|
||||
export * from "./useBabeSlots";
|
||||
export * from "./useErasTotalStaked";
|
||||
export * from "./useApplausesForTransaction";
|
||||
|
||||
@ -6,41 +6,42 @@ import { fromHex } from "@polkadot-api/utils";
|
||||
import { useUnstableProvider } from "./UnstableProvider"
|
||||
import { useMetadata } from "./MetadataProvider"
|
||||
|
||||
export const useApplauseDetails = ({ currentSession, argsHash }) => {
|
||||
export const useApplausesForTransaction = ({ currentSession, txHash, argsHash }) => {
|
||||
const { chainHead$, chainId } = useUnstableProvider()
|
||||
const metadata = useMetadata()
|
||||
const { data: applauseDetails } = useSWRSubscription(
|
||||
chainHead$ && argsHash && currentSession && chainId && metadata
|
||||
? ["applauseDetails", chainHead$, argsHash, currentSession, chainId, metadata]
|
||||
const { data: applausesForTransaction } = useSWRSubscription(
|
||||
chainHead$ && txHash && argsHash && currentSession && chainId && metadata
|
||||
? ["applausesForTransaction", chainHead$, txHash, argsHash, currentSession, chainId, metadata]
|
||||
: null,
|
||||
([_, chainHead$, argsHash, currentSession, chainId, metadata], { next }) => {
|
||||
([_, chainHead$, txHash, argsHash, currentSession, chainId, metadata], { next }) => {
|
||||
const { finalized$, storage$ } = chainHead$
|
||||
const subscription = finalized$.pipe(
|
||||
filter(Boolean),
|
||||
mergeMap((blockInfo) => {
|
||||
const builder = getDynamicBuilder(getLookupFn(metadata))
|
||||
const applauseDetails = builder.buildStorage("GhostSlowClaps", "ApplauseDetails")
|
||||
const applausesForTransaction = builder.buildStorage("GhostSlowClaps", "ApplausesForTransaction")
|
||||
|
||||
return storage$(blockInfo?.hash, "value", () =>
|
||||
applauseDetails?.keys.enc(
|
||||
applausesForTransaction?.keys.enc(
|
||||
currentSession,
|
||||
{ asBytes: () => fromHex(txHash) },
|
||||
{ asBytes: () => fromHex(argsHash) },
|
||||
)
|
||||
).pipe(
|
||||
filter(Boolean),
|
||||
distinct(),
|
||||
map((value) => applauseDetails?.value.dec(value))
|
||||
map((value) => applausesForTransaction?.value.dec(value))
|
||||
)
|
||||
}),
|
||||
)
|
||||
.subscribe({
|
||||
next(applauseDetails) {
|
||||
next(null, applauseDetails)
|
||||
next(applausesForTransaction) {
|
||||
next(null, applausesForTransaction)
|
||||
},
|
||||
error: next,
|
||||
})
|
||||
return () => subscription.unsubscribe()
|
||||
}
|
||||
)
|
||||
return applauseDetails
|
||||
return applausesForTransaction
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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;
|
||||
}, []);
|
||||
}
|
||||
@ -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 : []
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -1,10 +1,9 @@
|
||||
import {
|
||||
RESERVE_ADDRESSES,
|
||||
DAI_ADDRESSES,
|
||||
FTSO_ADDRESSES,
|
||||
STNK_ADDRESSES,
|
||||
GHST_ADDRESSES,
|
||||
FTSO_DAI_LP_ADDRESSES,
|
||||
WETH_ADDRESSES,
|
||||
} from "../constants/addresses";
|
||||
|
||||
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 GhostAbi } from "../abi/Ghost.json";
|
||||
import { abi as Erc20Abi } from "../abi/ERC20.json";
|
||||
import { abi as WethAbi } from "../abi/WETH9.json";
|
||||
|
||||
// TBD: should be extended on new tokens
|
||||
export const getTokenAbi = (name) => {
|
||||
@ -24,9 +22,6 @@ export const getTokenAbi = (name) => {
|
||||
case "GDAI":
|
||||
abi = DaiAbi;
|
||||
break;
|
||||
case "RESERVE":
|
||||
abi = DaiAbi;
|
||||
break;
|
||||
case "FTSO":
|
||||
abi = FatsoAbi;
|
||||
break;
|
||||
@ -45,9 +40,6 @@ export const getTokenAbi = (name) => {
|
||||
case "CSPR":
|
||||
abi = GhostAbi;
|
||||
break;
|
||||
case "WETH":
|
||||
abi = WethAbi;
|
||||
break;
|
||||
}
|
||||
return abi;
|
||||
}
|
||||
@ -62,9 +54,6 @@ export const getTokenDecimals = (name) => {
|
||||
case "GDAI":
|
||||
decimals = 18;
|
||||
break;
|
||||
case "RESERVE":
|
||||
decimals = 18;
|
||||
break;
|
||||
case "FTSO":
|
||||
decimals = 9;
|
||||
break;
|
||||
@ -83,9 +72,6 @@ export const getTokenDecimals = (name) => {
|
||||
case "CSPR":
|
||||
decimals = 18;
|
||||
break;
|
||||
case "WETH":
|
||||
decimals = 18;
|
||||
break;
|
||||
}
|
||||
return decimals;
|
||||
}
|
||||
@ -95,13 +81,10 @@ export const getTokenAddress = (chainId, name) => {
|
||||
let address = name;
|
||||
switch (name?.toUpperCase()) {
|
||||
case "DAI":
|
||||
address = RESERVE_ADDRESSES[chainId];
|
||||
address = DAI_ADDRESSES[chainId];
|
||||
break;
|
||||
case "GDAI":
|
||||
address = RESERVE_ADDRESSES[chainId];
|
||||
break;
|
||||
case "RESERVE":
|
||||
address = RESERVE_ADDRESSES[chainId];
|
||||
address = DAI_ADDRESSES[chainId];
|
||||
break;
|
||||
case "FTSO":
|
||||
address = FTSO_ADDRESSES[chainId];
|
||||
@ -124,15 +107,6 @@ export const getTokenAddress = (chainId, name) => {
|
||||
case "GDAI_FTSO":
|
||||
address = FTSO_DAI_LP_ADDRESSES[chainId];
|
||||
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;
|
||||
}
|
||||
@ -141,8 +115,8 @@ export const getTokenAddress = (chainId, name) => {
|
||||
export const getTokenIcons = (chainId, address) => {
|
||||
let icons = [""];
|
||||
switch (address) {
|
||||
case RESERVE_ADDRESSES[chainId]:
|
||||
icons = ["RESERVE"];
|
||||
case DAI_ADDRESSES[chainId]:
|
||||
icons = ["GDAI"];
|
||||
break;
|
||||
case FTSO_ADDRESSES[chainId]:
|
||||
icons = ["FTSO"];
|
||||
@ -154,7 +128,7 @@ export const getTokenIcons = (chainId, address) => {
|
||||
icons = ["GHST"];
|
||||
break;
|
||||
case FTSO_DAI_LP_ADDRESSES[chainId]:
|
||||
icons = ["FTSO", "RESERVE"];
|
||||
icons = ["FTSO", "GDAI"];
|
||||
break;
|
||||
}
|
||||
return icons;
|
||||
@ -170,14 +144,11 @@ export const getBondNameDisplayName = (chainId, stringValue, tokenAddress) => {
|
||||
export const getTokenPurchaseLink = (chainId, tokenAddress) => {
|
||||
let purchaseUrl = "https://app.dao.ghostchain.io/#/dex/uniswap";
|
||||
switch (tokenAddress) {
|
||||
case RESERVE_ADDRESSES[chainId]:
|
||||
case DAI_ADDRESSES[chainId]:
|
||||
purchaseUrl = "https://app.dao.ghostchain.io/#/faucet";
|
||||
if (chainId == 63) {
|
||||
purchaseUrl = "https://app.dao.ghostchain.io/#/wrapper";
|
||||
}
|
||||
break;
|
||||
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;
|
||||
}
|
||||
return purchaseUrl;
|
||||
|
||||
@ -1,144 +1,26 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useReadContract } from "wagmi";
|
||||
|
||||
import { useCurrentIndex, useGhostedSupply } from "../staking";
|
||||
import { useUniswapV2PairReserves } from "../uniswapv2";
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import {
|
||||
FTSO_DAI_LP_ADDRESSES,
|
||||
RESERVE_ADDRESSES,
|
||||
FTSO_ADDRESSES,
|
||||
CEX_TICKERS,
|
||||
NATIVE_TICKERS,
|
||||
} from "../../constants/addresses";
|
||||
import { FTSO_DAI_LP_ADDRESSES, DAI_ADDRESSES, FTSO_ADDRESSES } from "../../constants/addresses";
|
||||
|
||||
const cexPriceGetters = new Map();
|
||||
|
||||
function callWithCacheTTL(fn, ttlMs = 10000) {
|
||||
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 useDaiPrice = (chainId) => {
|
||||
const daiPrice = new DecimalBigNumber(1000000000000000000n, 18);
|
||||
return daiPrice;
|
||||
};
|
||||
|
||||
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 = RESERVE_ADDRESSES[chainId];
|
||||
const reserveAddress = DAI_ADDRESSES[chainId];
|
||||
const ftsoAddress = FTSO_ADDRESSES[chainId];
|
||||
if (!reserveAddress || !ftsoAddress) {
|
||||
return new DecimalBigNumber(0n, 9);
|
||||
@ -155,9 +37,8 @@ export const useFtsoPrice = (chainId) => {
|
||||
let price = 0n
|
||||
if (ftsoReserves > 0n)
|
||||
price = stableReserves / ftsoReserves;
|
||||
price = price * reservePrice._value;
|
||||
|
||||
return new DecimalBigNumber(price, 27);
|
||||
return new DecimalBigNumber(price, 9);
|
||||
};
|
||||
|
||||
export const useStnkPrice = (chainId) => {
|
||||
|
||||
@ -6,7 +6,6 @@ import { getTokenAbi, getTokenAddress, getTokenDecimals } from "../helpers";
|
||||
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { shorten } from "../../helpers";
|
||||
import { tokenNameConverter } from "../../helpers/tokenConverter";
|
||||
import { config } from "../../config";
|
||||
|
||||
export const useTotalSupply = (chainId, name) => {
|
||||
@ -69,9 +68,7 @@ export const useTokenSymbol = (chainId, name) => {
|
||||
chainId: chainId,
|
||||
});
|
||||
|
||||
let symbol = data ? data : "";
|
||||
symbol = tokenNameConverter(chainId, symbol);
|
||||
|
||||
const symbol = data ? data : "";
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
||||
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.")
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,23 +3,9 @@ import { useReadContract } from "wagmi";
|
||||
import { DAO_TREASURY_ADDRESSES } from "../../constants/addresses";
|
||||
import { abi as TreasuryAbi } from "../../abi/GhostTreasury.json";
|
||||
|
||||
import { useReservePrice } from "../prices/index";
|
||||
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
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) => {
|
||||
const { data: totalReservesRaw } = useReadContract({
|
||||
abi: TreasuryAbi,
|
||||
@ -29,13 +15,10 @@ export const useTotalReserves = (chainId) => {
|
||||
chainId: chainId,
|
||||
});
|
||||
|
||||
const original = useOrinalCoefficient(chainId);
|
||||
const price = useReservePrice(chainId);
|
||||
|
||||
const totalReservesPrepared = totalReservesRaw ? totalReservesRaw : 0n;
|
||||
const totalReserves = new DecimalBigNumber(totalReservesPrepared, 9);
|
||||
|
||||
return totalReserves.mul(price).div(original);
|
||||
return totalReserves;
|
||||
};
|
||||
|
||||
export const useLpValuation = (chainId, pairAddress, pairSupply) => {
|
||||
|
||||
@ -128,48 +128,3 @@ a:hover svg {
|
||||
.tooltip {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -18,18 +18,9 @@ export const darkPalette = {
|
||||
success: "#60C45B", // idk where this is - done
|
||||
userFeedback: "#49A1F2", // idk where this is
|
||||
error: "#F06F73", // red negative % - done
|
||||
warning: "#ed6c02", // idk where this is - done
|
||||
warning: "#49A1F2", // idk where this is - done
|
||||
pnlGain: "#60C45B", // green positive % - done
|
||||
},
|
||||
bridgeProgress: {
|
||||
error: "#F06F73",
|
||||
warning: "#ed6c02",
|
||||
success: "#60C45B",
|
||||
},
|
||||
validatorsColor: {
|
||||
red: "#fd9b9e",
|
||||
green: "#60c45b",
|
||||
},
|
||||
gray: {
|
||||
800: "#1F4671", // active menu - done
|
||||
700: "#50759E", // menu background color - done
|
||||
|
||||
@ -6,20 +6,11 @@ export const lightPalette = {
|
||||
},
|
||||
background: "linear-gradient(180.37deg, #B3BFC5 0.49%, #D1D5D4 26.3%, #EEEAE3 99.85%)",
|
||||
feedback: {
|
||||
success: "#60C45B", // idk where this is - done
|
||||
userFeedback: "#49A1F2", // idk where this is
|
||||
error: "#F06F73", // red negative % - done
|
||||
warning: "#ed6c02", // idk where this is - done
|
||||
pnlGain: "#60C45B", // green positive % - done
|
||||
},
|
||||
bridgeProgress: {
|
||||
error: "#F06F73",
|
||||
warning: "#ed6c02",
|
||||
success: "#60C45B",
|
||||
},
|
||||
validatorsColor: {
|
||||
red: "#fd9b9e",
|
||||
green: "#60c45b",
|
||||
success: "#94B9A1",
|
||||
userFeedback: "#49A1F2",
|
||||
error: "#FF6767",
|
||||
warning: "#FC8E5F",
|
||||
pnlGain: "#3D9C70",
|
||||
},
|
||||
gray: {
|
||||
700: "#FAFAFB",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user