diff --git a/package.json b/package.json index 2f0f035892..0ba52cf21d 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "react-dom": "18.2.0", "react-helmet": "6.1.0", "react-hot-toast": "2.2.0", - "react-icons": "4.3.1", + "react-icons": "5.5.0", "react-jazzicon": "1.0.4", "react-loading-skeleton": "3.3.1", "react-remove-scroll": "2.5.5", diff --git a/src/App/App.scss b/src/App/App.scss index 97b8c043e7..5a3b863da1 100644 --- a/src/App/App.scss +++ b/src/App/App.scss @@ -17,23 +17,11 @@ $app-card-padding: 1.5rem; display: flex; justify-content: space-between; position: relative; - padding-left: 3.2rem; - padding-right: 3.2rem; - background: transparent 0% 0% no-repeat padding-box; - border-bottom: 1px solid var(--color-slate-700); + // padding-left: 3.2rem; + // padding-right: 3.2rem; z-index: 1; } -.App-header.large::after { - height: 1.3rem; - width: 100%; - content: " "; - background: transparent linear-gradient(180deg, #00000030 0%, transparent 100%) 0% 0% no-repeat padding-box; - position: absolute; - bottom: -1.3rem; - left: 0; -} - .App-header.large .App-header-links { display: flex; } @@ -668,7 +656,6 @@ a.App-cta, button.App-cta { color: var(--color-white); text-decoration: none; - background: linear-gradient(90deg, rgba(45, 66, 252, 1) 0%, var(--color-blue-700) 100%); } .App-cta.small { @@ -739,7 +726,7 @@ button.App-button-option:disabled { .App-header-user { display: flex; align-items: center; - gap: 1.6rem; + gap: 0.5rem; } .Network-icon { @@ -1129,18 +1116,6 @@ button.App-button { position: relative; padding-left: 1.6rem; padding-right: 1.6rem; - background: transparent 0% 0% no-repeat padding-box; - border-bottom: 1px solid var(--color-slate-700); - } - - .App-header.small::after { - height: 1.3rem; - width: 100%; - content: " "; - background: transparent linear-gradient(180deg, #00000030 0%, transparent 100%) 0% 0% no-repeat padding-box; - position: absolute; - bottom: -1.3rem; - left: 0; } .Page-title-section { diff --git a/src/App/AppRoutes.tsx b/src/App/AppRoutes.tsx index 566c4e590a..cb21b5c656 100644 --- a/src/App/AppRoutes.tsx +++ b/src/App/AppRoutes.tsx @@ -141,7 +141,7 @@ export function AppRoutes() { return ( <> -
+
void }) { - - - - - {/* redirect from previous stake(earn) url */} - - - - - - diff --git a/src/components/AddressDropdown/AddressDropdown.tsx b/src/components/AddressDropdown/AddressDropdown.tsx index 3bd399abf4..97753b7caa 100644 --- a/src/components/AddressDropdown/AddressDropdown.tsx +++ b/src/components/AddressDropdown/AddressDropdown.tsx @@ -47,7 +47,7 @@ function AddressDropdown({ account, accountUrl, disconnectAccountAndCloseSetting return ( - -
- -
- ); -} diff --git a/src/pages/Stake/AffiliateVesterWithdrawModal.tsx b/src/pages/Stake/AffiliateVesterWithdrawModal.tsx deleted file mode 100644 index e8f1057051..0000000000 --- a/src/pages/Stake/AffiliateVesterWithdrawModal.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import { ethers } from "ethers"; -import { useCallback, useMemo, useState } from "react"; - -import { getContract } from "config/contracts"; -import { callContract } from "lib/contracts"; -import { abis } from "sdk/abis"; - -import Button from "components/Button/Button"; -import Modal from "components/Modal/Modal"; - -export function AffiliateVesterWithdrawModal(props) { - const { isVisible, setIsVisible, chainId, signer, setPendingTxns } = props; - const [isWithdrawing, setIsWithdrawing] = useState(false); - - const affiliateVesterAddress = getContract(chainId, "AffiliateVester"); - - const primaryText = useMemo(() => (isWithdrawing ? t`Confirming...` : t`Confirm Withdraw`), [isWithdrawing]); - - const onClickPrimary = useCallback(() => { - setIsWithdrawing(true); - const contract = new ethers.Contract(affiliateVesterAddress, abis.Vester, signer); - - callContract(chainId, contract, "withdraw", [], { - sentMsg: t`Withdraw submitted.`, - failMsg: t`Withdraw failed.`, - successMsg: t`Withdrawn!`, - setPendingTxns, - }) - .then(() => { - setIsVisible(false); - }) - .finally(() => { - setIsWithdrawing(false); - }); - }, [chainId, affiliateVesterAddress, signer, setPendingTxns, setIsVisible]); - - return ( -
- - -
- This will withdraw all esGMX tokens as well as pause vesting. -
-
- esGMX tokens that have been converted to GMX will be claimed and remain as GMX tokens. -
-
- To claim GMX tokens without withdrawing, use the "Claim" button. -
-
-
-
-
- -
-
-
- ); -} diff --git a/src/pages/Stake/ClaimModal.tsx b/src/pages/Stake/ClaimModal.tsx deleted file mode 100644 index 5fa09809d6..0000000000 --- a/src/pages/Stake/ClaimModal.tsx +++ /dev/null @@ -1,280 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import cx from "classnames"; -import { ethers } from "ethers"; -import React, { useCallback, useMemo, useState } from "react"; - -import { ARBITRUM } from "config/chains"; -import { getContract } from "config/contracts"; -import { SetPendingTransactions } from "context/PendingTxnsContext/PendingTxnsContext"; -import { useGovTokenAmount } from "domain/synthetics/governance/useGovTokenAmount"; -import { useGovTokenDelegates } from "domain/synthetics/governance/useGovTokenDelegates"; -import { useTokensAllowanceData } from "domain/synthetics/tokens"; -import { approveTokens } from "domain/tokens"; -import { callContract } from "lib/contracts"; -import { useLocalStorageSerializeKey } from "lib/localStorage"; -import { formatAmount } from "lib/numbers"; -import { UncheckedJsonRpcSigner } from "lib/rpc/UncheckedJsonRpcSigner"; -import { abis } from "sdk/abis"; -import { NATIVE_TOKEN_ADDRESS } from "sdk/configs/tokens"; - -import { AlertInfo } from "components/AlertInfo/AlertInfo"; -import { ApproveTokenButton } from "components/ApproveTokenButton/ApproveTokenButton"; -import Button from "components/Button/Button"; -import Checkbox from "components/Checkbox/Checkbox"; -import ExternalLink from "components/ExternalLink/ExternalLink"; -import ModalWithPortal from "components/Modal/ModalWithPortal"; - -import { GMX_DAO_LINKS } from "./constants"; - -export function ClaimModal(props: { - isVisible: boolean; - setIsVisible: (isVisible: boolean) => void; - rewardRouterAddress: string; - signer: UncheckedJsonRpcSigner | undefined; - chainId: number; - setPendingTxns: SetPendingTransactions; - totalGmxRewards: bigint | undefined; - nativeTokenSymbol: string; - wrappedTokenSymbol: string; - isNativeTokenToClaim?: boolean; - gmxUsageOptionsMsg?: React.ReactNode; - onClaimSuccess?: () => void; -}) { - const { - isVisible, - setIsVisible, - rewardRouterAddress, - signer, - chainId, - setPendingTxns, - totalGmxRewards, - nativeTokenSymbol, - wrappedTokenSymbol, - isNativeTokenToClaim, - gmxUsageOptionsMsg, - onClaimSuccess, - } = props; - const [isClaiming, setIsClaiming] = useState(false); - const [shouldClaimGmx, setShouldClaimGmx] = useLocalStorageSerializeKey( - [chainId, "StakeV2-compound-should-claim-gmx"], - true - ); - const [shouldStakeGmx, setShouldStakeGmx] = useLocalStorageSerializeKey( - [chainId, "StakeV2-compound-should-stake-gmx"], - true - ); - const [shouldClaimEsGmx, setShouldClaimEsGmx] = useLocalStorageSerializeKey( - [chainId, "StakeV2-compound-should-claim-es-gmx"], - true - ); - const [shouldStakeEsGmx, setShouldStakeEsGmx] = useLocalStorageSerializeKey( - [chainId, "StakeV2-compound-should-stake-es-gmx"], - true - ); - const [shouldClaimWeth, setShouldClaimWeth] = useLocalStorageSerializeKey( - [chainId, "StakeV2-compound-should-claim-weth"], - true - ); - const [shouldConvertWeth, setShouldConvertWeth] = useLocalStorageSerializeKey( - [chainId, "StakeV2-compound-should-convert-weth"], - true - ); - - const govTokenAmount = useGovTokenAmount(chainId); - const govTokenDelegatesAddress = useGovTokenDelegates(chainId); - const isUndelegatedGovToken = - chainId === ARBITRUM && govTokenDelegatesAddress === NATIVE_TOKEN_ADDRESS && govTokenAmount && govTokenAmount > 0; - - const gmxAddress = getContract(chainId, "GMX"); - const stakedGmxTrackerAddress = getContract(chainId, "StakedGmxTracker"); - - const [isApproving, setIsApproving] = useState(false); - - const { tokensAllowanceData: gmxAllowanceData } = useTokensAllowanceData(chainId, { - spenderAddress: stakedGmxTrackerAddress, - tokenAddresses: [gmxAddress], - }); - - const gmxTokenAllowance = gmxAllowanceData?.[gmxAddress]; - - const needApproval = - shouldStakeGmx && - totalGmxRewards !== undefined && - ((gmxTokenAllowance !== undefined && totalGmxRewards > gmxTokenAllowance) || - (totalGmxRewards > 0n && gmxTokenAllowance === undefined)); - - const isPrimaryEnabled = !isClaiming && !isApproving && !needApproval && !isUndelegatedGovToken; - - const primaryText = useMemo(() => { - if (needApproval || isApproving) { - return t`Pending GMX approval`; - } - if (isClaiming) { - return t`Claiming...`; - } - return t`Claim`; - }, [needApproval, isApproving, isClaiming]); - - const onClickPrimary = useCallback(() => { - if (needApproval) { - approveTokens({ - setIsApproving, - signer, - tokenAddress: gmxAddress, - spender: stakedGmxTrackerAddress, - chainId, - permitParams: undefined, - approveAmount: undefined, - }); - return; - } - - setIsClaiming(true); - - const contract = new ethers.Contract(rewardRouterAddress, abis.RewardRouter, signer); - callContract( - chainId, - contract, - "handleRewards", - [ - shouldClaimGmx || shouldStakeGmx, - shouldStakeGmx, - shouldClaimEsGmx || shouldStakeEsGmx, - shouldStakeEsGmx, - true, - isNativeTokenToClaim ? shouldClaimWeth || shouldConvertWeth : false, - isNativeTokenToClaim ? shouldConvertWeth : false, - ], - { - sentMsg: t`Claim submitted!`, - failMsg: t`Claim failed.`, - successMsg: t`Claim completed!`, - successDetailsMsg: !shouldStakeGmx ? gmxUsageOptionsMsg : undefined, - setPendingTxns, - } - ) - .then(() => { - setIsVisible(false); - onClaimSuccess?.(); - }) - .finally(() => { - setIsClaiming(false); - }); - }, [ - needApproval, - signer, - gmxAddress, - stakedGmxTrackerAddress, - chainId, - rewardRouterAddress, - shouldClaimGmx, - shouldStakeGmx, - shouldClaimEsGmx, - shouldStakeEsGmx, - isNativeTokenToClaim, - shouldClaimWeth, - shouldConvertWeth, - gmxUsageOptionsMsg, - setPendingTxns, - setIsVisible, - onClaimSuccess, - ]); - - const toggleShouldStakeGmx = useCallback( - (value: boolean) => { - if (value) { - setShouldClaimGmx(true); - } - setShouldStakeGmx(value); - }, - [setShouldClaimGmx, setShouldStakeGmx] - ); - - const toggleShouldStakeEsGmx = useCallback( - (value: boolean) => { - if (value) { - setShouldClaimEsGmx(true); - } - setShouldStakeEsGmx(value); - }, - [setShouldClaimEsGmx, setShouldStakeEsGmx] - ); - - const toggleConvertWeth = useCallback( - (value: boolean) => { - if (value) { - setShouldClaimWeth(true); - } - setShouldConvertWeth(value); - }, - [setShouldClaimWeth, setShouldConvertWeth] - ); - - return ( - -
-
- - Claim GMX Rewards - -
-
- - Stake GMX Rewards - -
-
- - Claim esGMX Rewards - -
-
- - Stake esGMX Rewards - -
- {isNativeTokenToClaim && ( - <> -
- - Claim {wrappedTokenSymbol} Rewards - -
-
- - - Convert {wrappedTokenSymbol} to {nativeTokenSymbol} - - -
- - )} -
- {(needApproval || isApproving) && ( -
- -
- )} - {isUndelegatedGovToken ? ( - - - - Delegate your undelegated {formatAmount(govTokenAmount, 18, 2, true)} GMX DAO - - voting power before claiming. - - - ) : null} -
- -
-
- ); -} diff --git a/src/pages/Stake/EscrowedGmxCard.tsx b/src/pages/Stake/EscrowedGmxCard.tsx deleted file mode 100644 index a68be74cc4..0000000000 --- a/src/pages/Stake/EscrowedGmxCard.tsx +++ /dev/null @@ -1,170 +0,0 @@ -import { Trans } from "@lingui/macro"; -import { useConnectModal } from "@rainbow-me/rainbowkit"; -import { useMemo } from "react"; -import useSWR from "swr"; - -import { ARBITRUM, getConstant } from "config/chains"; -import { getContract } from "config/contracts"; -import { USD_DECIMALS } from "config/factors"; -import { getIcons } from "config/icons"; -import { useGmxPrice } from "domain/legacy"; -import { useChainId } from "lib/chains"; -import { contractFetcher } from "lib/contracts"; -import { ProcessedData } from "lib/legacy"; -import { expandDecimals, formatAmount } from "lib/numbers"; -import useWallet from "lib/wallets/useWallet"; -import { bigMath } from "sdk/utils/bigmath"; - -import { AmountWithUsdBalance, AmountWithUsdHuman } from "components/AmountWithUsd/AmountWithUsd"; -import Button from "components/Button/Button"; -import GMXAprTooltip from "components/Stake/GMXAprTooltip"; -import Tooltip from "components/Tooltip/Tooltip"; - -export function EscrowedGmxCard({ - processedData, - showStakeEsGmxModal, - showUnstakeEsGmxModal, -}: { - processedData: ProcessedData | undefined; - showStakeEsGmxModal: () => void; - showUnstakeEsGmxModal: () => void; -}) { - const { active, signer } = useWallet(); - const { chainId } = useChainId(); - const { openConnectModal } = useConnectModal(); - - const icons = getIcons(chainId); - - const readerAddress = getContract(chainId, "Reader"); - - const esGmxAddress = getContract(chainId, "ES_GMX"); - - const stakedGmxDistributorAddress = getContract(chainId, "StakedGmxDistributor"); - const stakedGlpDistributorAddress = getContract(chainId, "StakedGlpDistributor"); - - const excludedEsGmxAccounts = [stakedGmxDistributorAddress, stakedGlpDistributorAddress]; - - const nativeTokenSymbol = getConstant(chainId, "nativeTokenSymbol"); - - const { data: esGmxSupply } = useSWR( - [`StakeV2:esGmxSupply:${active}`, chainId, readerAddress, "getTokenSupply", esGmxAddress], - { - fetcher: contractFetcher(signer, "ReaderV2", [excludedEsGmxAccounts]), - } - ); - - const { gmxPrice } = useGmxPrice(chainId, { arbitrum: chainId === ARBITRUM ? signer : undefined }, active); - - let esGmxSupplyUsd; - if (esGmxSupply !== undefined && gmxPrice !== undefined) { - esGmxSupplyUsd = bigMath.mulDiv(esGmxSupply, gmxPrice, expandDecimals(1, 18)); - } - - const gmxAvgAprText = useMemo(() => { - return `${formatAmount(processedData?.gmxAprTotal, 2, 2, true)}%`; - }, [processedData?.gmxAprTotal]); - - return ( -
-
-
- GLP - - Escrowed GMX - -
-
-
-
-
-
- Price -
-
${formatAmount(gmxPrice, USD_DECIMALS, 2, true)}
-
-
-
- Wallet -
-
- -
-
-
-
- Staked -
-
- -
-
-
-
-
- APR -
-
- ( - - )} - /> -
-
-
-
-
- Total Staked -
-
- -
-
-
-
- Total Supply -
-
- -
-
-
-
-
- {active && ( - - )} - {active && ( - - )} - {!active && ( - - )} -
-
-
-
- ); -} diff --git a/src/pages/Stake/GlpCard.tsx b/src/pages/Stake/GlpCard.tsx deleted file mode 100644 index 9e61e9c5b4..0000000000 --- a/src/pages/Stake/GlpCard.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { Trans } from "@lingui/macro"; - -import { getConstant } from "config/chains"; -import { USD_DECIMALS } from "config/factors"; -import { getIcons } from "config/icons"; -import { GLP_PRICE_DECIMALS } from "config/ui"; -import { useChainId } from "lib/chains"; -import { GLP_DECIMALS, ProcessedData } from "lib/legacy"; -import { formatKeyAmount } from "lib/numbers"; - -import { AmountWithUsdBalance, AmountWithUsdHuman } from "components/AmountWithUsd/AmountWithUsd"; -import Button from "components/Button/Button"; -import StatsTooltipRow from "components/StatsTooltip/StatsTooltipRow"; -import Tooltip from "components/Tooltip/Tooltip"; - -export function GlpCard({ processedData }: { processedData: ProcessedData | undefined }) { - const { chainId } = useChainId(); - - const icons = getIcons(chainId); - - const nativeTokenSymbol = getConstant(chainId, "nativeTokenSymbol"); - const wrappedTokenSymbol = getConstant(chainId, "wrappedTokenSymbol"); - - return ( -
-
-
-
- GLP - GLP -
-
-
-
-
-
- Price -
-
${formatKeyAmount(processedData, "glpPrice", USD_DECIMALS, GLP_PRICE_DECIMALS, true)}
-
-
-
- Wallet -
-
- -
-
-
-
- Staked -
-
- -
-
-
-
-
- Rewards -
-
- - - } - showDollar={false} - /> - - } - showDollar={false} - /> - - } - /> -
-
-
-
-
- Total Staked -
-
- -
-
-
-
- Total Supply -
-
- -
-
- -
-
-
-
-
-
- -
-
-
- ); -} diff --git a/src/pages/Stake/GmxAndVotingPowerCard.tsx b/src/pages/Stake/GmxAndVotingPowerCard.tsx deleted file mode 100644 index 6662ba54e1..0000000000 --- a/src/pages/Stake/GmxAndVotingPowerCard.tsx +++ /dev/null @@ -1,340 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import cx from "classnames"; -import { useMemo } from "react"; - -import { ARBITRUM, AVALANCHE, getConstant } from "config/chains"; -import { USD_DECIMALS } from "config/factors"; -import { getIcons } from "config/icons"; -import { useGmxPrice, useTotalGmxStaked, useTotalGmxSupply } from "domain/legacy"; -import { useGovTokenAmount } from "domain/synthetics/governance/useGovTokenAmount"; -import { useGovTokenDelegates } from "domain/synthetics/governance/useGovTokenDelegates"; -import { useChainId } from "lib/chains"; -import { ProcessedData, useENS } from "lib/legacy"; -import { expandDecimals, formatAmount, formatBalanceAmount, formatKeyAmount } from "lib/numbers"; -import { shortenAddressOrEns } from "lib/wallets"; -import useWallet from "lib/wallets/useWallet"; -import { NATIVE_TOKEN_ADDRESS } from "sdk/configs/tokens"; -import { bigMath } from "sdk/utils/bigmath"; - -import { AlertInfo } from "components/AlertInfo/AlertInfo"; -import { AmountWithUsdBalance, AmountWithUsdHuman } from "components/AmountWithUsd/AmountWithUsd"; -import Button from "components/Button/Button"; -import ExternalLink from "components/ExternalLink/ExternalLink"; -import GMXAprTooltip from "components/Stake/GMXAprTooltip"; -import ChainsStatsTooltipRow from "components/StatsTooltip/ChainsStatsTooltipRow"; -import StatsTooltipRow from "components/StatsTooltip/StatsTooltipRow"; -import Tooltip from "components/Tooltip/Tooltip"; - -import { GMX_DAO_LINKS, getGmxDAODelegateLink } from "./constants"; - -export function GmxAndVotingPowerCard({ - processedData, - sbfGmxBalance, - setIsUnstakeModalVisible, - setUnstakeModalTitle, - setUnstakeModalMaxAmount, - setUnstakeValue, - setUnstakingTokenSymbol, - setUnstakeMethodName, - showStakeGmxModal, -}: { - processedData: ProcessedData | undefined; - sbfGmxBalance: bigint; - setIsUnstakeModalVisible: (visible: boolean) => void; - setUnstakeModalTitle: (title: string) => void; - setUnstakeModalMaxAmount: (amount: bigint) => void; - setUnstakeValue: (value: string) => void; - setUnstakingTokenSymbol: (symbol: string) => void; - setUnstakeMethodName: (methodName: string) => void; - showStakeGmxModal: () => void; -}) { - const { chainId } = useChainId(); - const { active, signer, account } = useWallet(); - const icons = getIcons(chainId); - - const govTokenAmount = useGovTokenAmount(chainId); - const govTokenDelegatesAddress = useGovTokenDelegates(chainId); - const { ensName: govTokenDelegatesEns } = useENS(govTokenDelegatesAddress); - const { gmxPrice, gmxPriceFromArbitrum, gmxPriceFromAvalanche } = useGmxPrice( - chainId, - { arbitrum: chainId === ARBITRUM ? signer : undefined }, - active - ); - const isAnyFeeGmxTrackerRewards = (processedData?.feeGmxTrackerRewardsUsd ?? 0n) > 10n ** BigInt(USD_DECIMALS) / 100n; - - const stakedGMXInfo = useTotalGmxStaked(); - - const { [AVALANCHE]: avaxGmxStaked, [ARBITRUM]: arbitrumGmxStaked, total: totalGmxStaked } = stakedGMXInfo; - - let { total: totalGmxSupply } = useTotalGmxSupply(); - - let stakedGmxSupplyUsd; - if (totalGmxStaked !== 0n && gmxPrice !== undefined) { - stakedGmxSupplyUsd = bigMath.mulDiv(totalGmxStaked, gmxPrice, expandDecimals(1, 18)); - } - - let totalSupplyUsd; - if (totalGmxSupply !== undefined && totalGmxSupply !== 0n && gmxPrice !== undefined) { - totalSupplyUsd = bigMath.mulDiv(totalGmxSupply, gmxPrice, expandDecimals(1, 18)); - } - - const showUnstakeGmxModal = () => { - setIsUnstakeModalVisible(true); - setUnstakeModalTitle(t`Unstake GMX`); - let maxAmount = processedData?.gmxInStakedGmx; - - if (maxAmount !== undefined) { - maxAmount = bigMath.min(maxAmount, sbfGmxBalance); - } - - setUnstakeModalMaxAmount(maxAmount!); - setUnstakeValue(""); - setUnstakingTokenSymbol("GMX"); - setUnstakeMethodName("unstakeGmx"); - }; - - const stakedEntries = useMemo( - () => ({ - "Staked on Arbitrum": arbitrumGmxStaked, - "Staked on Avalanche": avaxGmxStaked, - }), - [arbitrumGmxStaked, avaxGmxStaked] - ); - - const gmxAvgAprText = useMemo(() => { - return `${formatAmount(processedData?.gmxAprTotal, 2, 2, true)}%`; - }, [processedData?.gmxAprTotal]); - - const nativeTokenSymbol = getConstant(chainId, "nativeTokenSymbol"); - const wrappedTokenSymbol = getConstant(chainId, "wrappedTokenSymbol"); - - return ( -
-
-
- GMX - {t`GMX & Voting Power`} -
-
-
-
-
-
- Price -
-
- {gmxPrice === undefined ? ( - "..." - ) : ( - ( - <> - - - - )} - /> - )} -
-
-
-
- Wallet -
-
- -
-
-
-
- Staked -
-
- -
-
- {chainId === ARBITRUM && ( -
-
- Voting Power -
-
- {govTokenAmount !== undefined ? ( - - {govTokenDelegatesAddress === NATIVE_TOKEN_ADDRESS && govTokenAmount > 0 ? ( - - - - Delegate your undelegated {formatAmount(govTokenAmount, 18, 2, true)} GMX DAO - {" "} - voting power. - - - ) : null} - No delegate found - ) : ( - - {govTokenDelegatesAddress === account - ? t`Myself` - : govTokenDelegatesEns - ? shortenAddressOrEns(govTokenDelegatesEns, 25) - : shortenAddressOrEns(govTokenDelegatesAddress, 13)} - - ) - } - showDollar={false} - /> -
- Explore the list of delegates. - - } - /> - ) : ( - "..." - )} -
-
- )} -
-
-
- APR -
-
- ( - - )} - /> -
-
-
-
- Rewards -
-
- - - } - showDollar={false} - /> - - } - showDollar={false} - /> - {isAnyFeeGmxTrackerRewards && ( - - } - showDollar={false} - /> - )} - - } - /> -
-
-
-
-
- Total Staked -
-
- } - content={} - /> -
-
-
-
- Total Supply -
- -
-
-
- - {active && ( - - )} - {active && ( - - )} - {active && chainId === ARBITRUM && ( - - )} - {active && ( - - )} -
-
-
- ); -} diff --git a/src/pages/Stake/Stake.css b/src/pages/Stake/Stake.css deleted file mode 100644 index a7c368e070..0000000000 --- a/src/pages/Stake/Stake.css +++ /dev/null @@ -1,128 +0,0 @@ -.StakeModal .Modal-content { - width: 31rem; -} -.StakeModal .Modal-body { - font-size: var(--font-size-body-medium); -} - -.DelegateGMXAlertInfo { - text-wrap: wrap; - - a { - color: var(--warning-yellow); - opacity: 0.7; - - &:hover { - color: var(--warning-yellow); - opacity: 1; - } - } -} - -.StakeV2 .Page-title-section { - position: relative; - z-index: 2; -} - -.CompoundModal-menu { - margin-bottom: 0.8rem; -} - -.CompoundModal-menu .Checkbox { - margin-bottom: 0.465rem; -} - -.StakeV2-address-input { - padding: 1.5rem 3.41rem; - padding-bottom: 0; -} - -.StakeV2-buy-gmx-modal .Modal-content { - max-width: 46.5rem; -} - -.StakeV2-address-input input { - box-sizing: border-box; - width: 100%; - font-size: 1.7rem; -} - -.StakeV2-boost-bar { - overflow: hidden; - vertical-align: middle; - margin-left: 0.31rem; - border-radius: 2px; - width: 1.5rem; - height: 0.8rem; - border: 1px solid var(--color-gray-500); - display: inline-block; - position: relative; -} - -.StakeV2-boost-icon { - font-size: 1.085rem; - z-index: 2; -} - -.StakeV2-boost-bar-fill { - z-index: 1; - position: absolute; - left: 0; - top: 0; - bottom: 0; - background: var(--color-gray-300); -} - -.StakeV2-cards { - display: grid; - grid-template-columns: 1fr 1fr; - grid-gap: 1.5rem; -} - -.App-card-buttons { - display: flex; - flex-wrap: wrap; - gap: 1rem; -} - -.App-card-buttons.glp-buttons { - padding-top: 0.8rem; -} - -.App-card-footer { - position: absolute; - bottom: 0; - left: 0; - right: 0; - padding: 1.5rem 1.5rem 1.5rem; -} -.App-card-footer .App-card-divider { - margin-bottom: 1.8rem; -} - -.Stake-modal-icons { - display: inline-flex; - line-height: 1; -} - -.Stake-modal-icons .icon { - vertical-align: bottom; -} - -@media (max-width: 900px) { - .StakeV2-cards { - grid-template-columns: 1fr; - } - - .StakeV2-content { - min-height: 100vh; - } - - .StakeV2-total-rewards-card { - grid-row: 4; - } - .App-card-footer { - position: relative; - padding: 0; - } -} diff --git a/src/pages/Stake/Stake.tsx b/src/pages/Stake/Stake.tsx deleted file mode 100644 index fe12949863..0000000000 --- a/src/pages/Stake/Stake.tsx +++ /dev/null @@ -1,316 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import { useCallback, useMemo, useState } from "react"; -import useSWR from "swr"; -import { zeroAddress } from "viem"; - -import { AVALANCHE, BOTANIX, getChainName } from "config/chains"; -import { getContract } from "config/contracts"; -import { getIncentivesV2Url } from "config/links"; -import { usePendingTxns } from "context/PendingTxnsContext/PendingTxnsContext"; -import useIncentiveStats from "domain/synthetics/common/useIncentiveStats"; -import { getTotalGmInfo, useMarketTokensData } from "domain/synthetics/markets"; -import { useLpInterviewNotification } from "domain/synthetics/userFeedback/useLpInterviewNotification"; -import { useChainId } from "lib/chains"; -import { contractFetcher } from "lib/contracts"; -import { PLACEHOLDER_ACCOUNT, getPageTitle } from "lib/legacy"; -import { formatAmount } from "lib/numbers"; -import useWallet from "lib/wallets/useWallet"; -import { bigMath } from "sdk/utils/bigmath"; - -import SEO from "components/Common/SEO"; -import ExternalLink from "components/ExternalLink/ExternalLink"; -import Footer from "components/Footer/Footer"; -import { InterviewModal } from "components/InterviewModal/InterviewModal"; -import PageTitle from "components/PageTitle/PageTitle"; -import { BotanixBanner } from "components/Synthetics/BotanixBanner/BotanixBanner"; -import UserIncentiveDistributionList from "components/Synthetics/UserIncentiveDistributionList/UserIncentiveDistributionList"; - -import { EscrowedGmxCard } from "./EscrowedGmxCard"; -import { GlpCard } from "./GlpCard"; -import { GmxAndVotingPowerCard } from "./GmxAndVotingPowerCard"; -import { StakeModal } from "./StakeModal"; -import { TotalRewardsCard } from "./TotalRewardsCard"; -import { UnstakeModal } from "./UnstakeModal"; -import { useProcessedData } from "./useProcessedData"; -import { Vesting } from "./Vesting"; - -import "./Stake.css"; - -function StakeContent() { - const { active, signer, account } = useWallet(); - const { chainId } = useChainId(); - const incentiveStats = useIncentiveStats(chainId); - const { isLpInterviewModalVisible, setIsLpInterviewModalVisible } = useLpInterviewNotification(); - - const incentivesMessage = useMemo(() => { - const avalancheLink = ( - - {getChainName(AVALANCHE)} - - ); - if (incentiveStats?.lp?.isActive && incentiveStats?.trading?.isActive) { - return ( -
- Liquidity and trading incentives programs are live on {avalancheLink}. -
- ); - } else if (incentiveStats?.lp?.isActive) { - return ( -
- Liquidity incentives program is live on {avalancheLink}. -
- ); - } else if (incentiveStats?.trading?.isActive) { - return ( -
- Trading incentives program is live on {avalancheLink}. -
- ); - } - }, [incentiveStats?.lp?.isActive, incentiveStats?.trading?.isActive]); - - const { setPendingTxns } = usePendingTxns(); - - const [isStakeGmxModalVisible, setIsStakeGmxModalVisible] = useState(false); - const [stakeGmxValue, setStakeGmxValue] = useState(""); - const [isStakeEsGmxModalVisible, setIsStakeEsGmxModalVisible] = useState(false); - const [stakeEsGmxValue, setStakeEsGmxValue] = useState(""); - - const [isUnstakeModalVisible, setIsUnstakeModalVisible] = useState(false); - const [unstakeModalTitle, setUnstakeModalTitle] = useState(""); - const [unstakeModalMaxAmount, setUnstakeModalMaxAmount] = useState(undefined); - const [unstakeValue, setUnstakeValue] = useState(""); - const [unstakingTokenSymbol, setUnstakingTokenSymbol] = useState(""); - const [unstakeMethodName, setUnstakeMethodName] = useState(""); - - const rewardRouterAddress = getContract(chainId, "RewardRouter"); - - const gmxAddress = getContract(chainId, "GMX"); - const esGmxAddress = getContract(chainId, "ES_GMX"); - - const stakedGmxTrackerAddress = getContract(chainId, "StakedGmxTracker"); - const feeGmxTrackerAddress = getContract(chainId, "FeeGmxTracker"); - - const { marketTokensData } = useMarketTokensData(chainId, { isDeposit: false }); - - const { data: sbfGmxBalance } = useSWR( - [`StakeV2:sbfGmxBalance:${active}`, chainId, feeGmxTrackerAddress, "balanceOf", account ?? PLACEHOLDER_ACCOUNT], - { - fetcher: contractFetcher(undefined, "Token"), - } - ); - - const userTotalGmInfo = useMemo(() => { - if (!active) return; - return getTotalGmInfo(marketTokensData); - }, [marketTokensData, active]); - - const processedData = useProcessedData(); - - const reservedAmount = - (processedData?.gmxInStakedGmx !== undefined && - processedData?.esGmxInStakedGmx !== undefined && - sbfGmxBalance !== undefined && - processedData?.gmxInStakedGmx + processedData?.esGmxInStakedGmx - sbfGmxBalance) || - 0n; - - let totalRewardTokens; - - if (processedData && processedData.bonusGmxInFeeGmx !== undefined) { - totalRewardTokens = processedData.bonusGmxInFeeGmx; - } - - let totalRewardAndLpTokens = totalRewardTokens ?? 0n; - if (processedData?.glpBalance !== undefined) { - totalRewardAndLpTokens = totalRewardAndLpTokens + processedData.glpBalance; - } - if ((userTotalGmInfo?.balance ?? 0n) > 0) { - totalRewardAndLpTokens = totalRewardAndLpTokens + (userTotalGmInfo?.balance ?? 0n); - } - - const showStakeGmxModal = useCallback(() => { - setIsStakeGmxModalVisible(true); - setStakeGmxValue(""); - }, []); - - const showStakeEsGmxModal = useCallback(() => { - setIsStakeEsGmxModalVisible(true); - setStakeEsGmxValue(""); - }, []); - - const showUnstakeEsGmxModal = () => { - setIsUnstakeModalVisible(true); - setUnstakeModalTitle(t`Unstake esGMX`); - let maxAmount = processedData?.esGmxInStakedGmx; - - if (maxAmount !== undefined) { - maxAmount = bigMath.min(maxAmount, sbfGmxBalance); - } - - setUnstakeModalMaxAmount(maxAmount); - setUnstakeValue(""); - setUnstakingTokenSymbol("esGMX"); - setUnstakeMethodName("unstakeEsGmx"); - }; - - let earnMsg; - if (totalRewardAndLpTokens && totalRewardAndLpTokens > 0) { - let gmxAmountStr; - if (processedData?.gmxInStakedGmx && processedData.gmxInStakedGmx > 0) { - gmxAmountStr = formatAmount(processedData.gmxInStakedGmx, 18, 2, true) + " GMX"; - } - let esGmxAmountStr; - if (processedData?.esGmxInStakedGmx && processedData.esGmxInStakedGmx > 0) { - esGmxAmountStr = formatAmount(processedData.esGmxInStakedGmx, 18, 2, true) + " esGMX"; - } - let glpStr; - if (processedData?.glpBalance && processedData.glpBalance > 0) { - glpStr = formatAmount(processedData.glpBalance, 18, 2, true) + " GLP"; - } - let gmStr; - if (userTotalGmInfo?.balance && userTotalGmInfo.balance > 0) { - gmStr = formatAmount(userTotalGmInfo.balance, 18, 2, true) + " GM"; - } - const amountStr = [gmxAmountStr, esGmxAmountStr, gmStr, glpStr].filter((s) => s).join(", "); - earnMsg = ( -
- - You are earning rewards with {formatAmount(totalRewardAndLpTokens, 18, 2, true)} tokens. -
- Tokens: {amountStr}. -
-
- ); - } - - return ( -
- - - - - Deposit GMX and{" "} - esGMX tokens to - earn rewards. - - {earnMsg &&
{earnMsg}
} - {incentivesMessage} -
- } - /> - - - - - - - -
-
- - - - -
-
- - - -
- Claim and view your incentives, airdrops, and prizes} - /> -
- - - - -
-
- ); -} - -export default function Stake() { - const { chainId } = useChainId(); - const isBotanix = chainId === BOTANIX; - - return isBotanix ? ( -
- - - - - -
-
- ) : ( - - ); -} diff --git a/src/pages/Stake/StakeModal.tsx b/src/pages/Stake/StakeModal.tsx deleted file mode 100644 index 2296887cce..0000000000 --- a/src/pages/Stake/StakeModal.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import cx from "classnames"; -import { ZeroAddress, ethers } from "ethers"; -import { useCallback, useMemo, useState } from "react"; - -import { ARBITRUM } from "config/chains"; -import { BASIS_POINTS_DIVISOR_BIGINT } from "config/factors"; -import { getIcons } from "config/icons"; -import { MAX_METAMASK_MOBILE_DECIMALS } from "config/ui"; -import { SetPendingTransactions } from "context/PendingTxnsContext/PendingTxnsContext"; -import { useGovTokenAmount } from "domain/synthetics/governance/useGovTokenAmount"; -import { useGovTokenDelegates } from "domain/synthetics/governance/useGovTokenDelegates"; -import { useTokensAllowanceData } from "domain/synthetics/tokens"; -import { approveTokens } from "domain/tokens"; -import { callContract } from "lib/contracts"; -import { ProcessedData } from "lib/legacy"; -import { formatAmount, formatAmountFree, limitDecimals, parseValue } from "lib/numbers"; -import { UncheckedJsonRpcSigner } from "lib/rpc/UncheckedJsonRpcSigner"; -import useIsMetamaskMobile from "lib/wallets/useIsMetamaskMobile"; -import { abis } from "sdk/abis"; -import { NATIVE_TOKEN_ADDRESS } from "sdk/configs/tokens"; -import { bigMath } from "sdk/utils/bigmath"; - -import { AlertInfo } from "components/AlertInfo/AlertInfo"; -import { ApproveTokenButton } from "components/ApproveTokenButton/ApproveTokenButton"; -import Button from "components/Button/Button"; -import BuyInputSection from "components/BuyInputSection/BuyInputSection"; -import ExternalLink from "components/ExternalLink/ExternalLink"; -import Modal from "components/Modal/Modal"; - -import { GMX_DAO_LINKS } from "./constants"; - -export function StakeModal(props: { - isVisible: boolean; - setIsVisible: (isVisible: boolean) => void; - chainId: number; - title: string; - maxAmount: bigint | undefined; - value: string; - setValue: (value: string) => void; - signer: UncheckedJsonRpcSigner | undefined; - stakingTokenSymbol: string; - stakingTokenAddress: string; - farmAddress: string; - rewardRouterAddress: string; - stakeMethodName: string; - setPendingTxns: SetPendingTransactions; - processedData: ProcessedData | undefined; -}) { - const { - isVisible, - setIsVisible, - chainId, - title, - maxAmount, - value, - setValue, - signer, - stakingTokenSymbol, - stakingTokenAddress, - farmAddress, - rewardRouterAddress, - stakeMethodName, - setPendingTxns, - processedData, - } = props; - - const govTokenAmount = useGovTokenAmount(chainId); - const govTokenDelegatesAddress = useGovTokenDelegates(chainId); - const isUndelegatedGovToken = useMemo( - () => - chainId === ARBITRUM && govTokenDelegatesAddress === NATIVE_TOKEN_ADDRESS && govTokenAmount && govTokenAmount > 0, - [chainId, govTokenDelegatesAddress, govTokenAmount] - ); - - const [isStaking, setIsStaking] = useState(false); - const isMetamaskMobile = useIsMetamaskMobile(); - const [isApproving, setIsApproving] = useState(false); - const icons = getIcons(chainId); - - const { tokensAllowanceData } = useTokensAllowanceData(chainId, { - spenderAddress: farmAddress, - tokenAddresses: [stakingTokenAddress].filter(Boolean), - }); - const tokenAllowance = tokensAllowanceData?.[stakingTokenAddress]; - - const amount = useMemo(() => parseValue(value, 18), [value]); - - const needApproval = - farmAddress !== ZeroAddress && tokenAllowance !== undefined && amount !== undefined && amount > tokenAllowance; - - const stakeBonusPercentage = useMemo(() => { - if ( - processedData && - amount !== undefined && - amount > 0 && - processedData.esGmxInStakedGmx !== undefined && - processedData.gmxInStakedGmx !== undefined - ) { - const divisor = processedData.esGmxInStakedGmx + processedData.gmxInStakedGmx; - if (divisor !== 0n) { - return bigMath.mulDiv(amount, BASIS_POINTS_DIVISOR_BIGINT, divisor); - } - } - return undefined; - }, [amount, processedData]); - - const error = useMemo(() => { - if (amount === undefined || amount === 0n) { - return t`Enter an amount`; - } - if (maxAmount !== undefined && amount > maxAmount) { - return t`Max amount exceeded`; - } - return undefined; - }, [amount, maxAmount]); - - const isPrimaryEnabled = useMemo( - () => !error && !isApproving && !needApproval && !isStaking && !isUndelegatedGovToken, - [error, isApproving, needApproval, isStaking, isUndelegatedGovToken] - ); - - const primaryText = useMemo(() => { - if (error) { - return error; - } - if (isApproving || needApproval) { - return t`Pending ${stakingTokenSymbol} approval`; - } - if (isStaking) { - return t`Staking...`; - } - return t`Stake`; - }, [error, isApproving, needApproval, isStaking, stakingTokenSymbol]); - - const onClickPrimary = useCallback(() => { - if (needApproval) { - approveTokens({ - setIsApproving, - signer, - tokenAddress: stakingTokenAddress, - spender: farmAddress, - chainId, - permitParams: undefined, - approveAmount: undefined, - }); - return; - } - - setIsStaking(true); - const contract = new ethers.Contract(rewardRouterAddress, abis.RewardRouter, signer); - - callContract(chainId, contract, stakeMethodName, [amount], { - sentMsg: t`Stake submitted!`, - failMsg: t`Stake failed.`, - setPendingTxns, - }) - .then(() => { - setIsVisible(false); - }) - .finally(() => { - setIsStaking(false); - }); - }, [ - needApproval, - signer, - stakingTokenAddress, - farmAddress, - chainId, - rewardRouterAddress, - stakeMethodName, - amount, - setPendingTxns, - setIsVisible, - ]); - - const onClickMaxButton = useCallback(() => { - if (maxAmount === undefined) return; - const formattedMaxAmount = formatAmountFree(maxAmount, 18, 18); - const finalMaxAmount = isMetamaskMobile - ? limitDecimals(formattedMaxAmount, MAX_METAMASK_MOBILE_DECIMALS) - : formattedMaxAmount; - setValue(finalMaxAmount); - }, [maxAmount, isMetamaskMobile, setValue]); - - return ( -
- -
- setValue(e.target.value)} - > -
- {stakingTokenSymbol} - {stakingTokenSymbol} -
-
-
- - {(needApproval || isApproving) && ( -
- -
- )} - - {stakeBonusPercentage !== undefined && - stakeBonusPercentage > 0 && - amount !== undefined && - maxAmount !== undefined && - amount <= maxAmount && ( - - You will earn {formatAmount(stakeBonusPercentage, 2, 2)}% more rewards with this action. - - )} - - {isUndelegatedGovToken ? ( - - - - Delegate your undelegated {formatAmount(govTokenAmount, 18, 2, true)} GMX DAO - {" "} - voting power before staking. - - - ) : null} - -
- -
-
-
- ); -} diff --git a/src/pages/Stake/TotalRewardsCard.tsx b/src/pages/Stake/TotalRewardsCard.tsx deleted file mode 100644 index bee47abaad..0000000000 --- a/src/pages/Stake/TotalRewardsCard.tsx +++ /dev/null @@ -1,263 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import { useConnectModal } from "@rainbow-me/rainbowkit"; -import { useCallback, useMemo, useState } from "react"; -import { Link } from "react-router-dom"; -import { toast } from "react-toastify"; -import useSWR from "swr"; - -import { ARBITRUM, getConstant } from "config/chains"; -import { getContract } from "config/contracts"; -import { USD_DECIMALS } from "config/factors"; -import { MARKETS } from "config/markets"; -import { usePendingTxns } from "context/PendingTxnsContext/PendingTxnsContext"; -import { useGmMarketsApy } from "domain/synthetics/markets/useGmMarketsApy"; -import { useChainId } from "lib/chains"; -import { contractFetcher } from "lib/contracts/contractFetcher"; -import { PLACEHOLDER_ACCOUNT, ProcessedData } from "lib/legacy"; -import { formatAmount, formatKeyAmount } from "lib/numbers"; -import useWallet from "lib/wallets/useWallet"; - -import { AmountWithUsdBalance } from "components/AmountWithUsd/AmountWithUsd"; -import Button from "components/Button/Button"; -import StatsTooltipRow from "components/StatsTooltip/StatsTooltipRow"; -import Tooltip from "components/Tooltip/Tooltip"; - -import { ClaimModal } from "./ClaimModal"; - -export function TotalRewardsCard({ - processedData, - showStakeGmxModal, -}: { - processedData: ProcessedData | undefined; - showStakeGmxModal: () => void; -}) { - const { active, account, signer } = useWallet(); - const { chainId } = useChainId(); - const { openConnectModal } = useConnectModal(); - const { setPendingTxns } = usePendingTxns(); - - const [isCompoundModalVisible, setIsCompoundModalVisible] = useState(false); - - const nativeTokenSymbol = getConstant(chainId, "nativeTokenSymbol"); - const wrappedTokenSymbol = getConstant(chainId, "wrappedTokenSymbol"); - const rewardRouterAddress = getContract(chainId, "RewardRouter"); - const readerAddress = getContract(chainId, "Reader"); - const gmxAddress = getContract(chainId, "GMX"); - const esGmxAddress = getContract(chainId, "ES_GMX"); - const glpAddress = getContract(chainId, "GLP"); - - const stakedGmxTrackerAddress = getContract(chainId, "StakedGmxTracker"); - - const walletTokens = [gmxAddress, esGmxAddress, glpAddress, stakedGmxTrackerAddress]; - - const isAnyNativeTokenRewards = - (processedData?.totalNativeTokenRewardsUsd ?? 0n) > 10n ** BigInt(USD_DECIMALS) / 100n; - - const { mutate: refetchBalances } = useSWR( - [ - `StakeV2:walletBalances:${active}`, - chainId, - readerAddress, - "getTokenBalancesWithSupplies", - account || PLACEHOLDER_ACCOUNT, - ], - { - fetcher: contractFetcher(signer, "ReaderV2", [walletTokens]), - } - ); - - const gmxAvgAprText = useMemo(() => { - return `${formatAmount(processedData?.gmxAprTotal, 2, 2, true)}%`; - }, [processedData?.gmxAprTotal]); - - const gmxMarketAddress = useMemo(() => { - if (chainId === ARBITRUM) { - return Object.values(MARKETS[ARBITRUM]).find((m) => m.indexTokenAddress === gmxAddress)?.marketTokenAddress; - } - return undefined; - }, [chainId, gmxAddress]); - - const { - marketsTokensApyData, - marketsTokensIncentiveAprData, - // glvTokensIncentiveAprData, - // marketsTokensLidoAprData, - // glvApyInfoData, - } = useGmMarketsApy(chainId, { period: "90d" }); - - const gmxMarketApyDataText = useMemo(() => { - if (!gmxMarketAddress || chainId !== ARBITRUM) return; - - const gmxApy = - (marketsTokensApyData?.[gmxMarketAddress] ?? 0n) + (marketsTokensIncentiveAprData?.[gmxMarketAddress] ?? 0n); - - return `${formatAmount(gmxApy, 28, 2, true)}%`; - }, [marketsTokensApyData, marketsTokensIncentiveAprData, gmxMarketAddress, chainId]); - - const hideToasts = useCallback(() => toast.dismiss(), []); - const handleStakeGmx = useCallback(async () => { - hideToasts(); - showStakeGmxModal(); - }, [showStakeGmxModal, hideToasts]); - - return ( - <> - -
  • - - - Stake GMX - {" "} - and earn {gmxAvgAprText} APR - -
  • - {chainId === ARBITRUM && ( - <> -
  • - - - Provide liquidity - {" "} - and earn {gmxMarketApyDataText} APY - -
  • -
  • - - - Trade GMX - - -
  • - - )} - - } - /> -
    -
    - Total Rewards -
    -
    -
    -
    -
    GMX
    - - } - position="bottom-end" - content={ - <> - - {t`GMX Staked Rewards`}: -
    - - } - showDollar={false} - value={ - - } - /> - - {t`Vested Claimable GMX`}: -
    - - } - showDollar={false} - value={ - - } - /> - - } - /> -
    -
    -
    - Escrowed GMX -
    -
    - -
    -
    - {isAnyNativeTokenRewards ? ( -
    -
    - {nativeTokenSymbol} ({wrappedTokenSymbol}) -
    -
    - -
    -
    - ) : null} -
    -
    - Total -
    -
    ${formatKeyAmount(processedData, "totalRewardsUsd", USD_DECIMALS, 2, true)}
    -
    -
    -
    -
    - {active && ( - - )} - {!active && ( - - )} -
    -
    -
    -
    - - ); -} diff --git a/src/pages/Stake/UnstakeModal.tsx b/src/pages/Stake/UnstakeModal.tsx deleted file mode 100644 index 52a0a913b9..0000000000 --- a/src/pages/Stake/UnstakeModal.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import { ethers } from "ethers"; -import { useCallback, useMemo, useState } from "react"; - -import { ARBITRUM } from "config/chains"; -import { BASIS_POINTS_DIVISOR_BIGINT } from "config/factors"; -import { getIcons } from "config/icons"; -import { SetPendingTransactions } from "context/PendingTxnsContext/PendingTxnsContext"; -import { callContract } from "lib/contracts"; -import { ProcessedData } from "lib/legacy"; -import { formatAmount, formatAmountFree, parseValue } from "lib/numbers"; -import { UncheckedJsonRpcSigner } from "lib/rpc/UncheckedJsonRpcSigner"; -import { abis } from "sdk/abis"; -import { bigMath } from "sdk/utils/bigmath"; - -import { AlertInfo } from "components/AlertInfo/AlertInfo"; -import Button from "components/Button/Button"; -import BuyInputSection from "components/BuyInputSection/BuyInputSection"; -import Modal from "components/Modal/Modal"; - -export function UnstakeModal(props: { - isVisible: boolean; - setIsVisible: (isVisible: boolean) => void; - chainId: number; - title: string; - maxAmount: bigint | undefined; - value: string; - setValue: (value: string) => void; - signer: UncheckedJsonRpcSigner | undefined; - unstakingTokenSymbol: string; - rewardRouterAddress: string; - unstakeMethodName: string; - reservedAmount: bigint | undefined; - setPendingTxns: SetPendingTransactions; - processedData: ProcessedData | undefined; -}) { - const { - isVisible, - setIsVisible, - chainId, - title, - maxAmount, - value, - setValue, - signer, - unstakingTokenSymbol, - rewardRouterAddress, - unstakeMethodName, - reservedAmount, - setPendingTxns, - processedData, - } = props; - - const [isUnstaking, setIsUnstaking] = useState(false); - const icons = getIcons(chainId); - - const amount = useMemo(() => parseValue(value, 18), [value]); - - const unstakeBonusLostPercentage = useMemo(() => { - if ( - processedData && - amount !== undefined && - amount > 0 && - processedData.esGmxInStakedGmx !== undefined && - processedData.gmxInStakedGmx !== undefined - ) { - const divisor = processedData.esGmxInStakedGmx + processedData.gmxInStakedGmx; - if (divisor !== 0n) { - return bigMath.mulDiv(amount, BASIS_POINTS_DIVISOR_BIGINT, divisor); - } - } - return undefined; - }, [amount, processedData]); - - const votingPowerBurnAmount = amount; - - const error = useMemo(() => { - if (amount === undefined || amount === 0n) { - return t`Enter an amount`; - } - if (maxAmount !== undefined && amount > maxAmount) { - return t`Max amount exceeded`; - } - return undefined; - }, [amount, maxAmount]); - - const isPrimaryEnabled = !error && !isUnstaking; - - const primaryText = useMemo(() => { - if (error) { - return error; - } - if (isUnstaking) { - return t`Unstaking...`; - } - return t`Unstake`; - }, [error, isUnstaking]); - - const onClickPrimary = useCallback(() => { - setIsUnstaking(true); - const contract = new ethers.Contract(rewardRouterAddress, abis.RewardRouter, signer); - callContract(chainId, contract, unstakeMethodName, [amount], { - sentMsg: t`Unstake submitted!`, - failMsg: t`Unstake failed.`, - successMsg: t`Unstake completed!`, - setPendingTxns, - }) - .then(() => { - setIsVisible(false); - }) - .finally(() => { - setIsUnstaking(false); - }); - }, [chainId, rewardRouterAddress, unstakeMethodName, amount, setPendingTxns, setIsVisible, signer]); - - const onClickMaxButton = useCallback(() => { - if (maxAmount === undefined) return; - setValue(formatAmountFree(maxAmount, 18, 18)); - }, [maxAmount, setValue]); - - return ( -
    - -
    - setValue(e.target.value)} - > -
    - {unstakingTokenSymbol} - {unstakingTokenSymbol} -
    -
    -
    - {reservedAmount !== undefined && reservedAmount > 0 && ( - - You have {formatAmount(reservedAmount, 18, 2, true)} tokens reserved for vesting. - - )} - {unstakeBonusLostPercentage !== undefined && - unstakeBonusLostPercentage > 0 && - amount !== undefined && - maxAmount !== undefined && - amount <= maxAmount && ( - - - {chainId === ARBITRUM ? ( - - Unstaking will burn {formatAmount(votingPowerBurnAmount, 18, 2, true)} voting power.  - - ) : null} - - You will earn {formatAmount(unstakeBonusLostPercentage, 2, 2)}% less rewards with this action. - - - - )} -
    - -
    -
    -
    - ); -} diff --git a/src/pages/Stake/VesterDepositModal.tsx b/src/pages/Stake/VesterDepositModal.tsx deleted file mode 100644 index 2842fb1202..0000000000 --- a/src/pages/Stake/VesterDepositModal.tsx +++ /dev/null @@ -1,241 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import { ethers } from "ethers"; -import { useCallback, useMemo, useState } from "react"; - -import { getIcons } from "config/icons"; -import { SetPendingTransactions } from "context/PendingTxnsContext/PendingTxnsContext"; -import { callContract } from "lib/contracts"; -import { formatAmount, formatAmountFree, parseValue } from "lib/numbers"; -import { UncheckedJsonRpcSigner } from "lib/rpc/UncheckedJsonRpcSigner"; -import { abis } from "sdk/abis"; -import { bigMath } from "sdk/utils/bigmath"; - -import Button from "components/Button/Button"; -import BuyInputSection from "components/BuyInputSection/BuyInputSection"; -import Modal from "components/Modal/Modal"; -import StatsTooltipRow from "components/StatsTooltip/StatsTooltipRow"; -import { SyntheticsInfoRow } from "components/Synthetics/SyntheticsInfoRow"; -import TooltipWithPortal from "components/Tooltip/TooltipWithPortal"; - -export function VesterDepositModal(props: { - isVisible: boolean; - setIsVisible: (isVisible: boolean) => void; - chainId: number; - title: string; - maxAmount: bigint | undefined; - value: string; - setValue: (value: string) => void; - balance: bigint | undefined; - vestedAmount: bigint | undefined; - averageStakedAmount: bigint | undefined; - maxVestableAmount: bigint | undefined; - signer: UncheckedJsonRpcSigner | undefined; - stakeTokenLabel: string; - reserveAmount: bigint | undefined; - maxReserveAmount: bigint | undefined; - vesterAddress: string; - setPendingTxns: SetPendingTransactions; -}) { - const { - isVisible, - setIsVisible, - chainId, - title, - maxAmount, - value, - setValue, - balance, - vestedAmount, - averageStakedAmount, - maxVestableAmount, - signer, - stakeTokenLabel, - reserveAmount, - maxReserveAmount, - vesterAddress, - setPendingTxns, - } = props; - const [isDepositing, setIsDepositing] = useState(false); - const icons = getIcons(chainId); - - const amount = useMemo(() => parseValue(value, 18), [value]); - - const { nextReserveAmount, nextDepositAmount, additionalReserveAmount } = useMemo(() => { - let nextReserveAmount = reserveAmount; - let nextDepositAmount = vestedAmount; - let additionalReserveAmount = 0n; - - if (amount !== undefined && vestedAmount !== undefined) { - nextDepositAmount = vestedAmount + amount; - } - - if ( - amount !== undefined && - nextDepositAmount !== undefined && - averageStakedAmount !== undefined && - maxVestableAmount !== undefined && - maxVestableAmount > 0 && - nextReserveAmount !== undefined - ) { - nextReserveAmount = bigMath.mulDiv(nextDepositAmount, averageStakedAmount, maxVestableAmount); - if (reserveAmount !== undefined && nextReserveAmount > reserveAmount) { - additionalReserveAmount = nextReserveAmount - reserveAmount; - } - } - - return { nextReserveAmount, nextDepositAmount, additionalReserveAmount }; - }, [amount, vestedAmount, averageStakedAmount, maxVestableAmount, reserveAmount]); - - const error = useMemo(() => { - if (amount === undefined) { - return t`Enter an amount`; - } - if (maxAmount !== undefined && amount > maxAmount) { - return t`Max amount exceeded`; - } - if (maxReserveAmount !== undefined && nextReserveAmount !== undefined && nextReserveAmount > maxReserveAmount) { - return t`Insufficient staked tokens`; - } - return undefined; - }, [amount, maxAmount, maxReserveAmount, nextReserveAmount]); - - const isPrimaryEnabled = !error && !isDepositing; - - const primaryText = useMemo(() => { - if (error) { - return error; - } - if (isDepositing) { - return t`Depositing...`; - } - return t`Deposit`; - }, [error, isDepositing]); - - const onClickPrimary = useCallback(() => { - setIsDepositing(true); - const contract = new ethers.Contract(vesterAddress, abis.Vester, signer); - - callContract(chainId, contract, "deposit", [amount], { - sentMsg: t`Deposit submitted!`, - failMsg: t`Deposit failed!`, - successMsg: t`Deposited!`, - setPendingTxns, - }) - .then(() => { - setIsVisible(false); - }) - .finally(() => { - setIsDepositing(false); - }); - }, [chainId, vesterAddress, amount, setPendingTxns, setIsVisible, signer]); - - const onClickMaxButton = useCallback(() => { - if (maxAmount === undefined) return; - setValue(formatAmountFree(maxAmount, 18, 18)); - }, [maxAmount, setValue]); - - return ( -
    - -
    - setValue(e.target.value)} - > -
    - esGMX - esGMX -
    -
    -
    - -
    - {formatAmount(balance, 18, 2, true)} esGMX} /> - -

    - Vault Capacity for your Account: -

    -
    - - -
    - } - /> - } - /> - {reserveAmount !== undefined && ( - - - - {amount !== undefined && - nextReserveAmount !== undefined && - maxReserveAmount !== undefined && - nextReserveAmount > maxReserveAmount && ( - <> -
    - - You need a total of at least {formatAmount(nextReserveAmount, 18, 2, true)}{" "} - {stakeTokenLabel} to vest {formatAmount(amount, 18, 2, true)} esGMX. - - - )} - - } - /> - } - /> - )} -
    -
    - -
    - -
    - ); -} diff --git a/src/pages/Stake/VesterWithdrawModal.tsx b/src/pages/Stake/VesterWithdrawModal.tsx deleted file mode 100644 index 8c94514958..0000000000 --- a/src/pages/Stake/VesterWithdrawModal.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import { ethers } from "ethers"; -import { useState } from "react"; - -import { SetPendingTransactions } from "context/PendingTxnsContext/PendingTxnsContext"; -import { callContract } from "lib/contracts"; -import { UncheckedJsonRpcSigner } from "lib/rpc/UncheckedJsonRpcSigner"; -import { abis } from "sdk/abis"; - -import Button from "components/Button/Button"; -import Modal from "components/Modal/Modal"; - -export function VesterWithdrawModal(props: { - isVisible: boolean; - setIsVisible: (isVisible: boolean) => void; - chainId: number; - title: string; - signer: UncheckedJsonRpcSigner | undefined; - vesterAddress: string; - setPendingTxns: SetPendingTransactions; -}) { - const { isVisible, setIsVisible, chainId, title, signer, vesterAddress, setPendingTxns } = props; - const [isWithdrawing, setIsWithdrawing] = useState(false); - - const onClickPrimary = () => { - setIsWithdrawing(true); - const contract = new ethers.Contract(vesterAddress, abis.Vester, signer); - - callContract(chainId, contract, "withdraw", [], { - sentMsg: t`Withdraw submitted.`, - failMsg: t`Withdraw failed.`, - successMsg: t`Withdrawn!`, - setPendingTxns, - }) - .then(() => { - setIsVisible(false); - }) - .finally(() => { - setIsWithdrawing(false); - }); - }; - - return ( -
    - - -
    - This will withdraw and unreserve all tokens as well as pause vesting. -
    -
    - esGMX tokens that have been converted to GMX will be claimed and remain as GMX tokens. -
    -
    - To claim GMX tokens without withdrawing, use the "Claim" button under the Total Rewards section. -
    -
    -
    -
    -
    - -
    -
    -
    - ); -} diff --git a/src/pages/Stake/Vesting.tsx b/src/pages/Stake/Vesting.tsx deleted file mode 100644 index a7fa5ac1fd..0000000000 --- a/src/pages/Stake/Vesting.tsx +++ /dev/null @@ -1,528 +0,0 @@ -import { Trans, t } from "@lingui/macro"; -import { useConnectModal } from "@rainbow-me/rainbowkit"; -import { useState } from "react"; -import useSWR from "swr"; - -import { getContract } from "config/contracts"; -import { getIcons } from "config/icons"; -import { usePendingTxns } from "context/PendingTxnsContext/PendingTxnsContext"; -import useVestingData from "domain/vesting/useVestingData"; -import { useChainId } from "lib/chains"; -import { contractFetcher } from "lib/contracts"; -import { helperToast } from "lib/helperToast"; -import { PLACEHOLDER_ACCOUNT, ProcessedData } from "lib/legacy"; -import { formatAmount, formatKeyAmount } from "lib/numbers"; -import useWallet from "lib/wallets/useWallet"; - -import Button from "components/Button/Button"; -import ExternalLink from "components/ExternalLink/ExternalLink"; -import PageTitle from "components/PageTitle/PageTitle"; -import StatsTooltipRow from "components/StatsTooltip/StatsTooltipRow"; -import Tooltip from "components/Tooltip/Tooltip"; - -import { AffiliateClaimModal } from "./AffiliateClaimModal"; -import { AffiliateVesterWithdrawModal } from "./AffiliateVesterWithdrawModal"; -import { VesterDepositModal } from "./VesterDepositModal"; -import { VesterWithdrawModal } from "./VesterWithdrawModal"; - -export function Vesting({ processedData }: { processedData: ProcessedData | undefined }) { - const { active, signer, account } = useWallet(); - const { chainId } = useChainId(); - const { openConnectModal } = useConnectModal(); - const [isAffiliateVesterWithdrawModalVisible, setIsAffiliateVesterWithdrawModalVisible] = useState(false); - const vestingData = useVestingData(account); - const { setPendingTxns } = usePendingTxns(); - - const [isVesterWithdrawModalVisible, setIsVesterWithdrawModalVisible] = useState(false); - const [vesterWithdrawTitle, setVesterWithdrawTitle] = useState(""); - const [vesterWithdrawAddress, setVesterWithdrawAddress] = useState(""); - const [vesterDepositMaxReserveAmount, setVesterDepositMaxReserveAmount] = useState(); - const [vesterDepositStakeTokenLabel, setVesterDepositStakeTokenLabel] = useState(""); - const [isVesterDepositModalVisible, setIsVesterDepositModalVisible] = useState(false); - const [vesterDepositTitle, setVesterDepositTitle] = useState(""); - const [vesterDepositMaxAmount, setVesterDepositMaxAmount] = useState(); - const [vesterDepositBalance, setVesterDepositBalance] = useState(); - const [vesterDepositVestedAmount, setVesterDepositVestedAmount] = useState(); - const [vesterDepositAverageStakedAmount, setVesterDepositAverageStakedAmount] = useState( - "" - ); - const [vesterDepositMaxVestableAmount, setVesterDepositMaxVestableAmount] = useState(); - const [vesterDepositValue, setVesterDepositValue] = useState(""); - const [vesterDepositReserveAmount, setVesterDepositReserveAmount] = useState(); - const [vesterDepositAddress, setVesterDepositAddress] = useState(""); - const [isAffiliateClaimModalVisible, setIsAffiliateClaimModalVisible] = useState(false); - - const icons = getIcons(chainId); - - const feeGmxTrackerAddress = getContract(chainId, "FeeGmxTracker"); - const gmxVesterAddress = getContract(chainId, "GmxVester"); - const glpVesterAddress = getContract(chainId, "GlpVester"); - const affiliateVesterAddress = getContract(chainId, "AffiliateVester"); - - const { data: sbfGmxBalance } = useSWR( - [`StakeV2:sbfGmxBalance:${active}`, chainId, feeGmxTrackerAddress, "balanceOf", account ?? PLACEHOLDER_ACCOUNT], - { - fetcher: contractFetcher(undefined, "Token"), - } - ); - - const reservedAmount = - (processedData?.gmxInStakedGmx !== undefined && - processedData?.esGmxInStakedGmx !== undefined && - sbfGmxBalance !== undefined && - processedData?.gmxInStakedGmx + processedData?.esGmxInStakedGmx - sbfGmxBalance) || - 0n; - - let totalRewardTokens; - - if (processedData && processedData.bonusGmxInFeeGmx !== undefined) { - totalRewardTokens = processedData.bonusGmxInFeeGmx; - } - - function showAffiliateVesterWithdrawModal() { - if (vestingData?.affiliateVesterVestedAmount === undefined || vestingData.affiliateVesterVestedAmount <= 0) { - helperToast.error(t`You have not deposited any tokens for vesting.`); - return; - } - - setIsAffiliateVesterWithdrawModalVisible(true); - } - - const showGmxVesterDepositModal = () => { - if (!vestingData) return; - - let remainingVestableAmount = vestingData.gmxVester.maxVestableAmount - vestingData.gmxVester.vestedAmount; - if (processedData?.esGmxBalance !== undefined && processedData?.esGmxBalance < remainingVestableAmount) { - remainingVestableAmount = processedData.esGmxBalance; - } - - setIsVesterDepositModalVisible(true); - setVesterDepositTitle(t`GMX Vault`); - setVesterDepositStakeTokenLabel("staked GMX + esGMX"); - setVesterDepositMaxAmount(remainingVestableAmount); - setVesterDepositBalance(processedData?.esGmxBalance); - setVesterDepositVestedAmount(vestingData.gmxVester.vestedAmount); - setVesterDepositMaxVestableAmount(vestingData.gmxVester.maxVestableAmount); - setVesterDepositAverageStakedAmount(vestingData.gmxVester.averageStakedAmount); - setVesterDepositReserveAmount(reservedAmount); - setVesterDepositMaxReserveAmount(totalRewardTokens); - setVesterDepositValue(""); - setVesterDepositAddress(gmxVesterAddress); - }; - - const showGlpVesterDepositModal = () => { - if (!vestingData) return; - - let remainingVestableAmount = vestingData.glpVester.maxVestableAmount - vestingData.glpVester.vestedAmount; - if (processedData?.esGmxBalance !== undefined && processedData?.esGmxBalance < remainingVestableAmount) { - remainingVestableAmount = processedData.esGmxBalance; - } - - setIsVesterDepositModalVisible(true); - setVesterDepositTitle(t`GLP Vault`); - setVesterDepositStakeTokenLabel("staked GLP"); - setVesterDepositMaxAmount(remainingVestableAmount); - setVesterDepositBalance(processedData?.esGmxBalance); - setVesterDepositVestedAmount(vestingData.glpVester.vestedAmount); - setVesterDepositMaxVestableAmount(vestingData.glpVester.maxVestableAmount); - setVesterDepositAverageStakedAmount(vestingData.glpVester.averageStakedAmount); - setVesterDepositReserveAmount(vestingData.glpVester.pairAmount); - setVesterDepositMaxReserveAmount(processedData?.glpBalance); - setVesterDepositValue(""); - setVesterDepositAddress(glpVesterAddress); - }; - - const showGmxVesterWithdrawModal = () => { - if (!vestingData || vestingData.gmxVesterVestedAmount === undefined || vestingData.gmxVesterVestedAmount === 0n) { - helperToast.error(t`You have not deposited any tokens for vesting.`); - return; - } - - setIsVesterWithdrawModalVisible(true); - setVesterWithdrawTitle(t`Withdraw from GMX Vault`); - setVesterWithdrawAddress(gmxVesterAddress); - }; - - const showGlpVesterWithdrawModal = () => { - if (!vestingData || vestingData.glpVesterVestedAmount === undefined || vestingData.glpVesterVestedAmount === 0n) { - helperToast.error(t`You have not deposited any tokens for vesting.`); - return; - } - - setIsVesterWithdrawModalVisible(true); - setVesterWithdrawTitle(t`Withdraw from GLP Vault`); - setVesterWithdrawAddress(glpVesterAddress); - }; - - function showAffiliateVesterDepositModal() { - if (!vestingData?.affiliateVester) { - helperToast.error(t`Unsupported network`); - return; - } - - let remainingVestableAmount = - vestingData.affiliateVester.maxVestableAmount - vestingData.affiliateVester.vestedAmount; - if (processedData?.esGmxBalance !== undefined && processedData.esGmxBalance < remainingVestableAmount) { - remainingVestableAmount = processedData.esGmxBalance; - } - - setIsVesterDepositModalVisible(true); - setVesterDepositTitle(t`Affiliate Vault`); - - setVesterDepositMaxAmount(remainingVestableAmount); - setVesterDepositBalance(processedData?.esGmxBalance); - setVesterDepositVestedAmount(vestingData?.affiliateVester.vestedAmount); - setVesterDepositMaxVestableAmount(vestingData?.affiliateVester.maxVestableAmount); - setVesterDepositAverageStakedAmount(vestingData?.affiliateVester.averageStakedAmount); - - setVesterDepositReserveAmount(undefined); - setVesterDepositValue(""); - - setVesterDepositAddress(affiliateVesterAddress); - } - - function showAffiliateVesterClaimModal() { - if (vestingData?.affiliateVesterClaimable === undefined || vestingData?.affiliateVesterClaimable <= 0) { - helperToast.error(t`You have no GMX tokens to claim.`); - return; - } - setIsAffiliateClaimModalVisible(true); - } - - return ( - <> - - - - -
    - - Convert esGMX tokens to GMX tokens. -
    - Please read the{" "} - - vesting details - {" "} - before using the vaults. - - } - /> -
    -
    -
    -
    -
    - GMX - GMX Vault -
    -
    -
    -
    -
    -
    - Staked Tokens -
    -
    - - - - - - } - /> -
    -
    -
    -
    - Reserved for Vesting -
    -
    - {formatAmount(reservedAmount, 18, 2, true)} / {formatAmount(totalRewardTokens, 18, 2, true)} -
    -
    -
    -
    - Vesting Status -
    -
    - - - {formatKeyAmount(vestingData, "gmxVesterClaimSum", 18, 4, true)} tokens have been converted - to GMX from the {formatKeyAmount(vestingData, "gmxVesterVestedAmount", 18, 4, true)} esGMX - deposited for vesting. - -
    - } - /> -
    -
    -
    -
    - Claimable -
    -
    - - {formatKeyAmount(vestingData, "gmxVesterClaimable", 18, 4, true)} GMX tokens can be claimed, - use the options under the Total Rewards section to claim them. - - } - /> -
    -
    -
    -
    - {!active && ( - - )} - {active && ( - - )} - {active && ( - - )} -
    -
    -
    -
    -
    -
    - GLP - GLP Vault -
    -
    -
    -
    -
    -
    - Staked Tokens -
    -
    {formatAmount(processedData?.glpBalance, 18, 2, true)} GLP
    -
    -
    -
    - Reserved for Vesting -
    -
    - {formatKeyAmount(vestingData, "glpVesterPairAmount", 18, 2, true)} /{" "} - {formatAmount(processedData?.glpBalance, 18, 2, true)} -
    -
    -
    -
    - Vesting Status -
    -
    - - - {formatKeyAmount(vestingData, "glpVesterClaimSum", 18, 4, true)} tokens have been converted - to GMX from the {formatKeyAmount(vestingData, "glpVesterVestedAmount", 18, 4, true)} esGMX - deposited for vesting. - -
    - } - /> -
    -
    -
    -
    - Claimable -
    -
    - - {formatKeyAmount(vestingData, "glpVesterClaimable", 18, 4, true)} GMX tokens can be claimed, - use the options under the Total Rewards section to claim them. - - } - /> -
    -
    -
    -
    - {!active && ( - - )} - {active && ( - - )} - {active && ( - - )} -
    -
    -
    - {(vestingData?.affiliateVesterMaxVestableAmount && vestingData?.affiliateVesterMaxVestableAmount > 0 && ( -
    -
    -
    - GLP - Affiliate Vault -
    -
    -
    -
    -
    -
    - Vesting Status -
    -
    - - - {formatKeyAmount(vestingData, "affiliateVesterClaimSum", 18, 4, true)} tokens have been - converted to GMX from the{" "} - {formatKeyAmount(vestingData, "affiliateVesterVestedAmount", 18, 4, true)} esGMX deposited - for vesting. - -
    - } - /> -
    -
    -
    -
    - Claimable -
    -
    {formatKeyAmount(vestingData, "affiliateVesterClaimable", 18, 4, true)}
    -
    -
    -
    - {!active && ( - - )} - {active && ( - - )} - {active && ( - - )} - {active && ( - - )} -
    -
    -
    - )) || - null} -
    -
    -
    - - ); -} diff --git a/src/pages/Stake/constants.ts b/src/pages/Stake/constants.ts deleted file mode 100644 index 65ac31e0fa..0000000000 --- a/src/pages/Stake/constants.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const GMX_DAO_LINKS = { - VOTING_POWER: "https://www.tally.xyz/gov/gmx/my-voting-power", - DELEGATES: "https://www.tally.xyz/gov/gmx/delegates", -}; - -export const getGmxDAODelegateLink = (address: string) => `https://www.tally.xyz/gov/gmx/delegate/${address}`; diff --git a/src/pages/Stake/useProcessedData.ts b/src/pages/Stake/useProcessedData.ts deleted file mode 100644 index 77a1dd7a50..0000000000 --- a/src/pages/Stake/useProcessedData.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { useMemo } from "react"; -import useSWR from "swr"; - -import { getServerUrl } from "config/backend"; -import { ARBITRUM } from "config/chains"; -import { getContract } from "config/contracts"; -import { useGmxPrice } from "domain/legacy"; -import useVestingData from "domain/vesting/useVestingData"; -import { useChainId } from "lib/chains"; -import { contractFetcher } from "lib/contracts"; -import { - PLACEHOLDER_ACCOUNT, - getBalanceAndSupplyData, - getDepositBalanceData, - getProcessedData, - getStakingData, -} from "lib/legacy"; -import useWallet from "lib/wallets/useWallet"; - -export function useProcessedData() { - const { active, signer, account } = useWallet(); - const { chainId } = useChainId(); - - const gmxSupplyUrl = getServerUrl(chainId, "/gmx_supply"); - - const glpManagerAddress = getContract(chainId, "GlpManager"); - const vaultAddress = getContract(chainId, "Vault"); - const nativeTokenAddress = getContract(chainId, "NATIVE_TOKEN"); - const rewardReaderAddress = getContract(chainId, "RewardReader"); - const readerAddress = getContract(chainId, "Reader"); - - const gmxAddress = getContract(chainId, "GMX"); - const esGmxAddress = getContract(chainId, "ES_GMX"); - const bnGmxAddress = getContract(chainId, "BN_GMX"); - const glpAddress = getContract(chainId, "GLP"); - - const stakedGmxTrackerAddress = getContract(chainId, "StakedGmxTracker"); - const bonusGmxTrackerAddress = getContract(chainId, "BonusGmxTracker"); - const feeGmxTrackerAddress = getContract(chainId, "FeeGmxTracker"); - - const stakedGlpTrackerAddress = getContract(chainId, "StakedGlpTracker"); - const feeGlpTrackerAddress = getContract(chainId, "FeeGlpTracker"); - const extendedGmxTrackerAddress = getContract(chainId, "ExtendedGmxTracker"); - - const walletTokens = [gmxAddress, esGmxAddress, glpAddress, stakedGmxTrackerAddress]; - const depositTokens = [ - gmxAddress, - esGmxAddress, - stakedGmxTrackerAddress, - extendedGmxTrackerAddress, - bnGmxAddress, - glpAddress, - ]; - const rewardTrackersForDepositBalances = [ - stakedGmxTrackerAddress, - stakedGmxTrackerAddress, - bonusGmxTrackerAddress, - feeGmxTrackerAddress, - feeGmxTrackerAddress, - feeGlpTrackerAddress, - ]; - const rewardTrackersForStakingInfo = [ - stakedGmxTrackerAddress, - bonusGmxTrackerAddress, - feeGmxTrackerAddress, - stakedGlpTrackerAddress, - feeGlpTrackerAddress, - extendedGmxTrackerAddress, - ]; - - const vestingData = useVestingData(account); - - const { gmxPrice } = useGmxPrice(chainId, { arbitrum: chainId === ARBITRUM ? signer : undefined }, active); - - const { data: stakedGmxSupply } = useSWR( - [`StakeV2:stakedGmxSupply:${active}`, chainId, gmxAddress, "balanceOf", stakedGmxTrackerAddress], - { - fetcher: contractFetcher(signer, "Token"), - } - ); - - const { data: aum } = useSWR( - [`processedData:getAums:${active}`, chainId, glpManagerAddress, "getAums"], - { - fetcher: async (key: any[]) => { - const aums = await contractFetcher(signer, "GlpManager")(key); - - let aum: bigint | undefined; - if (aums && aums.length > 0) { - aum = (aums[0] + aums[1]) / 2n; - } - return aum; - }, - } - ); - - const { data: nativeTokenPrice } = useSWR( - [`StakeV2:nativeTokenPrice:${active}`, chainId, vaultAddress, "getMinPrice", nativeTokenAddress], - { - fetcher: contractFetcher(signer, "Vault"), - } - ); - - const { data: gmxSupply } = useSWR([gmxSupplyUrl], { - fetcher: (args: [string]) => fetch(...args).then((res) => res.text()), - }); - - const balanceAndSupplyQuery = useSWR>( - [ - `processedData:walletBalances:${active}`, - chainId, - readerAddress, - "getTokenBalancesWithSupplies", - account || PLACEHOLDER_ACCOUNT, - ], - { - fetcher: async (key: any[]) => { - const walletBalances = await contractFetcher(signer, "ReaderV2", [walletTokens])(key); - return getBalanceAndSupplyData(walletBalances); - }, - } - ); - - const { balanceData, supplyData } = balanceAndSupplyQuery.data ?? {}; - - const { data: depositBalanceData } = useSWR>( - [ - `processedData:depositBalances:${active}`, - chainId, - rewardReaderAddress, - "getDepositBalances", - account || PLACEHOLDER_ACCOUNT, - ], - { - fetcher: async (key: any[]) => { - const depositBalances = await contractFetcher(signer, "RewardReader", [ - depositTokens, - rewardTrackersForDepositBalances, - ])(key); - return getDepositBalanceData(depositBalances); - }, - } - ); - - const { data: stakingData } = useSWR>( - [ - `processedData:stakingInfo:${active}`, - chainId, - rewardReaderAddress, - "getStakingInfo", - account || PLACEHOLDER_ACCOUNT, - ], - { - fetcher: async (key: any[]) => { - const stakingInfo = await contractFetcher(signer, "RewardReader", [rewardTrackersForStakingInfo])( - key - ); - return getStakingData(stakingInfo); - }, - } - ); - - const processedData = useMemo( - () => - getProcessedData( - balanceData, - supplyData, - depositBalanceData, - stakingData, - vestingData, - aum, - nativeTokenPrice, - stakedGmxSupply, - gmxPrice, - gmxSupply - ), - [ - balanceData, - supplyData, - depositBalanceData, - stakingData, - vestingData, - aum, - nativeTokenPrice, - stakedGmxSupply, - gmxPrice, - gmxSupply, - ] - ); - - return processedData; -} diff --git a/src/pages/SyntheticsPage/SyntheticsPage.tsx b/src/pages/SyntheticsPage/SyntheticsPage.tsx index 91bb78a56e..b4319b05a3 100644 --- a/src/pages/SyntheticsPage/SyntheticsPage.tsx +++ b/src/pages/SyntheticsPage/SyntheticsPage.tsx @@ -66,6 +66,8 @@ import { useIsCurtainOpen } from "components/Synthetics/TradeBox/Curtain"; import { TradeBoxResponsiveContainer } from "components/Synthetics/TradeBox/TradeBoxResponsiveContainer"; import { TradeHistory } from "components/Synthetics/TradeHistory/TradeHistory"; import { Chart } from "components/Synthetics/TVChart/Chart"; +import { ChartHeader } from "components/Synthetics/TVChart/ChartHeader"; +import { FavoriteTokensBar } from "components/Synthetics/TVChart/FavoriteTokensBar"; import Tabs from "components/Tabs/Tabs"; export type Props = { @@ -89,6 +91,8 @@ export function SyntheticsPage(p: Props) { useExternalSwapHandler(); const isMobile = useMedia("(max-width: 1100px)"); + const isSmallMobile = useMedia("(max-width: 700px)"); + const [isSettling, setIsSettling] = useState(false); const [listSection, setListSection] = useLocalStorageSerializeKey( @@ -262,13 +266,104 @@ export function SyntheticsPage(p: Props) { className={cx("Exchange page-layout", { "!pb-[333px]": isMobile, })} - > -
    - {isMobile && } -
    - - {!isMobile && ( -
    + > +
    + {isMobile && } + + + + + +
    +
    + + + {!isMobile && ( +
    +
    + +
    + {listSection === ListSection.Orders && selectedOrderKeys.length > 0 && ( + + )} + + + Chart positions + + +
    +
    + + {listSection === ListSection.Positions && ( + + )} + {listSection === ListSection.Orders && ( + + )} + {listSection === ListSection.Trades && } + {listSection === ListSection.Claims && renderClaims()} +
    + )} +
    + + {isMobile ? ( + <> +
    + +
    + {isSwap && !isTwap && ( + + )} + + ) : ( +
    + + +
    + {isSwap && !isTwap && ( + + )} +
    +
    + )} +
    + + {isMobile && ( +
    -
    - {listSection === ListSection.Orders && selectedOrderKeys.length > 0 && ( - - )} - - - Chart positions - - -
    - {listSection === ListSection.Positions && ( )}
    - - {isMobile ? ( - <> -
    - -
    - {isSwap && !isTwap && ( - - )} - - ) : ( -
    - - -
    - {isSwap && !isTwap && ( - - )} -
    -
    - )} - - {isMobile && ( -
    -
    - -
    - {listSection === ListSection.Positions && ( - - )} - {listSection === ListSection.Orders && ( - - )} - {listSection === ListSection.Trades && } - {listSection === ListSection.Claims && renderClaims()} -
    - )} -
    -
    ); } diff --git a/src/styles/Shared.scss b/src/styles/Shared.scss index 81896e0088..abd2e4a968 100644 --- a/src/styles/Shared.scss +++ b/src/styles/Shared.scss @@ -5,7 +5,6 @@ body, padding: 0; color: var(--color-white); font-size: 10px; - background-color: var(--main-bg-color); } /** @@ -73,11 +72,11 @@ input { } :root { - --main-bg-color: var(--color-slate-950); + --main-bg-color: var(--color-background); --dark-blue: var(--color-slate-800); --dark-blue-bg: var(--color-slate-800); --dark-blue-accent: var(--color-slate-100); - --primary-btn-bg: var(--color-blue-600); + --primary-btn-bg: var(--color-green-600); --primary-btn-hover: var(--color-blue-400); --primary-btn-active: color-mix(in srgb, var(--color-blue-300), var(--color-blue-400) 90%); --dark-blue-hover: var(--color-cold-blue-900); @@ -374,7 +373,6 @@ button.btn-link:hover .logout-icon { min-height: calc(100vh - 62px); display: flex; flex-direction: column; - padding-bottom: calc(4.7rem + 8rem); @media (max-width: 1024px) { padding-bottom: calc(4.7rem + 14rem); diff --git a/tailwind.config.js b/tailwind.config.js index f457c6dcfe..e7e1f64198 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -30,9 +30,9 @@ const colors = { 100: "#a0a3c4", 500: "#3e4361", 600: "#373c58", - 700: "#23263b", + 700: "rgba(216, 210, 255, 0.06)", 750: "#17182c", - 800: "#16182e", + 800: "rgba(235, 232, 255, 0.03)", 900: "#101124", 950: "#08091b", }, @@ -62,12 +62,13 @@ const colors = { 300: "#56dba8", 400: "#8CF3CB", 500: "#0FDE8D", - 600: "#1F3445", - 700: "#0FDE8D", - 800: "#178969", + 600: "#00D1CD", + 700: "rgba(1, 209, 150, 1)", + 800: "rgba(0, 213, 145, 0.30)", }, white: "#ffffff", black: "#000000", + background: "#050E16", stroke: { primary: "#252A47", }, diff --git a/yarn.lock b/yarn.lock index 7e87fa51e1..99fb9e957f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10637,7 +10637,7 @@ __metadata: react-dom: 18.2.0 react-helmet: 6.1.0 react-hot-toast: 2.2.0 - react-icons: 4.3.1 + react-icons: 5.5.0 react-jazzicon: 1.0.4 react-loading-skeleton: 3.3.1 react-remove-scroll: 2.5.5 @@ -14340,12 +14340,12 @@ __metadata: languageName: node linkType: hard -"react-icons@npm:4.3.1": - version: 4.3.1 - resolution: "react-icons@npm:4.3.1" +"react-icons@npm:5.5.0": + version: 5.5.0 + resolution: "react-icons@npm:5.5.0" peerDependencies: react: "*" - checksum: 2cc608acdd3e906aa3aaee290b8398220639677c949382d86e554020e5dab6a9f1ed7484418aa5272cb5c5c018e1fc9a323d433d901697a76530029f9ecb1263 + checksum: cbd74f4b7982e6e18d59798a6b578268c8eb0909d78d87bcf9b25f99b3e544fd189a76551cb5e770d17f154a60b668551aee108aaf8471309b23f7af3b2c5b07 languageName: node linkType: hard