support for native reserve added; e.g. mordor testnet

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2025-10-29 14:29:14 +03:00
parent e6ed1596ef
commit 494df5f139
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
17 changed files with 503 additions and 13 deletions

View File

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

View File

@ -14,7 +14,7 @@ import Sidebar from "./components/Sidebar/Sidebar";
import TopBar from "./components/TopBar/TopBar";
import { shouldTriggerSafetyCheck } from "./helpers";
import { isNetworkAvailable } from "./constants";
import { isNetworkAvailable, isNetworkLegacy } from "./constants";
import useTheme from "./hooks/useTheme";
import { useUnstableProvider } from "./hooks/ghost";
import { dark as darkTheme } from "./themes/dark.js";
@ -27,6 +27,7 @@ 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"));
@ -206,7 +207,10 @@ 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}/>} />
<Route path="/faucet" element={<Faucet config={config} 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="/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
src/abi/WETH9.json Normal file
View File

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

View File

@ -37,7 +37,7 @@ import BondIcon from "../Icon/BondIcon";
import StakeIcon from "../Icon/StakeIcon";
import WrapIcon from "../Icon/WrapIcon";
import { isNetworkAvailable } from "../../constants";
import { isNetworkAvailable, isNetworkLegacy } from "../../constants";
import { AVAILABLE_DEXES } from "../../constants/dexes";
import { ECOSYSTEM } from "../../constants/ecosystem";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
@ -46,6 +46,7 @@ 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";
@ -148,7 +149,10 @@ const NavContent = ({ chainId, addressChainId }) => {
}
/>
<NavItem icon={StakeIcon} label={`Stake`} to="/stake" />
<NavItem icon={ShowerIcon} label={`Faucet`} to="/faucet" />
{isNetworkLegacy(chainId)
? <NavItem icon={ShowerIcon} label={`Faucet`} to="/faucet" />
: <NavItem icon={WifiProtectedSetupIcon} label={`Wrapper`} to="/wrapper" />
}
<NavItem icon={PublicIcon} label={`Bridge`} to="/bridge" />
<NavItem
icon={CurrencyExchangeIcon}

View File

@ -57,6 +57,9 @@ const Token = ({ name, viewBox = "0 0 260 260", fontSize = "large", ...props })
case "WETH":
icon = WethIcon;
break;
case "METC":
icon = WethIcon;
break;
default:
icon = UnknownIcon;
}

View File

@ -1,8 +1,28 @@
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],
chains: [sepolia, hoodi, mordor],
transports: {
[sepolia.id]: fallback([
http('https://ethereum-sepolia-rpc.publicnode.com'),
@ -16,6 +36,9 @@ 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'),
])
},
})

View File

@ -1,10 +1,29 @@
export enum NetworkId {
TESTNET_SEPOLIA = 11155111,
TESTNET_HOODI = 560048,
TESTNET_MORDOR = 63,
}
export const isNetworkAvailable = (chainId, addressChainId) => {
chainId = addressChainId ? addressChainId : chainId;
let exists = false;
switch (chainId) {
case 11155111:
exists = true
break;
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:

View File

@ -3,78 +3,98 @@ 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 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 CEX_TICKERS = {
[NetworkId.TESTNET_MORDOR]: "ETCUSDT",
}

View File

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

View File

@ -19,6 +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 { DAI_ADDRESSES, FTSO_ADDRESSES, STNK_ADDRESSES, GHST_ADDRESSES } from "../../constants/addresses";
const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }) => {
@ -60,7 +61,7 @@ const TokenModal = ({ chainId, account, listOpen, setListOpen, setTokenAddress }
return [
{
name: daiSymbol,
icons: ["GDAI"],
icons: isNetworkLegacy(chainId) ? ["GDAI"] : ["WETH"],
balance: daiBalance,
address: DAI_ADDRESSES[chainId]
},

View File

@ -6,6 +6,7 @@ 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 {
@ -133,7 +134,7 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
/>
<TokenTab
isMobileScreen={isMobileScreen}
tokenUrl="/faucet"
tokenUrl={isNetworkLegacy(chainId) ? "/faucet" : "/wrapper"}
tokenUrlParams=""
theme={theme}
tokenName={daiSymbol}

View File

@ -0,0 +1,260 @@
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 { DAI_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: daiBalance, refetch: daiBalanceRefetch } = useTokenBalance(chainId, "GDAI", address);
const { data: nativeBalance, refetch: balanceRefetch } = useBalance({ address });
const { symbol: faucetSymbol } = useTokenSymbol(chainId, "GDAI");
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: "/faucet" });
}, [])
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 daiBalanceRefetch();
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={`${faucetSymbol} Faucet`} subtitle={`Swap ${chainName} ${nativeInfo.symbol} for ${faucetSymbol}.`} />
<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="Mint" style={{ fontSize: "1.5rem" }} />
<Tab aria-label="faucet-burn-button" label="Burn" style={{ fontSize: "1.5rem" }} />
</Tabs>
{!isSemiSmallScreen && <PrimaryButton
variant="text"
href={`${scanInfo.url}/token/${DAI_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={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(daiBalance.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, faucetSymbol)}</Typography>
</Box>
<Box display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Your {faucetSymbol} balance:</Typography>}
<Typography fontSize="12px" lineHeight="15px">{formatCurrency(daiBalance, 5, faucetSymbol)}</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 && daiBalance?.lt(preparedAmount))
)
}
loading={isPending}
onClick={() => actionOrConnect()}
>
{address === "" ?
"Connect"
:
isMint ? "Mint" : "Burn"
}
</PrimaryButton>
</Box>
</Paper>
</Box>
</Container>
</Box>
)
}
export default WethWrapper;

View File

@ -4,6 +4,7 @@ import {
STNK_ADDRESSES,
GHST_ADDRESSES,
FTSO_DAI_LP_ADDRESSES,
WETH_ADDRESSES,
} from "../constants/addresses";
import { abi as DaiAbi } from "../abi/Reserve.json";
@ -11,6 +12,7 @@ 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) => {
@ -40,6 +42,9 @@ export const getTokenAbi = (name) => {
case "CSPR":
abi = GhostAbi;
break;
case "WETH":
abi = WethAbi;
break;
}
return abi;
}
@ -72,6 +77,9 @@ export const getTokenDecimals = (name) => {
case "CSPR":
decimals = 18;
break;
case "WETH":
decimals = 18;
break;
}
return decimals;
}
@ -107,6 +115,9 @@ export const getTokenAddress = (chainId, name) => {
case "GDAI_FTSO":
address = FTSO_DAI_LP_ADDRESSES[chainId];
break;
case "WETH":
address = WETH_ADDRESSES[chainId];
break;
}
return address;
}

View File

@ -1,12 +1,79 @@
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, DAI_ADDRESSES, FTSO_ADDRESSES } from "../../constants/addresses";
import {
FTSO_DAI_LP_ADDRESSES,
DAI_ADDRESSES,
FTSO_ADDRESSES,
CEX_TICKERS
} from "../../constants/addresses";
const cexPriceGetters = new Map();
function callWithCacheTTL(fn, ttlMs = 5000) {
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 useDaiPrice = (chainId) => {
const daiPrice = new DecimalBigNumber(1000000000000000000n, 18);
const [daiPrice, setDaiPrice] = useState(new DecimalBigNumber(1000000000000000000n, 18));
const cexTicker = CEX_TICKERS[chainId];
useEffect(() => {
if (!cexTicker) {
setDaiPrice(new DecimalBigNumber(1000000000000000000n, 18));
return;
}
let getCexPriceCached = cexPriceGetters.get(chainId);
if (!getCexPriceCached) {
getCexPriceCached = callWithCacheTTL(() => {
return fetch(`https://api.binance.com/api/v3/ticker/price?symbol=${cexTicker}`)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json();
});
}, 5000);
cexPriceGetters.set(chainId, getCexPriceCached);
}
getCexPriceCached()
.then(price => {
const coinPrice = Number(price?.price ?? 0.0);
const priceInWei = Math.floor(coinPrice * 1e18)
setDaiPrice(new DecimalBigNumber(BigInt(priceInWei), 18));
})
.catch(error => {
setDaiPrice(new DecimalBigNumber(0n, 18));
});
}, [chainId, cexTicker])
return daiPrice;
};
@ -20,6 +87,7 @@ export const useFtsoPrice = (chainId) => {
"GDAI",
);
const reservePrice = useDaiPrice(chainId);
const reserveAddress = DAI_ADDRESSES[chainId];
const ftsoAddress = FTSO_ADDRESSES[chainId];
if (!reserveAddress || !ftsoAddress) {
@ -37,8 +105,9 @@ export const useFtsoPrice = (chainId) => {
let price = 0n
if (ftsoReserves > 0n)
price = stableReserves / ftsoReserves;
price = price * reservePrice._value;
return new DecimalBigNumber(price, 9);
return new DecimalBigNumber(price, 27);
};
export const useStnkPrice = (chainId) => {

View File

@ -229,3 +229,53 @@ 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.")
}
}

View File

@ -3,9 +3,23 @@ import { useReadContract } from "wagmi";
import { DAO_TREASURY_ADDRESSES } from "../../constants/addresses";
import { abi as TreasuryAbi } from "../../abi/GhostTreasury.json";
import { useDaiPrice } 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,
@ -15,10 +29,13 @@ export const useTotalReserves = (chainId) => {
chainId: chainId,
});
const original = useOrinalCoefficient(chainId);
const price = useDaiPrice(chainId);
const totalReservesPrepared = totalReservesRaw ? totalReservesRaw : 0n;
const totalReserves = new DecimalBigNumber(totalReservesPrepared, 9);
return totalReserves;
return totalReserves.mul(price).div(original);
};
export const useLpValuation = (chainId, pairAddress, pairSupply) => {