Commit e584a5fa authored by Vignesh Mohankumar's avatar Vignesh Mohankumar Committed by GitHub

chore: removes unused Liquidity Mining code (#5451)

* feat: remove unused liquidity mining pages

* some changes

* rm more
parent 332ef6e6
import styled, { useTheme } from 'styled-components/macro'
import { ThemedText } from '../../theme'
import { AutoColumn } from '../Column'
const Wrapper = styled(AutoColumn)`
margin-right: 8px;
height: 100%;
`
const Grouping = styled(AutoColumn)`
width: fit-content;
padding: 4px;
/* background-color: ${({ theme }) => theme.backgroundInteractive}; */
border-radius: 16px;
`
const Circle = styled.div<{ confirmed?: boolean; disabled?: boolean }>`
width: 48px;
height: 48px;
background-color: ${({ theme, confirmed, disabled }) =>
disabled ? theme.deprecated_bg3 : confirmed ? theme.accentSuccess : theme.accentAction};
border-radius: 50%;
color: ${({ theme, disabled }) => (disabled ? theme.textTertiary : theme.textPrimary)};
display: flex;
align-items: center;
justify-content: center;
line-height: 8px;
font-size: 16px;
padding: 1rem;
`
const CircleRow = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`
interface ProgressCirclesProps {
steps: boolean[]
disabled?: boolean
}
/**
* Based on array of steps, create a step counter of circles.
* A circle can be enabled, disabled, or confirmed. States are derived
* from previous step.
*
* An extra circle is added to represent the ability to swap, add, or remove.
* This step will never be marked as complete (because no 'txn done' state in body ui).
*
* @param steps array of booleans where true means step is complete
*/
export default function ProgressCircles({ steps, disabled = false, ...rest }: ProgressCirclesProps) {
const theme = useTheme()
return (
<Wrapper justify="center" {...rest}>
<Grouping>
{steps.map((step, i) => {
return (
<CircleRow key={i}>
<Circle confirmed={step} disabled={disabled || (!steps[i - 1] && i !== 0)}>
{step ? '' : i + 1 + '.'}
</Circle>
<ThemedText.DeprecatedMain color={theme.deprecated_text4}>|</ThemedText.DeprecatedMain>
</CircleRow>
)
})}
<Circle disabled={disabled || !steps[steps.length - 1]}>{steps.length + 1 + '.'}</Circle>
</Grouping>
</Wrapper>
)
}
import type { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import StakingRewardsJson from '@uniswap/liquidity-staker/build/StakingRewards.json'
import { useWeb3React } from '@web3-react/core'
import { ReactNode, useState } from 'react'
import styled from 'styled-components/macro'
import { useContract } from '../../hooks/useContract'
import { StakingInfo } from '../../state/stake/hooks'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { TransactionType } from '../../state/transactions/types'
import { CloseIcon, ThemedText } from '../../theme'
import { ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import Modal from '../Modal'
import { LoadingView, SubmittedView } from '../ModalViews'
import { RowBetween } from '../Row'
const { abi: STAKING_REWARDS_ABI } = StakingRewardsJson
function useStakingContract(stakingAddress?: string, withSignerIfPossible?: boolean) {
return useContract(stakingAddress, STAKING_REWARDS_ABI, withSignerIfPossible)
}
const ContentWrapper = styled(AutoColumn)`
width: 100%;
padding: 1rem;
`
interface StakingModalProps {
isOpen: boolean
onDismiss: () => void
stakingInfo: StakingInfo
}
export default function ClaimRewardModal({ isOpen, onDismiss, stakingInfo }: StakingModalProps) {
const { account } = useWeb3React()
// monitor call to help UI loading state
const addTransaction = useTransactionAdder()
const [hash, setHash] = useState<string | undefined>()
const [attempting, setAttempting] = useState(false)
function wrappedOnDismiss() {
setHash(undefined)
setAttempting(false)
onDismiss()
}
const stakingContract = useStakingContract(stakingInfo.stakingRewardAddress)
async function onClaimReward() {
if (stakingContract && stakingInfo?.stakedAmount && account) {
setAttempting(true)
await stakingContract
.getReward({ gasLimit: 350000 })
.then((response: TransactionResponse) => {
addTransaction(response, {
type: TransactionType.CLAIM,
recipient: account,
})
setHash(response.hash)
})
.catch((error: any) => {
setAttempting(false)
console.log(error)
})
}
}
let error: ReactNode | undefined
if (!account) {
error = <Trans>Connect Wallet</Trans>
}
if (!stakingInfo?.stakedAmount) {
error = error ?? <Trans>Enter an amount</Trans>
}
return (
<Modal isOpen={isOpen} onDismiss={wrappedOnDismiss} maxHeight={90}>
{!attempting && !hash && (
<ContentWrapper gap="lg">
<RowBetween>
<ThemedText.DeprecatedMediumHeader>
<Trans>Claim</Trans>
</ThemedText.DeprecatedMediumHeader>
<CloseIcon onClick={wrappedOnDismiss} />
</RowBetween>
{stakingInfo?.earnedAmount && (
<AutoColumn justify="center" gap="md">
<ThemedText.HeadlineLarge>{stakingInfo?.earnedAmount?.toSignificant(6)}</ThemedText.HeadlineLarge>
<ThemedText.DeprecatedBody>
<Trans>Unclaimed UNI</Trans>
</ThemedText.DeprecatedBody>
</AutoColumn>
)}
<ThemedText.DeprecatedSubHeader style={{ textAlign: 'center' }}>
<Trans>When you claim without withdrawing your liquidity remains in the mining pool.</Trans>
</ThemedText.DeprecatedSubHeader>
<ButtonError disabled={!!error} error={!!error && !!stakingInfo?.stakedAmount} onClick={onClaimReward}>
{error ?? <Trans>Claim</Trans>}
</ButtonError>
</ContentWrapper>
)}
{attempting && !hash && (
<LoadingView onDismiss={wrappedOnDismiss}>
<AutoColumn gap="md" justify="center">
<ThemedText.DeprecatedBody fontSize={20}>
<Trans>Claiming {stakingInfo?.earnedAmount?.toSignificant(6)} UNI</Trans>
</ThemedText.DeprecatedBody>
</AutoColumn>
</LoadingView>
)}
{hash && (
<SubmittedView onDismiss={wrappedOnDismiss} hash={hash}>
<AutoColumn gap="md" justify="center">
<ThemedText.DeprecatedLargeHeader>
<Trans>Transaction Submitted</Trans>
</ThemedText.DeprecatedLargeHeader>
<ThemedText.DeprecatedBody fontSize={20}>
<Trans>Claimed UNI!</Trans>
</ThemedText.DeprecatedBody>
</AutoColumn>
</SubmittedView>
)}
</Modal>
)
}
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import JSBI from 'jsbi'
import styled from 'styled-components/macro'
import { BIG_INT_SECONDS_IN_WEEK } from '../../constants/misc'
import { useColor } from '../../hooks/useColor'
import useStablecoinPrice from '../../hooks/useStablecoinPrice'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useV2Pair } from '../../hooks/useV2Pairs'
import { StakingInfo } from '../../state/stake/hooks'
import { StyledInternalLink, ThemedText } from '../../theme'
import { currencyId } from '../../utils/currencyId'
import { unwrappedToken } from '../../utils/unwrappedToken'
import { ButtonPrimary } from '../Button'
import { AutoColumn } from '../Column'
import DoubleCurrencyLogo from '../DoubleLogo'
import { RowBetween } from '../Row'
import { Break, CardBGImage, CardNoise } from './styled'
const StatContainer = styled.div`
display: flex;
justify-content: space-between;
flex-direction: column;
gap: 12px;
margin-bottom: 1rem;
margin-right: 1rem;
margin-left: 1rem;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
display: none;
`};
`
const Wrapper = styled(AutoColumn)<{ showBackground: boolean; bgColor: any }>`
border-radius: 12px;
width: 100%;
overflow: hidden;
position: relative;
opacity: ${({ showBackground }) => (showBackground ? '1' : '1')};
background: ${({ theme, bgColor, showBackground }) =>
`radial-gradient(91.85% 100% at 1.84% 0%, ${bgColor} 0%, ${
showBackground ? theme.black : theme.deprecated_bg5
} 100%) `};
color: ${({ theme, showBackground }) => (showBackground ? theme.white : theme.textPrimary)} !important;
${({ showBackground }) =>
showBackground &&
` box-shadow: 0px 0px 1px rgba(0, 0, 0, 0.01), 0px 4px 8px rgba(0, 0, 0, 0.04), 0px 16px 24px rgba(0, 0, 0, 0.04),
0px 24px 32px rgba(0, 0, 0, 0.01);`}
`
const TopSection = styled.div`
display: grid;
grid-template-columns: 48px 1fr 120px;
grid-gap: 0px;
align-items: center;
padding: 1rem;
z-index: 1;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
grid-template-columns: 48px 1fr 96px;
`};
`
const BottomSection = styled.div<{ showBackground: boolean }>`
padding: 12px 16px;
opacity: ${({ showBackground }) => (showBackground ? '1' : '0.4')};
border-radius: 0 0 12px 12px;
display: flex;
flex-direction: row;
align-items: baseline;
justify-content: space-between;
z-index: 1;
`
export default function PoolCard({ stakingInfo }: { stakingInfo: StakingInfo }) {
const token0 = stakingInfo.tokens[0]
const token1 = stakingInfo.tokens[1]
const currency0 = unwrappedToken(token0)
const currency1 = unwrappedToken(token1)
const isStaking = Boolean(stakingInfo.stakedAmount.greaterThan('0'))
// get the color of the token
const token = currency0.isNative ? token1 : token0
const WETH = currency0.isNative ? token0 : token1
const backgroundColor = useColor(token)
const totalSupplyOfStakingToken = useTotalSupply(stakingInfo.stakedAmount.currency)
const [, stakingTokenPair] = useV2Pair(...stakingInfo.tokens)
// let returnOverMonth: Percent = new Percent('0')
let valueOfTotalStakedAmountInWETH: CurrencyAmount<Token> | undefined
if (totalSupplyOfStakingToken && stakingTokenPair) {
// take the total amount of LP tokens staked, multiply by ETH value of all LP tokens, divide by all LP tokens
valueOfTotalStakedAmountInWETH = CurrencyAmount.fromRawAmount(
WETH,
JSBI.divide(
JSBI.multiply(
JSBI.multiply(stakingInfo.totalStakedAmount.quotient, stakingTokenPair.reserveOf(WETH).quotient),
JSBI.BigInt(2) // this is b/c the value of LP shares are ~double the value of the WETH they entitle owner to
),
totalSupplyOfStakingToken.quotient
)
)
}
// get the USD value of staked WETH
const USDPrice = useStablecoinPrice(WETH)
const valueOfTotalStakedAmountInUSDC =
valueOfTotalStakedAmountInWETH && USDPrice?.quote(valueOfTotalStakedAmountInWETH)
return (
<Wrapper showBackground={isStaking} bgColor={backgroundColor}>
<CardBGImage desaturate />
<CardNoise />
<TopSection>
<DoubleCurrencyLogo currency0={currency0} currency1={currency1} size={24} />
<ThemedText.DeprecatedWhite fontWeight={600} fontSize={24} style={{ marginLeft: '8px' }}>
{currency0.symbol}-{currency1.symbol}
</ThemedText.DeprecatedWhite>
<StyledInternalLink to={`/uni/${currencyId(currency0)}/${currencyId(currency1)}`} style={{ width: '100%' }}>
<ButtonPrimary padding="8px" $borderRadius="8px">
{isStaking ? <Trans>Manage</Trans> : <Trans>Deposit</Trans>}
</ButtonPrimary>
</StyledInternalLink>
</TopSection>
<StatContainer>
<RowBetween>
<ThemedText.DeprecatedWhite>
<Trans>Total deposited</Trans>
</ThemedText.DeprecatedWhite>
<ThemedText.DeprecatedWhite>
{valueOfTotalStakedAmountInUSDC ? (
<Trans>${valueOfTotalStakedAmountInUSDC.toFixed(0, { groupSeparator: ',' })}</Trans>
) : (
<Trans>{valueOfTotalStakedAmountInWETH?.toSignificant(4, { groupSeparator: ',' }) ?? '-'} ETH</Trans>
)}
</ThemedText.DeprecatedWhite>
</RowBetween>
<RowBetween>
<ThemedText.DeprecatedWhite>
<Trans>Pool rate</Trans>
</ThemedText.DeprecatedWhite>
<ThemedText.DeprecatedWhite>
{stakingInfo ? (
stakingInfo.active ? (
<Trans>
{stakingInfo.totalRewardRate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toFixed(0, { groupSeparator: ',' })}{' '}
UNI / week
</Trans>
) : (
<Trans>0 UNI / week</Trans>
)
) : (
'-'
)}
</ThemedText.DeprecatedWhite>
</RowBetween>
</StatContainer>
{isStaking && (
<>
<Break />
<BottomSection showBackground={true}>
<ThemedText.DeprecatedBlack color="white" fontWeight={500}>
<span>
<Trans>Your rate</Trans>
</span>
</ThemedText.DeprecatedBlack>
<ThemedText.DeprecatedBlack style={{ textAlign: 'right' }} color="white" fontWeight={500}>
<span role="img" aria-label="wizard-icon" style={{ marginRight: '0.5rem' }}>
</span>
{stakingInfo ? (
stakingInfo.active ? (
<Trans>
{stakingInfo.rewardRate
?.multiply(BIG_INT_SECONDS_IN_WEEK)
?.toSignificant(4, { groupSeparator: ',' })}{' '}
UNI / week
</Trans>
) : (
<Trans>0 UNI / week</Trans>
)
) : (
'-'
)}
</ThemedText.DeprecatedBlack>
</BottomSection>
</>
)}
</Wrapper>
)
}
import type { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import StakingRewardsJson from '@uniswap/liquidity-staker/build/StakingRewards.json'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk'
import { useWeb3React } from '@web3-react/core'
import { useV2LiquidityTokenPermit } from 'hooks/useV2LiquidityTokenPermit'
import { useCallback, useState } from 'react'
import styled from 'styled-components/macro'
import { ApprovalState, useApproveCallback } from '../../hooks/useApproveCallback'
import { useContract, usePairContract, useV2RouterContract } from '../../hooks/useContract'
import useTransactionDeadline from '../../hooks/useTransactionDeadline'
import { StakingInfo, useDerivedStakeInfo } from '../../state/stake/hooks'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { TransactionType } from '../../state/transactions/types'
import { CloseIcon, ThemedText } from '../../theme'
import { formatCurrencyAmount } from '../../utils/formatCurrencyAmount'
import { maxAmountSpend } from '../../utils/maxAmountSpend'
import { ButtonConfirmed, ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import CurrencyInputPanel from '../CurrencyInputPanel'
import Modal from '../Modal'
import { LoadingView, SubmittedView } from '../ModalViews'
import ProgressCircles from '../ProgressSteps'
import { RowBetween } from '../Row'
const { abi: STAKING_REWARDS_ABI } = StakingRewardsJson
function useStakingContract(stakingAddress?: string, withSignerIfPossible?: boolean) {
return useContract(stakingAddress, STAKING_REWARDS_ABI, withSignerIfPossible)
}
const HypotheticalRewardRate = styled.div<{ dim: boolean }>`
display: flex;
justify-content: space-between;
padding-right: 20px;
padding-left: 20px;
opacity: ${({ dim }) => (dim ? 0.5 : 1)};
`
const ContentWrapper = styled(AutoColumn)`
width: 100%;
padding: 1rem;
`
interface StakingModalProps {
isOpen: boolean
onDismiss: () => void
stakingInfo: StakingInfo
userLiquidityUnstaked: CurrencyAmount<Token> | undefined
}
export default function StakingModal({ isOpen, onDismiss, stakingInfo, userLiquidityUnstaked }: StakingModalProps) {
const { provider } = useWeb3React()
// track and parse user input
const [typedValue, setTypedValue] = useState('')
const { parsedAmount, error } = useDerivedStakeInfo(
typedValue,
stakingInfo.stakedAmount.currency,
userLiquidityUnstaked
)
const parsedAmountWrapped = parsedAmount?.wrapped
let hypotheticalRewardRate: CurrencyAmount<Token> = CurrencyAmount.fromRawAmount(stakingInfo.rewardRate.currency, '0')
if (parsedAmountWrapped?.greaterThan('0')) {
hypotheticalRewardRate = stakingInfo.getHypotheticalRewardRate(
stakingInfo.stakedAmount.add(parsedAmountWrapped),
stakingInfo.totalStakedAmount.add(parsedAmountWrapped),
stakingInfo.totalRewardRate
)
}
// state for pending and submitted txn views
const addTransaction = useTransactionAdder()
const [attempting, setAttempting] = useState<boolean>(false)
const [hash, setHash] = useState<string | undefined>()
const wrappedOnDismiss = useCallback(() => {
setHash(undefined)
setAttempting(false)
onDismiss()
}, [onDismiss])
// pair contract for this token to be staked
const dummyPair = new Pair(
CurrencyAmount.fromRawAmount(stakingInfo.tokens[0], '0'),
CurrencyAmount.fromRawAmount(stakingInfo.tokens[1], '0')
)
const pairContract = usePairContract(dummyPair.liquidityToken.address)
// approval data for stake
const deadline = useTransactionDeadline()
const router = useV2RouterContract()
const { signatureData, gatherPermitSignature } = useV2LiquidityTokenPermit(parsedAmountWrapped, router?.address)
const [approval, approveCallback] = useApproveCallback(parsedAmount, stakingInfo.stakingRewardAddress)
const stakingContract = useStakingContract(stakingInfo.stakingRewardAddress)
async function onStake() {
setAttempting(true)
if (stakingContract && parsedAmount && deadline) {
if (approval === ApprovalState.APPROVED) {
await stakingContract.stake(`0x${parsedAmount.quotient.toString(16)}`, { gasLimit: 350000 })
} else if (signatureData) {
stakingContract
.stakeWithPermit(
`0x${parsedAmount.quotient.toString(16)}`,
signatureData.deadline,
signatureData.v,
signatureData.r,
signatureData.s,
{ gasLimit: 350000 }
)
.then((response: TransactionResponse) => {
addTransaction(response, {
type: TransactionType.DEPOSIT_LIQUIDITY_STAKING,
token0Address: stakingInfo.tokens[0].address,
token1Address: stakingInfo.tokens[1].address,
})
setHash(response.hash)
})
.catch((error: any) => {
setAttempting(false)
console.log(error)
})
} else {
setAttempting(false)
throw new Error('Attempting to stake without approval or a signature. Please contact support.')
}
}
}
// wrapped onUserInput to clear signatures
const onUserInput = useCallback((typedValue: string) => {
setTypedValue(typedValue)
}, [])
// used for max input button
const maxAmountInput = maxAmountSpend(userLiquidityUnstaked)
const atMaxAmount = Boolean(maxAmountInput && parsedAmount?.equalTo(maxAmountInput))
const handleMax = useCallback(() => {
maxAmountInput && onUserInput(maxAmountInput.toExact())
}, [maxAmountInput, onUserInput])
async function onAttemptToApprove() {
if (!pairContract || !provider || !deadline) throw new Error('missing dependencies')
if (!parsedAmount) throw new Error('missing liquidity amount')
if (gatherPermitSignature) {
try {
await gatherPermitSignature()
} catch (error) {
// try to approve if gatherPermitSignature failed for any reason other than the user rejecting it
if (error?.code !== 4001) {
await approveCallback()
}
}
} else {
await approveCallback()
}
}
return (
<Modal isOpen={isOpen} onDismiss={wrappedOnDismiss} maxHeight={90}>
{!attempting && !hash && (
<ContentWrapper gap="lg">
<RowBetween>
<ThemedText.DeprecatedMediumHeader>
<Trans>Deposit</Trans>
</ThemedText.DeprecatedMediumHeader>
<CloseIcon onClick={wrappedOnDismiss} />
</RowBetween>
<CurrencyInputPanel
value={typedValue}
onUserInput={onUserInput}
onMax={handleMax}
showMaxButton={!atMaxAmount}
currency={stakingInfo.stakedAmount.currency}
pair={dummyPair}
label=""
renderBalance={(amount) => <Trans>Available to deposit: {formatCurrencyAmount(amount, 4)}</Trans>}
id="stake-liquidity-token"
/>
<HypotheticalRewardRate dim={!hypotheticalRewardRate.greaterThan('0')}>
<div>
<ThemedText.DeprecatedBlack fontWeight={600}>
<Trans>Weekly Rewards</Trans>
</ThemedText.DeprecatedBlack>
</div>
<ThemedText.DeprecatedBlack>
<Trans>
{hypotheticalRewardRate
.multiply((60 * 60 * 24 * 7).toString())
.toSignificant(4, { groupSeparator: ',' })}{' '}
UNI / week
</Trans>
</ThemedText.DeprecatedBlack>
</HypotheticalRewardRate>
<RowBetween>
<ButtonConfirmed
mr="0.5rem"
onClick={onAttemptToApprove}
confirmed={approval === ApprovalState.APPROVED || signatureData !== null}
disabled={approval !== ApprovalState.NOT_APPROVED || signatureData !== null}
>
<Trans>Approve</Trans>
</ButtonConfirmed>
<ButtonError
disabled={!!error || (signatureData === null && approval !== ApprovalState.APPROVED)}
error={!!error && !!parsedAmount}
onClick={onStake}
>
{error ?? <Trans>Deposit</Trans>}
</ButtonError>
</RowBetween>
<ProgressCircles steps={[approval === ApprovalState.APPROVED || signatureData !== null]} disabled={true} />
</ContentWrapper>
)}
{attempting && !hash && (
<LoadingView onDismiss={wrappedOnDismiss}>
<AutoColumn gap="md" justify="center">
<ThemedText.DeprecatedLargeHeader>
<Trans>Depositing Liquidity</Trans>
</ThemedText.DeprecatedLargeHeader>
<ThemedText.DeprecatedBody fontSize={20}>
<Trans>{parsedAmount?.toSignificant(4)} UNI-V2</Trans>
</ThemedText.DeprecatedBody>
</AutoColumn>
</LoadingView>
)}
{attempting && hash && (
<SubmittedView onDismiss={wrappedOnDismiss} hash={hash}>
<AutoColumn gap="md" justify="center">
<ThemedText.DeprecatedLargeHeader>
<Trans>Transaction Submitted</Trans>
</ThemedText.DeprecatedLargeHeader>
<ThemedText.DeprecatedBody fontSize={20}>
<Trans>Deposited {parsedAmount?.toSignificant(4)} UNI-V2</Trans>
</ThemedText.DeprecatedBody>
</AutoColumn>
</SubmittedView>
)}
</Modal>
)
}
import type { TransactionResponse } from '@ethersproject/providers'
import { Trans } from '@lingui/macro'
import StakingRewardsJson from '@uniswap/liquidity-staker/build/StakingRewards.json'
import { useWeb3React } from '@web3-react/core'
import { ReactNode, useState } from 'react'
import styled from 'styled-components/macro'
import { useContract } from '../../hooks/useContract'
import { StakingInfo } from '../../state/stake/hooks'
import { useTransactionAdder } from '../../state/transactions/hooks'
import { TransactionType } from '../../state/transactions/types'
import { CloseIcon, ThemedText } from '../../theme'
import { ButtonError } from '../Button'
import { AutoColumn } from '../Column'
import FormattedCurrencyAmount from '../FormattedCurrencyAmount'
import Modal from '../Modal'
import { LoadingView, SubmittedView } from '../ModalViews'
import { RowBetween } from '../Row'
const { abi: STAKING_REWARDS_ABI } = StakingRewardsJson
function useStakingContract(stakingAddress?: string, withSignerIfPossible?: boolean) {
return useContract(stakingAddress, STAKING_REWARDS_ABI, withSignerIfPossible)
}
const ContentWrapper = styled(AutoColumn)`
width: 100%;
padding: 1rem;
`
interface StakingModalProps {
isOpen: boolean
onDismiss: () => void
stakingInfo: StakingInfo
}
export default function UnstakingModal({ isOpen, onDismiss, stakingInfo }: StakingModalProps) {
const { account } = useWeb3React()
// monitor call to help UI loading state
const addTransaction = useTransactionAdder()
const [hash, setHash] = useState<string | undefined>()
const [attempting, setAttempting] = useState(false)
function wrappedOnDismiss() {
setHash(undefined)
setAttempting(false)
onDismiss()
}
const stakingContract = useStakingContract(stakingInfo.stakingRewardAddress)
async function onWithdraw() {
if (stakingContract && stakingInfo?.stakedAmount) {
setAttempting(true)
await stakingContract
.exit({ gasLimit: 300000 })
.then((response: TransactionResponse) => {
addTransaction(response, {
type: TransactionType.WITHDRAW_LIQUIDITY_STAKING,
token0Address: stakingInfo.tokens[0].address,
token1Address: stakingInfo.tokens[1].address,
})
setHash(response.hash)
})
.catch((error: any) => {
setAttempting(false)
console.log(error)
})
}
}
let error: ReactNode | undefined
if (!account) {
error = <Trans>Connect a wallet</Trans>
}
if (!stakingInfo?.stakedAmount) {
error = error ?? <Trans>Enter an amount</Trans>
}
return (
<Modal isOpen={isOpen} onDismiss={wrappedOnDismiss} maxHeight={90}>
{!attempting && !hash && (
<ContentWrapper gap="lg">
<RowBetween>
<ThemedText.DeprecatedMediumHeader>
<Trans>Withdraw</Trans>
</ThemedText.DeprecatedMediumHeader>
<CloseIcon onClick={wrappedOnDismiss} />
</RowBetween>
{stakingInfo?.stakedAmount && (
<AutoColumn justify="center" gap="md">
<ThemedText.HeadlineLarge>
<FormattedCurrencyAmount currencyAmount={stakingInfo.stakedAmount} />
</ThemedText.HeadlineLarge>
<ThemedText.DeprecatedBody>
<Trans>Deposited liquidity:</Trans>
</ThemedText.DeprecatedBody>
</AutoColumn>
)}
{stakingInfo?.earnedAmount && (
<AutoColumn justify="center" gap="md">
<ThemedText.HeadlineLarge>
<FormattedCurrencyAmount currencyAmount={stakingInfo?.earnedAmount} />
</ThemedText.HeadlineLarge>
<ThemedText.DeprecatedBody>
<Trans>Unclaimed UNI</Trans>
</ThemedText.DeprecatedBody>
</AutoColumn>
)}
<ThemedText.DeprecatedSubHeader style={{ textAlign: 'center' }}>
<Trans>When you withdraw, your UNI is claimed and your liquidity is removed from the mining pool.</Trans>
</ThemedText.DeprecatedSubHeader>
<ButtonError disabled={!!error} error={!!error && !!stakingInfo?.stakedAmount} onClick={onWithdraw}>
{error ?? <Trans>Withdraw & Claim</Trans>}
</ButtonError>
</ContentWrapper>
)}
{attempting && !hash && (
<LoadingView onDismiss={wrappedOnDismiss}>
<AutoColumn gap="md" justify="center">
<ThemedText.DeprecatedBody fontSize={20}>
<Trans>Withdrawing {stakingInfo?.stakedAmount?.toSignificant(4)} UNI-V2</Trans>
</ThemedText.DeprecatedBody>
<ThemedText.DeprecatedBody fontSize={20}>
<Trans>Claiming {stakingInfo?.earnedAmount?.toSignificant(4)} UNI</Trans>
</ThemedText.DeprecatedBody>
</AutoColumn>
</LoadingView>
)}
{hash && (
<SubmittedView onDismiss={wrappedOnDismiss} hash={hash}>
<AutoColumn gap="md" justify="center">
<ThemedText.DeprecatedLargeHeader>
<Trans>Transaction Submitted</Trans>
</ThemedText.DeprecatedLargeHeader>
<ThemedText.DeprecatedBody fontSize={20}>
<Trans>Withdrew UNI-V2!</Trans>
</ThemedText.DeprecatedBody>
<ThemedText.DeprecatedBody fontSize={20}>
<Trans>Claimed UNI!</Trans>
</ThemedText.DeprecatedBody>
</AutoColumn>
</SubmittedView>
)}
</Modal>
)
}
...@@ -11,9 +11,6 @@ export const L2_DEADLINE_FROM_NOW = 60 * 5 ...@@ -11,9 +11,6 @@ export const L2_DEADLINE_FROM_NOW = 60 * 5
export const DEFAULT_TXN_DISMISS_MS = 25000 export const DEFAULT_TXN_DISMISS_MS = 25000
export const L2_TXN_DISMISS_MS = 5000 export const L2_TXN_DISMISS_MS = 5000
// used for rewards deadlines
export const BIG_INT_SECONDS_IN_WEEK = JSBI.BigInt(60 * 60 * 24 * 7)
export const BIG_INT_ZERO = JSBI.BigInt(0) export const BIG_INT_ZERO = JSBI.BigInt(0)
// one basis JSBI.BigInt // one basis JSBI.BigInt
......
...@@ -31,8 +31,6 @@ import About from './About' ...@@ -31,8 +31,6 @@ import About from './About'
import AddLiquidity from './AddLiquidity' import AddLiquidity from './AddLiquidity'
import { RedirectDuplicateTokenIds } from './AddLiquidity/redirects' import { RedirectDuplicateTokenIds } from './AddLiquidity/redirects'
import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects' import { RedirectDuplicateTokenIdsV2 } from './AddLiquidityV2/redirects'
import Earn from './Earn'
import Manage from './Earn/Manage'
import Landing from './Landing' import Landing from './Landing'
import MigrateV2 from './MigrateV2' import MigrateV2 from './MigrateV2'
import MigrateV2Pair from './MigrateV2/MigrateV2Pair' import MigrateV2Pair from './MigrateV2/MigrateV2Pair'
...@@ -218,8 +216,6 @@ export default function App() { ...@@ -218,8 +216,6 @@ export default function App() {
} }
/> />
<Route path="create-proposal" element={<Navigate to="/vote/create-proposal" replace />} /> <Route path="create-proposal" element={<Navigate to="/vote/create-proposal" replace />} />
<Route path="uni" element={<Earn />} />
<Route path="uni/:currencyIdA/:currencyIdB" element={<Manage />} />
<Route path="send" element={<RedirectPathToSwapOnly />} /> <Route path="send" element={<RedirectPathToSwapOnly />} />
<Route path="swap" element={<Swap />} /> <Route path="swap" element={<Swap />} />
......
import { useEffect, useMemo, useState } from 'react'
import { REWARDS_DURATION_DAYS, STAKING_GENESIS } from '../../state/stake/hooks'
import { ThemedText } from '../../theme'
const MINUTE = 60
const HOUR = MINUTE * 60
const DAY = HOUR * 24
const REWARDS_DURATION = DAY * REWARDS_DURATION_DAYS
export function Countdown({ exactEnd }: { exactEnd?: Date }) {
// get end/beginning times
const end = useMemo(
() => (exactEnd ? Math.floor(exactEnd.getTime() / 1000) : STAKING_GENESIS + REWARDS_DURATION),
[exactEnd]
)
const begin = useMemo(() => end - REWARDS_DURATION, [end])
// get current time
const [time, setTime] = useState(() => Math.floor(Date.now() / 1000))
useEffect((): (() => void) | void => {
// we only need to tick if rewards haven't ended yet
if (time <= end) {
const timeout = setTimeout(() => setTime(Math.floor(Date.now() / 1000)), 1000)
return () => {
clearTimeout(timeout)
}
}
}, [time, end])
const timeUntilGenesis = begin - time
const timeUntilEnd = end - time
let timeRemaining: number
let message: string
if (timeUntilGenesis >= 0) {
message = 'Rewards begin in'
timeRemaining = timeUntilGenesis
} else {
const ongoing = timeUntilEnd >= 0
if (ongoing) {
message = 'Rewards end in'
timeRemaining = timeUntilEnd
} else {
message = 'Rewards have ended!'
timeRemaining = Infinity
}
}
const days = (timeRemaining - (timeRemaining % DAY)) / DAY
timeRemaining -= days * DAY
const hours = (timeRemaining - (timeRemaining % HOUR)) / HOUR
timeRemaining -= hours * HOUR
const minutes = (timeRemaining - (timeRemaining % MINUTE)) / MINUTE
timeRemaining -= minutes * MINUTE
const seconds = timeRemaining
return (
<ThemedText.DeprecatedBlack fontWeight={400}>
{message}{' '}
{Number.isFinite(timeRemaining) && (
<code>
{`${days}:${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds
.toString()
.padStart(2, '0')}`}
</code>
)}
</ThemedText.DeprecatedBlack>
)
}
import { Trans } from '@lingui/macro'
import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { useWeb3React } from '@web3-react/core'
import JSBI from 'jsbi'
import { useCallback, useState } from 'react'
import { Link, useParams } from 'react-router-dom'
import styled from 'styled-components/macro'
import { CountUp } from 'use-count-up'
import { ButtonEmpty, ButtonPrimary } from '../../components/Button'
import { AutoColumn } from '../../components/Column'
import DoubleCurrencyLogo from '../../components/DoubleLogo'
import ClaimRewardModal from '../../components/earn/ClaimRewardModal'
import StakingModal from '../../components/earn/StakingModal'
import { CardBGImage, CardNoise, CardSection, DataCard } from '../../components/earn/styled'
import UnstakingModal from '../../components/earn/UnstakingModal'
import { RowBetween } from '../../components/Row'
import { BIG_INT_SECONDS_IN_WEEK, BIG_INT_ZERO } from '../../constants/misc'
import { useCurrency } from '../../hooks/Tokens'
import { useColor } from '../../hooks/useColor'
import usePrevious from '../../hooks/usePrevious'
import useStablecoinPrice from '../../hooks/useStablecoinPrice'
import { useTotalSupply } from '../../hooks/useTotalSupply'
import { useV2Pair } from '../../hooks/useV2Pairs'
import { useToggleWalletModal } from '../../state/application/hooks'
import { useTokenBalance } from '../../state/connection/hooks'
import { useStakingInfo } from '../../state/stake/hooks'
import { ThemedText } from '../../theme'
import { currencyId } from '../../utils/currencyId'
const PageWrapper = styled(AutoColumn)`
padding: 68px 8px 0px;
max-width: 640px;
width: 100%;
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
padding: 48px 8px 0px;
}
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
padding-top: 20px;
}
`
const PositionInfo = styled(AutoColumn)<{ dim: any }>`
position: relative;
max-width: 640px;
width: 100%;
opacity: ${({ dim }) => (dim ? 0.6 : 1)};
`
const BottomSection = styled(AutoColumn)`
border-radius: 12px;
width: 100%;
position: relative;
`
const StyledDataCard = styled(DataCard)<{ bgColor?: any; showBackground?: any }>`
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #1e1a31 0%, #3d51a5 100%);
z-index: 2;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
background: ${({ theme, bgColor, showBackground }) =>
`radial-gradient(91.85% 100% at 1.84% 0%, ${bgColor} 0%, ${
showBackground ? theme.black : theme.deprecated_bg5
} 100%) `};
`
const StyledBottomCard = styled(DataCard)<{ dim: any }>`
background: ${({ theme }) => theme.deprecated_bg3};
opacity: ${({ dim }) => (dim ? 0.4 : 1)};
margin-top: -40px;
padding: 0 1.25rem 1rem 1.25rem;
padding-top: 32px;
z-index: 1;
`
const PoolData = styled(DataCard)`
background: none;
border: 1px solid ${({ theme }) => theme.deprecated_bg4};
padding: 1rem;
z-index: 1;
`
const VoteCard = styled(DataCard)`
background: radial-gradient(76.02% 75.41% at 1.84% 0%, #27ae60 0%, #000000 100%);
overflow: hidden;
`
const DataRow = styled(RowBetween)`
justify-content: center;
gap: 12px;
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
flex-direction: column;
gap: 12px;
`};
`
export default function Manage() {
const { currencyIdA, currencyIdB } = useParams<{ currencyIdA: string; currencyIdB: string }>()
const { account } = useWeb3React()
// get currencies and pair
const [currencyA, currencyB] = [useCurrency(currencyIdA), useCurrency(currencyIdB)]
const tokenA = (currencyA ?? undefined)?.wrapped
const tokenB = (currencyB ?? undefined)?.wrapped
const [, stakingTokenPair] = useV2Pair(tokenA, tokenB)
const stakingInfo = useStakingInfo(stakingTokenPair)?.[0]
// detect existing unstaked LP position to show add button if none found
const userLiquidityUnstaked = useTokenBalance(account ?? undefined, stakingInfo?.stakedAmount?.currency)
const showAddLiquidityButton = Boolean(stakingInfo?.stakedAmount?.equalTo('0') && userLiquidityUnstaked?.equalTo('0'))
// toggle for staking modal and unstaking modal
const [showStakingModal, setShowStakingModal] = useState(false)
const [showUnstakingModal, setShowUnstakingModal] = useState(false)
const [showClaimRewardModal, setShowClaimRewardModal] = useState(false)
// fade cards if nothing staked or nothing earned yet
const disableTop = !stakingInfo?.stakedAmount || stakingInfo.stakedAmount.equalTo(JSBI.BigInt(0))
const token = currencyA?.isNative ? tokenB : tokenA
const WETH = currencyA?.isNative ? tokenA : tokenB
const backgroundColor = useColor(token)
// get WETH value of staked LP tokens
const totalSupplyOfStakingToken = useTotalSupply(stakingInfo?.stakedAmount?.currency)
let valueOfTotalStakedAmountInWETH: CurrencyAmount<Token> | undefined
if (totalSupplyOfStakingToken && stakingTokenPair && stakingInfo && WETH) {
// take the total amount of LP tokens staked, multiply by ETH value of all LP tokens, divide by all LP tokens
valueOfTotalStakedAmountInWETH = CurrencyAmount.fromRawAmount(
WETH,
JSBI.divide(
JSBI.multiply(
JSBI.multiply(stakingInfo.totalStakedAmount.quotient, stakingTokenPair.reserveOf(WETH).quotient),
JSBI.BigInt(2) // this is b/c the value of LP shares are ~double the value of the WETH they entitle owner to
),
totalSupplyOfStakingToken.quotient
)
)
}
const countUpAmount = stakingInfo?.earnedAmount?.toFixed(6) ?? '0'
const countUpAmountPrevious = usePrevious(countUpAmount) ?? '0'
// get the USD value of staked WETH
const USDPrice = useStablecoinPrice(WETH)
const valueOfTotalStakedAmountInUSDC =
valueOfTotalStakedAmountInWETH && USDPrice?.quote(valueOfTotalStakedAmountInWETH)
const toggleWalletModal = useToggleWalletModal()
const handleDepositClick = useCallback(() => {
if (account) {
setShowStakingModal(true)
} else {
toggleWalletModal()
}
}, [account, toggleWalletModal])
return (
<PageWrapper gap="lg" justify="center">
<RowBetween style={{ gap: '24px' }}>
<ThemedText.DeprecatedMediumHeader style={{ margin: 0 }}>
<Trans>
{currencyA?.symbol}-{currencyB?.symbol} Liquidity Mining
</Trans>
</ThemedText.DeprecatedMediumHeader>
<DoubleCurrencyLogo currency0={currencyA ?? undefined} currency1={currencyB ?? undefined} size={24} />
</RowBetween>
<DataRow style={{ gap: '24px' }}>
<PoolData>
<AutoColumn gap="sm">
<ThemedText.DeprecatedBody style={{ margin: 0 }}>
<Trans>Total deposits</Trans>
</ThemedText.DeprecatedBody>
<ThemedText.DeprecatedBody fontSize={24} fontWeight={500}>
{valueOfTotalStakedAmountInUSDC
? `$${valueOfTotalStakedAmountInUSDC.toFixed(0, { groupSeparator: ',' })}`
: `${valueOfTotalStakedAmountInWETH?.toSignificant(4, { groupSeparator: ',' }) ?? '-'} ETH`}
</ThemedText.DeprecatedBody>
</AutoColumn>
</PoolData>
<PoolData>
<AutoColumn gap="sm">
<ThemedText.DeprecatedBody style={{ margin: 0 }}>
<Trans>Pool Rate</Trans>
</ThemedText.DeprecatedBody>
<ThemedText.DeprecatedBody fontSize={24} fontWeight={500}>
{stakingInfo?.active ? (
<Trans>
{stakingInfo.totalRewardRate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toFixed(0, { groupSeparator: ',' })}{' '}
UNI / week
</Trans>
) : (
<Trans>0 UNI / week</Trans>
)}
</ThemedText.DeprecatedBody>
</AutoColumn>
</PoolData>
</DataRow>
{showAddLiquidityButton && (
<VoteCard>
<CardBGImage />
<CardNoise />
<CardSection>
<AutoColumn gap="md">
<RowBetween>
<ThemedText.DeprecatedWhite fontWeight={600}>
<Trans>Step 1. Get UNI-V2 Liquidity tokens</Trans>
</ThemedText.DeprecatedWhite>
</RowBetween>
<RowBetween style={{ marginBottom: '1rem' }}>
<ThemedText.DeprecatedWhite fontSize={14}>
<Trans>
UNI-V2 LP tokens are required. Once you&apos;ve added liquidity to the {currencyA?.symbol}-
{currencyB?.symbol} pool you can stake your liquidity tokens on this page.
</Trans>
</ThemedText.DeprecatedWhite>
</RowBetween>
<ButtonPrimary
padding="8px"
$borderRadius="8px"
width="fit-content"
as={Link}
to={`/add/${currencyA && currencyId(currencyA)}/${currencyB && currencyId(currencyB)}`}
>
<Trans>
Add {currencyA?.symbol}-{currencyB?.symbol} liquidity
</Trans>
</ButtonPrimary>
</AutoColumn>
</CardSection>
<CardBGImage />
<CardNoise />
</VoteCard>
)}
{stakingInfo && (
<>
<StakingModal
isOpen={showStakingModal}
onDismiss={() => setShowStakingModal(false)}
stakingInfo={stakingInfo}
userLiquidityUnstaked={userLiquidityUnstaked}
/>
<UnstakingModal
isOpen={showUnstakingModal}
onDismiss={() => setShowUnstakingModal(false)}
stakingInfo={stakingInfo}
/>
<ClaimRewardModal
isOpen={showClaimRewardModal}
onDismiss={() => setShowClaimRewardModal(false)}
stakingInfo={stakingInfo}
/>
</>
)}
<PositionInfo gap="lg" justify="center" dim={showAddLiquidityButton}>
<BottomSection gap="lg" justify="center">
<StyledDataCard disabled={disableTop} bgColor={backgroundColor} showBackground={!showAddLiquidityButton}>
<CardSection>
<CardBGImage desaturate />
<CardNoise />
<AutoColumn gap="md">
<RowBetween>
<ThemedText.DeprecatedWhite fontWeight={600}>
<Trans>Your liquidity deposits</Trans>
</ThemedText.DeprecatedWhite>
</RowBetween>
<RowBetween style={{ alignItems: 'baseline' }}>
<ThemedText.DeprecatedWhite fontSize={36} fontWeight={600}>
{stakingInfo?.stakedAmount?.toSignificant(6) ?? '-'}
</ThemedText.DeprecatedWhite>
<ThemedText.DeprecatedWhite>
<Trans>
UNI-V2 {currencyA?.symbol}-{currencyB?.symbol}
</Trans>
</ThemedText.DeprecatedWhite>
</RowBetween>
</AutoColumn>
</CardSection>
</StyledDataCard>
<StyledBottomCard dim={stakingInfo?.stakedAmount?.equalTo(JSBI.BigInt(0))}>
<CardBGImage desaturate />
<CardNoise />
<AutoColumn gap="sm">
<RowBetween>
<div>
<ThemedText.DeprecatedBlack>
<Trans>Your unclaimed UNI</Trans>
</ThemedText.DeprecatedBlack>
</div>
{stakingInfo?.earnedAmount && JSBI.notEqual(BIG_INT_ZERO, stakingInfo?.earnedAmount?.quotient) && (
<ButtonEmpty
padding="8px"
$borderRadius="8px"
width="fit-content"
onClick={() => setShowClaimRewardModal(true)}
>
<Trans>Claim</Trans>
</ButtonEmpty>
)}
</RowBetween>
<RowBetween style={{ alignItems: 'baseline' }}>
<ThemedText.DeprecatedLargeHeader fontSize={36} fontWeight={600}>
<CountUp
key={countUpAmount}
isCounting
decimalPlaces={4}
start={parseFloat(countUpAmountPrevious)}
end={parseFloat(countUpAmount)}
thousandsSeparator=","
duration={1}
/>
</ThemedText.DeprecatedLargeHeader>
<ThemedText.DeprecatedBlack fontSize={16} fontWeight={500}>
<span role="img" aria-label="wizard-icon" style={{ marginRight: '8px ' }}>
</span>
{stakingInfo?.active ? (
<Trans>
{stakingInfo.rewardRate?.multiply(BIG_INT_SECONDS_IN_WEEK)?.toFixed(0, { groupSeparator: ',' })}{' '}
UNI / week
</Trans>
) : (
<Trans>0 UNI / week</Trans>
)}
</ThemedText.DeprecatedBlack>
</RowBetween>
</AutoColumn>
</StyledBottomCard>
</BottomSection>
<ThemedText.DeprecatedMain style={{ textAlign: 'center' }} fontSize={14}>
<span role="img" aria-label="wizard-icon" style={{ marginRight: '8px' }}>
⭐️
</span>
<Trans>When you withdraw, the contract will automagically claim UNI on your behalf!</Trans>
</ThemedText.DeprecatedMain>
{!showAddLiquidityButton && (
<DataRow style={{ marginBottom: '1rem' }}>
{stakingInfo && stakingInfo.active && (
<ButtonPrimary padding="8px" $borderRadius="8px" width="160px" onClick={handleDepositClick}>
{stakingInfo?.stakedAmount?.greaterThan(JSBI.BigInt(0)) ? (
<Trans>Deposit</Trans>
) : (
<Trans>Deposit UNI-V2 LP Tokens</Trans>
)}
</ButtonPrimary>
)}
{stakingInfo?.stakedAmount?.greaterThan(JSBI.BigInt(0)) && (
<>
<ButtonPrimary
padding="8px"
$borderRadius="8px"
width="160px"
onClick={() => setShowUnstakingModal(true)}
>
<Trans>Withdraw</Trans>
</ButtonPrimary>
</>
)}
</DataRow>
)}
{!userLiquidityUnstaked ? null : userLiquidityUnstaked.equalTo('0') ? null : !stakingInfo?.active ? null : (
<ThemedText.DeprecatedMain>
<Trans>{userLiquidityUnstaked.toSignificant(6)} UNI-V2 LP tokens available</Trans>
</ThemedText.DeprecatedMain>
)}
</PositionInfo>
</PageWrapper>
)
}
import { Trans } from '@lingui/macro'
import { useWeb3React } from '@web3-react/core'
import JSBI from 'jsbi'
import styled, { useTheme } from 'styled-components/macro'
import { OutlineCard } from '../../components/Card'
import { AutoColumn } from '../../components/Column'
import PoolCard from '../../components/earn/PoolCard'
import { CardBGImage, CardNoise, CardSection, DataCard } from '../../components/earn/styled'
import Loader from '../../components/Loader'
import { RowBetween } from '../../components/Row'
import { BIG_INT_ZERO } from '../../constants/misc'
import { STAKING_REWARDS_INFO, useStakingInfo } from '../../state/stake/hooks'
import { ExternalLink, ThemedText } from '../../theme'
import { Countdown } from './Countdown'
const PageWrapper = styled(AutoColumn)`
padding: 68px 8px 0px;
max-width: 640px;
width: 100%;
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.md}px`}) {
padding: 48px 8px 0px;
}
@media only screen and (max-width: ${({ theme }) => `${theme.breakpoint.sm}px`}) {
padding-top: 20px;
}
`
const TopSection = styled(AutoColumn)`
max-width: 720px;
width: 100%;
`
const PoolSection = styled.div`
display: grid;
grid-template-columns: 1fr;
column-gap: 10px;
row-gap: 15px;
width: 100%;
justify-self: center;
`
const DataRow = styled(RowBetween)`
${({ theme }) => theme.deprecated_mediaWidth.deprecated_upToSmall`
flex-direction: column;
`};
`
export default function Earn() {
const theme = useTheme()
const { chainId } = useWeb3React()
// staking info for connected account
const stakingInfos = useStakingInfo()
/**
* only show staking cards with balance
* @todo only account for this if rewards are inactive
*/
const stakingInfosWithBalance = stakingInfos?.filter((s) => JSBI.greaterThan(s.stakedAmount.quotient, BIG_INT_ZERO))
// toggle copy if rewards are inactive
const stakingRewardsExist = Boolean(typeof chainId === 'number' && (STAKING_REWARDS_INFO[chainId]?.length ?? 0) > 0)
return (
<PageWrapper gap="lg" justify="center">
<TopSection gap="md">
<DataCard>
<CardBGImage />
<CardNoise />
<CardSection>
<AutoColumn gap="md">
<RowBetween>
<ThemedText.DeprecatedWhite fontWeight={600}>
<Trans>Uniswap liquidity mining</Trans>
</ThemedText.DeprecatedWhite>
</RowBetween>
<RowBetween>
<ThemedText.DeprecatedWhite fontSize={14}>
<Trans>
Deposit your Liquidity Provider tokens to receive UNI, the Uniswap protocol governance token.
</Trans>
</ThemedText.DeprecatedWhite>
</RowBetween>{' '}
<ExternalLink
style={{ color: theme.white, textDecoration: 'underline' }}
href="https://uniswap.org/blog/uni/"
target="_blank"
>
<ThemedText.DeprecatedWhite fontSize={14}>
<Trans>Read more about UNI</Trans>
</ThemedText.DeprecatedWhite>
</ExternalLink>
</AutoColumn>
</CardSection>
<CardBGImage />
<CardNoise />
</DataCard>
</TopSection>
<AutoColumn gap="lg" style={{ width: '100%', maxWidth: '720px' }}>
<DataRow style={{ alignItems: 'baseline' }}>
<ThemedText.DeprecatedMediumHeader style={{ marginTop: '0.5rem' }}>
<Trans>Participating pools</Trans>
</ThemedText.DeprecatedMediumHeader>
<Countdown exactEnd={stakingInfos?.[0]?.periodFinish} />
</DataRow>
<PoolSection>
{stakingRewardsExist && stakingInfos?.length === 0 ? (
<Loader style={{ margin: 'auto' }} />
) : !stakingRewardsExist ? (
<OutlineCard>
<Trans>No active pools</Trans>
</OutlineCard>
) : stakingInfos?.length !== 0 && stakingInfosWithBalance.length === 0 ? (
<OutlineCard>
<Trans>No active pools</Trans>
</OutlineCard>
) : (
stakingInfosWithBalance?.map((stakingInfo) => {
// need to sort by added liquidity here
return <PoolCard key={stakingInfo.stakingRewardAddress} stakingInfo={stakingInfo} />
})
)}
</PoolSection>
</AutoColumn>
</PageWrapper>
)
}
import { Interface } from '@ethersproject/abi' import { Interface } from '@ethersproject/abi'
import { Trans } from '@lingui/macro'
import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json' import { abi as STAKING_REWARDS_ABI } from '@uniswap/liquidity-staker/build/StakingRewards.json'
import { CurrencyAmount, Token } from '@uniswap/sdk-core' import { CurrencyAmount, Token } from '@uniswap/sdk-core'
import { Pair } from '@uniswap/v2-sdk' import { Pair } from '@uniswap/v2-sdk'
...@@ -8,8 +7,7 @@ import { SupportedChainId } from 'constants/chains' ...@@ -8,8 +7,7 @@ import { SupportedChainId } from 'constants/chains'
import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp' import useCurrentBlockTimestamp from 'hooks/useCurrentBlockTimestamp'
import JSBI from 'jsbi' import JSBI from 'jsbi'
import { NEVER_RELOAD, useMultipleContractSingleData } from 'lib/hooks/multicall' import { NEVER_RELOAD, useMultipleContractSingleData } from 'lib/hooks/multicall'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { useMemo } from 'react'
import { ReactNode, useMemo } from 'react'
import { DAI, UNI, USDC_MAINNET, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens' import { DAI, UNI, USDC_MAINNET, USDT, WBTC, WRAPPED_NATIVE_CURRENCY } from '../../constants/tokens'
...@@ -17,9 +15,7 @@ const STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI) ...@@ -17,9 +15,7 @@ const STAKING_REWARDS_INTERFACE = new Interface(STAKING_REWARDS_ABI)
export const STAKING_GENESIS = 1600387200 export const STAKING_GENESIS = 1600387200
export const REWARDS_DURATION_DAYS = 60 const STAKING_REWARDS_INFO: {
export const STAKING_REWARDS_INFO: {
[chainId: number]: { [chainId: number]: {
tokens: [Token, Token] tokens: [Token, Token]
stakingRewardAddress: string stakingRewardAddress: string
...@@ -45,7 +41,7 @@ export const STAKING_REWARDS_INFO: { ...@@ -45,7 +41,7 @@ export const STAKING_REWARDS_INFO: {
], ],
} }
export interface StakingInfo { interface StakingInfo {
// the address of the reward contract // the address of the reward contract
stakingRewardAddress: string stakingRewardAddress: string
// the tokens involved in this pair // the tokens involved in this pair
...@@ -227,35 +223,3 @@ export function useStakingInfo(pairToFilterBy?: Pair | null): StakingInfo[] { ...@@ -227,35 +223,3 @@ export function useStakingInfo(pairToFilterBy?: Pair | null): StakingInfo[] {
uni, uni,
]) ])
} }
// based on typed value
export function useDerivedStakeInfo(
typedValue: string,
stakingToken: Token | undefined,
userLiquidityUnstaked: CurrencyAmount<Token> | undefined
): {
parsedAmount?: CurrencyAmount<Token>
error?: ReactNode
} {
const { account } = useWeb3React()
const parsedInput: CurrencyAmount<Token> | undefined = tryParseCurrencyAmount(typedValue, stakingToken)
const parsedAmount =
parsedInput && userLiquidityUnstaked && JSBI.lessThanOrEqual(parsedInput.quotient, userLiquidityUnstaked.quotient)
? parsedInput
: undefined
let error: ReactNode | undefined
if (!account) {
error = <Trans>Connect Wallet</Trans>
}
if (!parsedAmount) {
error = error ?? <Trans>Enter an amount</Trans>
}
return {
parsedAmount,
error,
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment