Compare commits
3 Commits
787b7e6322
...
96d7465436
| Author | SHA1 | Date | |
|---|---|---|---|
| 96d7465436 | |||
| f68551043e | |||
| 5faa7b0ba9 |
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "ghost-dao-interface",
|
"name": "ghost-dao-interface",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.7.40",
|
"version": "0.7.43",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
30
src/constants/gatekeeper.js
Normal file
30
src/constants/gatekeeper.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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;
|
||||||
@ -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, useCirculatingSupply } from "../../hooks/tokens";
|
import { useTokenSymbol } 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,8 +252,7 @@ const WelcomeView = ({
|
|||||||
|
|
||||||
const { epoch } = useEpoch(chainId);
|
const { epoch } = useEpoch(chainId);
|
||||||
const { gatekeeperAddress } = useGatekeeperAddress(chainId);
|
const { gatekeeperAddress } = useGatekeeperAddress(chainId);
|
||||||
const circulatingSupply = useCirculatingSupply(chainId, "STNK");
|
const { gatekeepedApy, apyInner } = useGatekeeperApy(chainId);
|
||||||
const gatekeepedApy = useGatekeeperApy(chainId);
|
|
||||||
|
|
||||||
const { isExtensionMissing } = useUnstableProvider();
|
const { isExtensionMissing } = useUnstableProvider();
|
||||||
|
|
||||||
@ -262,16 +261,6 @@ 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()();
|
||||||
@ -314,7 +303,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(apyInner * gatekeepedApy, 2)}% APY</Typography>
|
<Typography variant="h5">{formatNumber(gatekeepedApy, 2)}% APY</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<PrimaryButton
|
<PrimaryButton
|
||||||
|
|||||||
@ -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 { useEpoch, useGatekeeperApy } from "../../../hooks/staking";
|
import { useGatekeeperApy } from "../../../hooks/staking";
|
||||||
import { useTokenSymbol, useBalance, useCirculatingSupply } from "../../../hooks/tokens";
|
import { useTokenSymbol, useBalance } from "../../../hooks/tokens";
|
||||||
import { SecondaryButton } from "../../../components/Button";
|
import { SecondaryButton } from "../../../components/Button";
|
||||||
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
|
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
|
||||||
|
|
||||||
@ -68,10 +68,7 @@ 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)
|
||||||
@ -83,16 +80,6 @@ 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;
|
||||||
@ -138,7 +125,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(apyInner * gatekeepedApy, 0)}% APY`}
|
sideName={`${formatNumber(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>
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
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";
|
||||||
@ -8,44 +15,60 @@ 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 { data: gatekeeper, refetch } = useReadContract({
|
const circulatingSupply = useCirculatingSupply(chainId, "STNK");
|
||||||
abi: StakingAbi,
|
const { gatekeeperAddress } = useGatekeeperAddress(chainId);
|
||||||
address: STAKING_ADDRESSES[chainId],
|
const { epoch } = useEpoch(chainId);
|
||||||
functionName: "gatekeeper",
|
|
||||||
scopeKey: `gatekeeper-${chainId}`,
|
|
||||||
chainId: chainId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { data: metadata, error } = useReadContract({
|
const { data: metadata, error } = useReadContract({
|
||||||
abi: GatekeeperAbi,
|
abi: GatekeeperAbi,
|
||||||
address: gatekeeper,
|
address: gatekeeperAddress,
|
||||||
functionName: "metadata",
|
functionName: "metadata",
|
||||||
scopeKey: `gatekeeperMetadata-${chainId}-${gatekeeper}`,
|
scopeKey: `gatekeeperMetadata-${chainId}-${gatekeeperAddress}`,
|
||||||
chainId: chainId,
|
chainId: chainId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const amountIn = new DecimalBigNumber(metadata?.amountIn ?? 0n, 18);
|
const amountIn = new DecimalBigNumber(
|
||||||
const amountOut = new DecimalBigNumber(metadata?.amountOut ?? 0n, 18);
|
(metadata?.amountIn ?? 0n) + (amountInHistory[chainId] ?? 0n),
|
||||||
|
18,
|
||||||
|
);
|
||||||
|
const amountOut = new DecimalBigNumber(
|
||||||
|
(metadata?.amountOut ?? 0n) + (amountOutHistory[chainId] ?? 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 feeIn = new DecimalBigNumber(6900n, 2);
|
const feeInBig = new DecimalBigNumber((feeIn[chainId] ?? 0n), 5);
|
||||||
const feeOut = new DecimalBigNumber(6900n, 2);
|
const feeOutBig = new DecimalBigNumber((feeOut[chainId] ?? 0n), 5);
|
||||||
const stakeRatio = new DecimalBigNumber(69000n, 3);
|
const stakeRatioBig = new DecimalBigNumber((stakeRatio ?? 0n), 5);
|
||||||
|
|
||||||
const numerator = amountIn.mul(feeIn).add(amountOut.mul(feeOut));
|
const numerator = amountIn.mul(feeInBig).add(amountOut.mul(feeOutBig));
|
||||||
const denominator = amountIn.mul(stakeRatio).sub(amountOut.mul(stakeRatio));
|
const denominator = amountIn.mul(stakeRatioBig).sub(amountOut.mul(stakeRatioBig));
|
||||||
|
|
||||||
if (denominator?.toString() === "0") {
|
let apyInner = Infinity;
|
||||||
return 1;
|
if ((circulatingSupply?._value ?? 0n) === 0n) {
|
||||||
|
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());
|
||||||
return Math.pow(1 + result, power);
|
const gatekeepedApy = Math.pow(1 + result, power) * 100;
|
||||||
|
|
||||||
|
return {
|
||||||
|
gatekeepedApy: apyInner + gatekeepedApy + (apyInner * gatekeepedApy) / 100,
|
||||||
|
apyInner,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useCurrentIndex = (chainId) => {
|
export const useCurrentIndex = (chainId) => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user