change dashboard table
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
be5b102522
commit
e102d819a0
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ghost-dao-interface",
|
"name": "ghost-dao-interface",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.13",
|
"version": "0.7.14",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
1
src/abi/GhostGatekeeper.json
Normal file
1
src/abi/GhostGatekeeper.json
Normal file
File diff suppressed because one or more lines are too long
@ -12,6 +12,7 @@ import {
|
|||||||
CurrentIndex,
|
CurrentIndex,
|
||||||
} from "./components/Metric";
|
} from "./components/Metric";
|
||||||
import TokenInfo from "./components/TokenInfo";
|
import TokenInfo from "./components/TokenInfo";
|
||||||
|
import ProtocolDetails from "./components/ProtocolDetails";
|
||||||
|
|
||||||
import Paper from "../../components/Paper/Paper";
|
import Paper from "../../components/Paper/Paper";
|
||||||
import PageTitle from "../../components/PageTitle/PageTitle";
|
import PageTitle from "../../components/PageTitle/PageTitle";
|
||||||
@ -60,7 +61,7 @@ const MetricsDashboard = ({ chainId }) => {
|
|||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
<Paper style={{ padding: "0px" }} fullWidth={true} >
|
<Paper style={{ padding: "0px" }} fullWidth={true} >
|
||||||
<TokenInfo isMobileScreen={isMobileScreen} chainId={chainId} />
|
<ProtocolDetails theme={theme} isMobileScreen={isMobileScreen} chainId={chainId} />
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
137
src/containers/TreasuryDashboard/components/ProtocolDetails.jsx
Normal file
137
src/containers/TreasuryDashboard/components/ProtocolDetails.jsx
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { useMemo } from "react";
|
||||||
|
import { Grid, Box, Typography } from "@mui/material";
|
||||||
|
import { useConfig } from "wagmi";
|
||||||
|
import { useNavigate, createSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
|
import { formatNumber } from "../../../helpers";
|
||||||
|
import { useEpoch, useGatekeeperApy } from "../../../hooks/staking";
|
||||||
|
import { useTokenSymbol, useBalance, useCirculatingSupply } from "../../../hooks/tokens";
|
||||||
|
import { SecondaryButton } from "../../../components/Button";
|
||||||
|
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
|
||||||
|
|
||||||
|
import { GATEKEEPER_ADDRESSES, EMPTY_ADDRESS } from "../../../constants/addresses";
|
||||||
|
|
||||||
|
const ProtocolDetail = ({ isMobileScreen, theme, name, sideName, url, urlParams, description }) => {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return (
|
||||||
|
<Box position="relative" width={`${isMobileScreen ? "100%" : "48%"}`}>
|
||||||
|
<Box
|
||||||
|
borderRadius="9px"
|
||||||
|
padding="12px"
|
||||||
|
sx={{ backgroundColor: theme.colors.paper.card }}
|
||||||
|
display="flex"
|
||||||
|
flexDirection="column"
|
||||||
|
justifyContent="space-between"
|
||||||
|
>
|
||||||
|
<Box display="flex" gap={"3px"} alignItems="center" justifyContent="space-between">
|
||||||
|
<Typography fontSize="20px" fontWeight="bold" lineHeight="33px">
|
||||||
|
{name}
|
||||||
|
</Typography>
|
||||||
|
<Typography color={theme.colors.primary[300]} fontSize="20px" fontWeight="bold" lineHeight="33px">
|
||||||
|
{sideName}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
<>
|
||||||
|
<Box my="18px">
|
||||||
|
<Typography color={theme.colors.gray[40]} mt="9px">
|
||||||
|
{description}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box display="flex" justifyContent="center" width="100%">
|
||||||
|
<SecondaryButton
|
||||||
|
onClick={() => urlParams
|
||||||
|
? navigate({
|
||||||
|
pathname: url,
|
||||||
|
search: urlParams.toString()
|
||||||
|
})
|
||||||
|
: navigate(url, { replace: true })
|
||||||
|
}
|
||||||
|
fullWidth
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</SecondaryButton>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProtocolDetails = ({ chainId, isMobileScreen, theme, }) => {
|
||||||
|
const config = useConfig();
|
||||||
|
const nativeSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
|
||||||
|
const networkName = config?.getClient()?.chain?.name;
|
||||||
|
|
||||||
|
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
|
||||||
|
const { symbol: csprSymbol } = useTokenSymbol(chainId, "CSPR");
|
||||||
|
const { contractAddress: ftsoAddress } = useBalance(chainId, "FTSO", EMPTY_ADDRESS);
|
||||||
|
|
||||||
|
const circulatingSupply = useCirculatingSupply(chainId, "STNK");
|
||||||
|
const gatekeepedApy = useGatekeeperApy(chainId);
|
||||||
|
const { epoch } = useEpoch(chainId);
|
||||||
|
|
||||||
|
const apyInner = useMemo(() => {
|
||||||
|
let apy = Infinity;
|
||||||
|
if (circulatingSupply._value > 0n) {
|
||||||
|
const value = epoch.distribute.div(circulatingSupply);
|
||||||
|
apy = 100 * (Math.pow(1 + parseFloat(value.toString()), 1095) - 1);
|
||||||
|
if (apy === 0) apy = Infinity;
|
||||||
|
}
|
||||||
|
return apy;
|
||||||
|
}, [circulatingSupply, epoch]);
|
||||||
|
|
||||||
|
const bridgeNumbers = useMemo(() => {
|
||||||
|
const connectedNetworks = Object.keys(GATEKEEPER_ADDRESSES).length;
|
||||||
|
const number = 1 + connectedNetworks * 3;
|
||||||
|
return `(${number}, ${number})`;
|
||||||
|
}, [chainId]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid container spacing={0} justifyContent={"center"}>
|
||||||
|
<Box display="flex" flexWrap="wrap" justifyContent="space-between" mt="10px" gap="25px">
|
||||||
|
<ProtocolDetail
|
||||||
|
isMobileScreen={isMobileScreen}
|
||||||
|
theme={theme}
|
||||||
|
url={`/${networkName.toLowerCase()}/dex/uniswap`}
|
||||||
|
urlParams={createSearchParams({
|
||||||
|
from: EMPTY_ADDRESS,
|
||||||
|
to: `${ftsoAddress}`,
|
||||||
|
})}
|
||||||
|
name="(3, 3) Swap"
|
||||||
|
sideName="Unlock Magic"
|
||||||
|
description={`Buying strategy expands bond capacity triggering a positive loop to strengthen the Treasury and attract more stakers. Swap ${nativeSymbol} for ${ftsoSymbol} to unlock (3, 3) Stake.`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProtocolDetail
|
||||||
|
isMobileScreen={isMobileScreen}
|
||||||
|
theme={theme}
|
||||||
|
url={`/${networkName.toLowerCase()}/bonds`}
|
||||||
|
name="(1, 1) Bond"
|
||||||
|
sideName="Up to 40% Discount"
|
||||||
|
description={`Bonding strategy grows Treasury and builds Protocol Owned Liquidity (POL) by allowing usersto acquire ${csprSymbol} at a discount. Take advantage of the next available bond.`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProtocolDetail
|
||||||
|
isMobileScreen={isMobileScreen}
|
||||||
|
theme={theme}
|
||||||
|
url={`/${networkName.toLowerCase()}/stake`}
|
||||||
|
name="(3, 3) Stake"
|
||||||
|
sideName={`${formatNumber(apyInner, 0)}% APY`}
|
||||||
|
description={`Staking enables (3, 3) coordination by aligning long-term incentives, sustainably backed (1, 1) Bonds, LP fees, and Farming. Stake ${ftsoSymbol} to earn rewards and unlock ${bridgeNumbers} Stake\u00B2.`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ProtocolDetail
|
||||||
|
isMobileScreen={isMobileScreen}
|
||||||
|
theme={theme}
|
||||||
|
url={`/${networkName.toLowerCase()}/bridge`}
|
||||||
|
name={`${bridgeNumbers} Stake\u00B2`}
|
||||||
|
sideName={`${formatNumber(apyInner * gatekeepedApy, 0)}% APY`}
|
||||||
|
description={`Staking\u00B2 strategy further deepens long-term incentives powered by sustainable cross-chain bridging revenue. ${bridgeNumbers} Stake\u00B2 your ${csprSymbol} to receive organic APY with no warmup and dillution.`}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Grid>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProtocolDetails;
|
||||||
@ -5,12 +5,52 @@ import toast from "react-hot-toast";
|
|||||||
import { config } from "../../config";
|
import { config } from "../../config";
|
||||||
|
|
||||||
import { isNetworkLegacyType } from "../../constants";
|
import { isNetworkLegacyType } from "../../constants";
|
||||||
import { STAKING_ADDRESSES } from "../../constants/addresses";
|
import { STAKING_ADDRESSES, GATEKEEPER_ADDRESSES } from "../../constants/addresses";
|
||||||
import { abi as StakingAbi } from "../../abi/GhostStaking.json";
|
import { abi as StakingAbi } from "../../abi/GhostStaking.json";
|
||||||
|
import { abi as GatekeeperAbi } from "../../abi/GhostGatekeeper.json";
|
||||||
|
|
||||||
import { shorten } from "../../helpers";
|
import { shorten } from "../../helpers";
|
||||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||||
|
|
||||||
|
export const useGatekeeperApy = (chainId) => {
|
||||||
|
const { data: gatekeeper, refetch } = useReadContract({
|
||||||
|
abi: StakingAbi,
|
||||||
|
address: STAKING_ADDRESSES[chainId],
|
||||||
|
functionName: "gatekeeper",
|
||||||
|
scopeKey: `gatekeeper-${chainId}`,
|
||||||
|
chainId: chainId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { data: metadata, error } = useReadContract({
|
||||||
|
abi: GatekeeperAbi,
|
||||||
|
address: GATEKEEPER_ADDRESSES[chainId],
|
||||||
|
functionName: "gatekeeperMetadata",
|
||||||
|
scopeKey: `gatekeeperMetadata-${chainId}`,
|
||||||
|
chainId: chainId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const amountIn = new DecimalBigNumber(metadata?.amountIn ?? 0n, 18);
|
||||||
|
const amountOut = new DecimalBigNumber(metadata?.amountOut ?? 0n, 18);
|
||||||
|
|
||||||
|
const deployedAt = metadata?.deployedAt ?? 0;
|
||||||
|
const unixSeconds = Math.floor(Date.now() / 1000);
|
||||||
|
const power = 365 * 86400 / (unixSeconds - deployedAt);
|
||||||
|
|
||||||
|
const feeIn = new DecimalBigNumber(6900n, 2);
|
||||||
|
const feeOut = new DecimalBigNumber(6900n, 2);
|
||||||
|
const stakeRatio = new DecimalBigNumber(69000n, 3);
|
||||||
|
|
||||||
|
const numerator = amountIn.mul(feeIn).add(amountOut.mul(feeOut));
|
||||||
|
const denominator = amountIn.mul(stakeRatio).sub(amountOut.mul(stakeRatio));
|
||||||
|
|
||||||
|
if (denominator?.toString() === "0") {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = Number(numerator.div(denominator).toString());
|
||||||
|
return Math.pow(1 + result, power);
|
||||||
|
}
|
||||||
|
|
||||||
export const useCurrentIndex = (chainId) => {
|
export const useCurrentIndex = (chainId) => {
|
||||||
const { data: index, refetch } = useReadContract({
|
const { data: index, refetch } = useReadContract({
|
||||||
abi: StakingAbi,
|
abi: StakingAbi,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user