Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MYC/esMYC Staking to Earn page #236

Open
wants to merge 17 commits into
base: trs
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/Addresses.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ const CONTRACTS = {
NATIVE_TOKEN: "0x08466D6683d2A39E3597500c1F17b792555FCAB9",
MLP: "0x25C7873FC2B2FB0A5A5D6346ee8940dCCd6A88B8",
TCR: "0x428F127d29f45dbB05B99a5d3adeA8171A3Ec390",
MYC: "0x428F127d29f45dbB05B99a5d3adeA8171A3Ec390",
MYC: "0x46873E80daf930265B7E5419BBC266cC2880ff8c",
MYC_V2: "0x46873E80daf930265B7E5419BBC266cC2880ff8c", // used for testnet staking
ES_MYC: "0xe2b9C2F3c8BD2b6Bc1E75b5650133be26Ca324Af",
ES_MYC_V2: "0x4897Dca24BcB50014456bcBBc59A2D6530FadCeB", // used for testnet staking
BN_MYC: "0x6cb7A4b0360Cf1B54eE9140F8Da45726671E7fdb",
USDG: "0x52d1A1d053CF2132C9bF9989aaaaF7921f9011BE",

Expand Down Expand Up @@ -60,6 +62,8 @@ const CONTRACTS = {
FeeDistributorReader: "0x55ce0A81b697f1eff846e8a56D3f303A5BE490b1",
MerkleDistributor: "0xEC503757C71440A7c82B6BB2d689bC4d191bC75d",
MerkleDistributorReader: "0xbB097d322D793ecd03721538d906d0C450d3839C",

MYCStakingRewards: "0x32Ff08751299a13A4a47Aff8841f1cC6eE287b9F", // v2 staking
},
42161: {
// arbitrum mainnet
Expand Down Expand Up @@ -114,7 +118,7 @@ const CONTRACTS = {
MerkleDistributorReader: "0xbB097d322D793ecd03721538d906d0C450d3839C",

LentMYC: "0x9B225FF56C48671d4D04786De068Ed8b88b672d6",
MYCStakingRewards: "0xF9B003Ee160dA9677115Ad3c5bd6BB6dADcB2F93" // v2 staking
MYCStakingRewards: "0xF9B003Ee160dA9677115Ad3c5bd6BB6dADcB2F93", // v2 staking
},
};

Expand Down
106 changes: 85 additions & 21 deletions src/Api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
USD_DECIMALS,
ETH_DECIMALS,
ARBITRUM_GOERLI,
SECONDS_PER_YEAR
SECONDS_PER_YEAR,
} from "../Helpers";
import { getTokenBySymbol } from "../data/Tokens";

