Compare commits

..

No commits in common. "96d7465436eecef2f8a5eebb9aefb6c77a89b92b" and "787b7e632254eb326e53ced6ef82b23bfd982964" have entirely different histories.

5 changed files with 55 additions and 84 deletions

View File

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

View File

@ -1,30 +0,0 @@
import { NetworkId } from "../constants";
export const amountInHistory = {
[NetworkId.TESTNET_SEPOLIA]: 0n,
[NetworkId.TESTNET_HOODI]: 0n,
[NetworkId.TESTNET_MORDOR]: 0n,
}
export const amountOutHistory = {
[NetworkId.TESTNET_SEPOLIA]: 0n,
[NetworkId.TESTNET_HOODI]: 0n,
[NetworkId.TESTNET_MORDOR]: 0n,
}
// denominator is 1e5
export const feeIn = {
[NetworkId.TESTNET_SEPOLIA]: 6900n,
[NetworkId.TESTNET_HOODI]: 6900n,
[NetworkId.TESTNET_MORDOR]: 6900n,
}
// denominator is 1e5
export const feeOut = {
[NetworkId.TESTNET_SEPOLIA]: 0n,
[NetworkId.TESTNET_HOODI]: 0n,
[NetworkId.TESTNET_MORDOR]: 0n,
}
// denominator is 1e5
export const stakeRatio = 5000n;

View File

@ -20,7 +20,7 @@ import { GHOST_CONNECT } from "../../constants/ecosystem";
import { useLocalStorage } from "../../hooks/localstorage"; import { useLocalStorage } from "../../hooks/localstorage";
import { useBreakoutModal } from "../../hooks/breakoutModal"; import { useBreakoutModal } from "../../hooks/breakoutModal";
import { useTokenSymbol } from "../../hooks/tokens"; import { useTokenSymbol, useCirculatingSupply } from "../../hooks/tokens";
import { useEpoch, useGatekeeperApy, useGatekeeperAddress } from "../../hooks/staking"; import { useEpoch, useGatekeeperApy, useGatekeeperAddress } from "../../hooks/staking";
import { useEvmNetwork, useCurrentIndex, useUnstableProvider } from "../../hooks/ghost"; import { useEvmNetwork, useCurrentIndex, useUnstableProvider } from "../../hooks/ghost";
import { formatNumber, shorten } from "../../helpers"; import { formatNumber, shorten } from "../../helpers";
@ -252,7 +252,8 @@ const WelcomeView = ({
const { epoch } = useEpoch(chainId); const { epoch } = useEpoch(chainId);
const { gatekeeperAddress } = useGatekeeperAddress(chainId); const { gatekeeperAddress } = useGatekeeperAddress(chainId);
const { gatekeepedApy, apyInner } = useGatekeeperApy(chainId); const circulatingSupply = useCirculatingSupply(chainId, "STNK");
const gatekeepedApy = useGatekeeperApy(chainId);
const { isExtensionMissing } = useUnstableProvider(); const { isExtensionMissing } = useUnstableProvider();
@ -261,6 +262,16 @@ const WelcomeView = ({
closeModal(); closeModal();
} }
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 callDefaultFunction = useCallback(async () => { const callDefaultFunction = useCallback(async () => {
setIsPending(true); setIsPending(true);
await defaultFunction()(); await defaultFunction()();
@ -303,7 +314,7 @@ const WelcomeView = ({
</Box> </Box>
<Box display="flex" justifyContent="center" flexDirection="column" alignItems="center"> <Box display="flex" justifyContent="center" flexDirection="column" alignItems="center">
<Typography variant="h5">{formatNumber(gatekeepedApy, 2)}% APY</Typography> <Typography variant="h5">{formatNumber(apyInner * gatekeepedApy, 2)}% APY</Typography>
</Box> </Box>
<PrimaryButton <PrimaryButton

View File

@ -5,8 +5,8 @@ import { useNavigate, createSearchParams } from "react-router-dom";
import { formatNumber } from "../../../helpers"; import { formatNumber } from "../../../helpers";
import { useLiveBonds } from "../../../hooks/bonds/index"; import { useLiveBonds } from "../../../hooks/bonds/index";
import { useGatekeeperApy } from "../../../hooks/staking"; import { useEpoch, useGatekeeperApy } from "../../../hooks/staking";
import { useTokenSymbol, useBalance } from "../../../hooks/tokens"; import { useTokenSymbol, useBalance, useCirculatingSupply } from "../../../hooks/tokens";
import { SecondaryButton } from "../../../components/Button"; import { SecondaryButton } from "../../../components/Button";
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
@ -68,7 +68,10 @@ const ProtocolDetails = ({ chainId, isMobileScreen, theme, }) => {
const { symbol: csprSymbol } = useTokenSymbol(chainId, "CSPR"); const { symbol: csprSymbol } = useTokenSymbol(chainId, "CSPR");
const { contractAddress: ftsoAddress } = useBalance(chainId, "FTSO", EMPTY_ADDRESS); const { contractAddress: ftsoAddress } = useBalance(chainId, "FTSO", EMPTY_ADDRESS);
const { liveBonds } = useLiveBonds(chainId); const { liveBonds } = useLiveBonds(chainId);
const { gatekeepedApy, apyInner } = useGatekeeperApy(chainId);
const circulatingSupply = useCirculatingSupply(chainId, "STNK");
const gatekeepedApy = useGatekeeperApy(chainId);
const { epoch } = useEpoch(chainId);
const maxBondDiscountTest = useMemo(() => { const maxBondDiscountTest = useMemo(() => {
const sortedGhostBonds = liveBonds.filter((bond) => !bond.isSoldOut) const sortedGhostBonds = liveBonds.filter((bond) => !bond.isSoldOut)
@ -80,6 +83,16 @@ const ProtocolDetails = ({ chainId, isMobileScreen, theme, }) => {
return `Up to ${formatNumber(maxDiscount, 0)}% Discount`; return `Up to ${formatNumber(maxDiscount, 0)}% Discount`;
}, [liveBonds]); }, [liveBonds]);
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 bridgeNumbers = useMemo(() => {
const connectedNetworks = Object.keys(GATEKEEPER_ADDRESSES).length; const connectedNetworks = Object.keys(GATEKEEPER_ADDRESSES).length;
const number = 1 + connectedNetworks * 3; const number = 1 + connectedNetworks * 3;
@ -125,7 +138,7 @@ const ProtocolDetails = ({ chainId, isMobileScreen, theme, }) => {
theme={theme} theme={theme}
url={`/${networkName.toLowerCase()}/bridge`} url={`/${networkName.toLowerCase()}/bridge`}
name={`${bridgeNumbers} Stake\u00B2`} name={`${bridgeNumbers} Stake\u00B2`}
sideName={`${formatNumber(gatekeepedApy, 0)}% APY`} sideName={`${formatNumber(apyInner * gatekeepedApy, 0)}% APY`}
description={`Staking\u00B2 strategy further deepens long-term incentives powered by sustainable crosschain bridging revenue. ${bridgeNumbers} Stake\u00B2 your ${csprSymbol} to receive organic APY with no warm-up and no dilution.`} description={`Staking\u00B2 strategy further deepens long-term incentives powered by sustainable crosschain bridging revenue. ${bridgeNumbers} Stake\u00B2 your ${csprSymbol} to receive organic APY with no warm-up and no dilution.`}
/> />
</Box> </Box>

View File

@ -1,13 +1,6 @@
import { useReadContract } from "wagmi"; import { useReadContract } from "wagmi";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import {
amountInHistory,
amountOutHistory,
feeIn,
feeOut,
stakeRatio
} from "../../constants/gatekeeper";
import { STAKING_ADDRESSES } from "../../constants/addresses"; import { STAKING_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 { abi as GatekeeperAbi } from "../../abi/GhostGatekeeper.json";
@ -15,60 +8,44 @@ 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";
import { executeOnChainTransaction } from "../helpers"; import { executeOnChainTransaction } from "../helpers";
import { useCirculatingSupply } from "../tokens";
export const useGatekeeperApy = (chainId) => { export const useGatekeeperApy = (chainId) => {
const circulatingSupply = useCirculatingSupply(chainId, "STNK"); const { data: gatekeeper, refetch } = useReadContract({
const { gatekeeperAddress } = useGatekeeperAddress(chainId); abi: StakingAbi,
const { epoch } = useEpoch(chainId); address: STAKING_ADDRESSES[chainId],
functionName: "gatekeeper",
const { data: metadata, error } = useReadContract({ scopeKey: `gatekeeper-${chainId}`,
abi: GatekeeperAbi,
address: gatekeeperAddress,
functionName: "metadata",
scopeKey: `gatekeeperMetadata-${chainId}-${gatekeeperAddress}`,
chainId: chainId, chainId: chainId,
}); });
const amountIn = new DecimalBigNumber( const { data: metadata, error } = useReadContract({
(metadata?.amountIn ?? 0n) + (amountInHistory[chainId] ?? 0n), abi: GatekeeperAbi,
18, address: gatekeeper,
); functionName: "metadata",
const amountOut = new DecimalBigNumber( scopeKey: `gatekeeperMetadata-${chainId}-${gatekeeper}`,
(metadata?.amountOut ?? 0n) + (amountOutHistory[chainId] ?? 0n), chainId: chainId,
18, });
);
const amountIn = new DecimalBigNumber(metadata?.amountIn ?? 0n, 18);
const amountOut = new DecimalBigNumber(metadata?.amountOut ?? 0n, 18);
const deployedAt = metadata?.deployedAt ?? 0; const deployedAt = metadata?.deployedAt ?? 0;
const unixSeconds = Math.floor(Date.now() / 1000); const unixSeconds = Math.floor(Date.now() / 1000);
const power = 365 * 86400 / (unixSeconds - deployedAt); const power = 365 * 86400 / (unixSeconds - deployedAt);
const feeInBig = new DecimalBigNumber((feeIn[chainId] ?? 0n), 5); const feeIn = new DecimalBigNumber(6900n, 2);
const feeOutBig = new DecimalBigNumber((feeOut[chainId] ?? 0n), 5); const feeOut = new DecimalBigNumber(6900n, 2);
const stakeRatioBig = new DecimalBigNumber((stakeRatio ?? 0n), 5); const stakeRatio = new DecimalBigNumber(69000n, 3);
const numerator = amountIn.mul(feeInBig).add(amountOut.mul(feeOutBig)); const numerator = amountIn.mul(feeIn).add(amountOut.mul(feeOut));
const denominator = amountIn.mul(stakeRatioBig).sub(amountOut.mul(stakeRatioBig)); const denominator = amountIn.mul(stakeRatio).sub(amountOut.mul(stakeRatio));
let apyInner = Infinity; if (denominator?.toString() === "0") {
if ((circulatingSupply?._value ?? 0n) === 0n) { return 1;
return { gatekeepedApy: apyInner, apyInner};
} }
const value = epoch.distribute.div(circulatingSupply);
apyInner = 100 * (Math.pow(1 + parseFloat(value.toString()), 1095) - 1);
if (apyInner === 0) apyInner = Infinity;
if (!denominator?._value || denominator._value.isZero()) {
return { gatekeepedApy: apyInner, apyInner };
}
const result = Number(numerator.div(denominator).toString()); const result = Number(numerator.div(denominator).toString());
const gatekeepedApy = Math.pow(1 + result, power) * 100; return Math.pow(1 + result, power);
return {
gatekeepedApy: apyInner + gatekeepedApy + (apyInner * gatekeepedApy) / 100,
apyInner,
}
} }
export const useCurrentIndex = (chainId) => { export const useCurrentIndex = (chainId) => {