Expand Down Expand Up @@ -125,16 +125,22 @@ export function useSpreadCaptureVolume(chainId) {
const [res, setRes] = useState(undefined);

useEffect(() => {
getMycGraphClient(chainId).query({ query }).then((res) => {
const totalMMFees = res.data.volumeStats.reduce((sum, stat) => sum
.add(MM_FEE_MULTIPLIER.mul(stat.mint))
.add(MM_FEE_MULTIPLIER.mul(stat.burn))
.add(MM_FEE_MULTIPLIER.mul(stat.margin))
.add(MM_FEE_MULTIPLIER.mul(stat.liquidation))
.add(MM_SWAPS_FEE_MULTIPLIER.mul(stat.swap))
, bigNumberify(0));
setRes(totalMMFees.div(expandDecimals(1, FEE_MULTIPLIER_BASIS_POINTS)))
}).catch(console.warn);
getMycGraphClient(chainId)
.query({ query })
.then((res) => {
const totalMMFees = res.data.volumeStats.reduce(
(sum, stat) =>
sum
.add(MM_FEE_MULTIPLIER.mul(stat.mint))
.add(MM_FEE_MULTIPLIER.mul(stat.burn))
.add(MM_FEE_MULTIPLIER.mul(stat.margin))
.add(MM_FEE_MULTIPLIER.mul(stat.liquidation))
.add(MM_SWAPS_FEE_MULTIPLIER.mul(stat.swap)),
bigNumberify(0)
);
setRes(totalMMFees.div(expandDecimals(1, FEE_MULTIPLIER_BASIS_POINTS)));
})
.catch(console.warn);
rogue-hacker marked this conversation as resolved.
Show resolved Hide resolved
}, [setRes, query, chainId]);

return res;
Expand Down Expand Up @@ -1065,31 +1071,45 @@ export function useStakingApr(mycPrice, ethPrice) {

// apr is annualised rewards (USD value) / total staked (USD value) * 100
const { data: tokensPerInterval } = useSWR(
[`useStakingApr:tokensPerInterval:${ARBITRUM}`, ARBITRUM, getContract(ARBITRUM, "MYCStakingRewards"), "tokensPerInterval"],
[
`useStakingApr:tokensPerInterval:${ARBITRUM}`,
ARBITRUM,
getContract(ARBITRUM, "MYCStakingRewards"),
"tokensPerInterval",
],
{
fetcher: fetcher(undefined, RewardsTracker),
}
);

const mycTokenAddress = getContract(ARBITRUM, 'MYC');
const mycTokenAddress = getContract(ARBITRUM, "MYC");
const { data: mycDeposited } = useSWR(
[`useStakingApr:totalDepositSupply(MYC):${ARBITRUM}`, ARBITRUM, getContract(ARBITRUM, "MYCStakingRewards"), "totalDepositSupply"],
[
`useStakingApr:totalDepositSupply(MYC):${ARBITRUM}`,
ARBITRUM,
getContract(ARBITRUM, "MYCStakingRewards"),
"totalDepositSupply",
],
{
fetcher: fetcher(undefined, RewardsTracker, mycTokenAddress)
fetcher: fetcher(undefined, RewardsTracker, mycTokenAddress),
}
);

const esMycTokenAddress = getContract(ARBITRUM, 'ES_MYC');
const esMycTokenAddress = getContract(ARBITRUM, "ES_MYC");
const { data: esMycDeposited } = useSWR(
[`useStakingApr:totalDepositSupply(esMYC):${ARBITRUM}`, ARBITRUM, getContract(ARBITRUM, "MYCStakingRewards"), "totalDepositSupply"],
[
`useStakingApr:totalDepositSupply(esMYC):${ARBITRUM}`,
ARBITRUM,
getContract(ARBITRUM, "MYCStakingRewards"),
"totalDepositSupply",
],
{
fetcher: fetcher(undefined, RewardsTracker, esMycTokenAddress)
fetcher: fetcher(undefined, RewardsTracker, esMycTokenAddress),
}
);

useEffect(() => {
if(ethPrice?.gt(0) && mycPrice?.gt(0) && tokensPerInterval && mycDeposited && esMycDeposited) {

if (ethPrice?.gt(0) && mycPrice?.gt(0) && tokensPerInterval && mycDeposited && esMycDeposited) {
const tokensPerYear = tokensPerInterval.mul(SECONDS_PER_YEAR);
const annualRewardsUsd = tokensPerYear.mul(ethPrice);

Expand All @@ -1099,14 +1119,58 @@ export function useStakingApr(mycPrice, ethPrice) {
const aprPrecision = 10;
const apr = annualRewardsUsd.mul(expandDecimals(1, aprPrecision)).div(totalDepositsUsd);

const formattedApr = apr.toNumber() / (10 ** aprPrecision) * 100;
const formattedApr = (apr.toNumber() / 10 ** aprPrecision) * 100;
setStakingApr(formattedApr.toFixed(2));
}
}, [ethPrice, mycPrice, tokensPerInterval, mycDeposited, esMycDeposited]);

return stakingApr;
}


export function useStakingValues(chainId) {
// const mycTokenAddress = getContract(chainId, "MYC");
// const esMycTokenAddress = getContract(chainId, "ES_MYC");
const stakingAddress = getContract(chainId, "MYCStakingRewards");

const { data: isPaused } = useSWR(
[`useStakingValues:inPrivateStakingMode:${chainId}`, chainId, stakingAddress, "inPrivateStakingMode"],
{
fetcher: fetcher(undefined, RewardsTracker),
}
);

const { data: totalStaked } = useSWR(
[`useStakingValues:totalSupply:${chainId}`, chainId, stakingAddress, "totalSupply"],
{
fetcher: fetcher(undefined, RewardsTracker),
}
);

const { data: depositCap } = useSWR(
[`useStakingValues:depositCap:${chainId}`, chainId, stakingAddress, "depositCap"],
{
fetcher: fetcher(undefined, RewardsTracker),
}
);

// const { data: totalMycDeposited } = useSWR(
// [`useStakingValues:totalDepositSupply(MYC):${chainId}`, chainId, stakingAddress, "totalDepositSupply"],
// {
// fetcher: fetcher(undefined, RewardsTracker, mycTokenAddress),
// }
// );

// const { data: totalEsMycDeposited } = useSWR(
// [`useStakingValues:totalDepositSupply(MYC):${chainId}`, chainId, stakingAddress, "totalDepositSupply"],
// {
// fetcher: fetcher(undefined, RewardsTracker, esMycTokenAddress),
// }
// );

return { isPaused, totalStaked, depositCap };
}

export function useTotalStaked() {
const [totalStakedMyc, setTotalStakedMyc] = useState(null);

Expand Down
7 changes: 7 additions & 0 deletions src/abis/RewardTracker.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/components/Navigation/Sidebar/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export default function Sidebar({
</BottomMenuItem>
<SocialLinksMenu>
{socialLinks.map((item) => (
<a href={item.path} target="_blank" rel="noopener noreferrer">
<a key={item.name} href={item.path} target="_blank" rel="noopener noreferrer">
<item.icon title={item.name} />
</a>
))}
Expand Down
70 changes: 70 additions & 0 deletions src/components/Stake/Sections.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { BigNumber } from "ethers/lib/ethers";
import { FC } from "react";
import * as StakeV2Styled from "src/views/Stake/StakeV2Styles";
import { TokenIcon } from "src/components/Stake/TokenIcon";
import { CompatibleToken, TokenIconSize, TokenIconSizeEnum } from "src/components/Stake/types";
import { convertStringToFloat } from "src/utils/common";
import { ETH_DECIMALS, expandDecimals, formatAmount, USD_DECIMALS } from "src/Helpers";
import { ZERO_BN } from "src/components/Stake/presets";

interface TokenAmountProps {
large?: boolean;
tokenAmount: BigNumber;
tokenUsdPrice: BigNumber;
selectedToken: CompatibleToken;
tokenIconSize: TokenIconSize;
decimals?: number;
}

export const TokenAmount: FC<TokenAmountProps> = ({
large,
tokenAmount,
tokenUsdPrice,
selectedToken,
tokenIconSize = TokenIconSizeEnum.lg,
decimals = 2,
}) => (
<StakeV2Styled.FlexColEnd>
<TokenAmountRow
large={large}
tokenAmount={formatAmount(tokenAmount || ZERO_BN, ETH_DECIMALS, decimals, false)}
selectedToken={selectedToken}
tokenIconSize={tokenIconSize}
decimals={decimals}
/>
<StakeV2Styled.Subtitle>
$
{formatAmount(
tokenAmount && tokenUsdPrice ? tokenUsdPrice.mul(tokenAmount).div(expandDecimals(1, USD_DECIMALS)) : ZERO_BN,
ETH_DECIMALS,
2,
true
)}
</StakeV2Styled.Subtitle>
</StakeV2Styled.FlexColEnd>
);

interface TokenAmountRowProps {
large?: boolean;
tokenAmount: string;
selectedToken: CompatibleToken;
tokenIconSize: TokenIconSize;
decimals?: number;
}

export const TokenAmountRow: FC<TokenAmountRowProps> = ({
large,
tokenAmount,
selectedToken,
tokenIconSize,
decimals = 2,
}) => (
<>
{/* @ts-ignore-next-line */}
<StakeV2Styled.AmountRow large={large}>
<span>{convertStringToFloat(tokenAmount.toString(), decimals)}</span>
<TokenIcon token={selectedToken} size={tokenIconSize as TokenIconSize} />
<span>{selectedToken}</span>
</StakeV2Styled.AmountRow>
</>
);
13 changes: 13 additions & 0 deletions src/components/Stake/TokenIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import styled from "styled-components";
import { CompatibleToken, TokenIconSize, TokenIconSizeEnum } from "./types";
import { tokenAltText, tokenIcon } from "src/components/Stake/presets";

export const TokenIcon: React.FC<{ token: CompatibleToken; size: TokenIconSize }> = ({ token, size }) => (
<Token src={tokenIcon[token]} alt={tokenAltText[token]} size={size} />
);

const Token = styled.img<{ size: TokenIconSize }>`
margin-left: 8px;
margin-right: 4px;
width: ${({ size }) => (size === TokenIconSizeEnum.sm ? "16px" : "20px")};
`;
21 changes: 21 additions & 0 deletions src/components/Stake/presets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ethers } from 'ethers'
import { CompatibleToken } from 'src/components/Stake/types'
import mycTokenIcon from "../../img/earn/myc.svg"
import esMycTokenIcon from "../../img/earn/esmyc.svg"
import ethTokenIcon from "../../img/earn/eth.svg"

export const tokenIcon: Record<CompatibleToken, any> = {
MYC: mycTokenIcon,
esMYC: esMycTokenIcon,
WETH: ethTokenIcon,
ETH: ethTokenIcon,
}

export const tokenAltText: Record<CompatibleToken, any> = {
MYC: 'Mycelium token logo',
esMYC: 'Escrowed Mycelium token logo',
WETH: 'Wrapped Ether token logo',
ETH: 'Ether token logo',
}

export const ZERO_BN = ethers.BigNumber.from(0)
15 changes: 15 additions & 0 deletions src/components/Stake/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export type CompatibleToken = 'MYC' | 'esMYC' | 'WETH' | 'ETH'

export enum CompatibleTokenEnum {
MYC = 'MYC',
esMYC = 'esMYC',
WETH = 'WETH',
ETH = 'ETH',
}

export type TokenIconSize = "sm" | "lg";

export enum TokenIconSizeEnum {
sm = "sm",
lg = "lg",
}
Loading