Commit 5cbc56cf authored by Kristie Huang's avatar Kristie Huang Committed by GitHub

feat: [info] add new stats box (#7522)

* feat: [info] add new stats section, wip

* add stats section

* implement fdv and market cap

* use fdv from backend gql

* code cleanup

* update cypress tests

* should only wrap if screen width <= 640

* minor design nits

* remove sitemap change

* nit pr review
parent 098c7b9c
import { ChainId, WETH9 } from '@uniswap/sdk-core' import { ChainId, WETH9 } from '@uniswap/sdk-core'
import { FeatureFlag } from 'featureFlags'
import { ARB, UNI } from '../../src/constants/tokens' import { ARB, UNI } from '../../src/constants/tokens'
import { getTestSelector } from '../utils' import { getTestSelector } from '../utils'
...@@ -14,8 +15,9 @@ describe('Token details', () => { ...@@ -14,8 +15,9 @@ describe('Token details', () => {
it('Uniswap token should have all information populated', () => { it('Uniswap token should have all information populated', () => {
// Uniswap token // Uniswap token
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`) cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`, {
featureFlags: [{ name: FeatureFlag.infoTDP, value: false }],
})
// Price chart should be filled in // Price chart should be filled in
cy.get('[data-cy="chart-header"]').should('include.text', '$') cy.get('[data-cy="chart-header"]').should('include.text', '$')
cy.get('[data-cy="price-chart"]').should('exist') cy.get('[data-cy="price-chart"]').should('exist')
...@@ -47,6 +49,22 @@ describe('Token details', () => { ...@@ -47,6 +49,22 @@ describe('Token details', () => {
cy.contains(UNI_ADDRESS).should('exist') cy.contains(UNI_ADDRESS).should('exist')
}) })
it('Uniswap token should have correct stats boxes if infoTDP flag on', () => {
// Uniswap token
cy.visit(`/tokens/ethereum/${UNI_ADDRESS}`, {
featureFlags: [{ name: FeatureFlag.infoTDP, value: true }],
})
// Stats should have: TVL, FDV, market cap, 24H volume
cy.get(getTestSelector('token-details-stats')).should('exist')
cy.get(getTestSelector('token-details-stats')).within(() => {
cy.get('[data-cy="tvl"]').should('include.text', '$')
cy.get('[data-cy="fdv"]').should('include.text', '$')
cy.get('[data-cy="market-cap"]').should('include.text', '$')
cy.get('[data-cy="volume-24h"]').should('include.text', '$')
})
})
it('token with warning and low trading volume should have all information populated', () => { it('token with warning and low trading volume should have all information populated', () => {
// Null token created for this test, 0 trading volume and has warning modal // Null token created for this test, 0 trading volume and has warning modal
cy.visit('/tokens/ethereum/0x1eFBB78C8b917f67986BcE54cE575069c0143681') cy.visit('/tokens/ethereum/0x1eFBB78C8b917f67986BcE54cE575069c0143681')
......
...@@ -2,6 +2,8 @@ import { Trans } from '@lingui/macro' ...@@ -2,6 +2,8 @@ import { Trans } from '@lingui/macro'
import { ChainId } from '@uniswap/sdk-core' import { ChainId } from '@uniswap/sdk-core'
import { MouseoverTooltip } from 'components/Tooltip' import { MouseoverTooltip } from 'components/Tooltip'
import { getChainInfo } from 'constants/chainInfo' import { getChainInfo } from 'constants/chainInfo'
import { useInfoTDPEnabled } from 'featureFlags/flags/infoTDP'
import { TokenQueryData } from 'graphql/data/Token'
import { ReactNode } from 'react' import { ReactNode } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { ExternalLink, ThemedText } from 'theme/components' import { ExternalLink, ThemedText } from 'theme/components'
...@@ -15,9 +17,13 @@ import { HEADER_DESCRIPTIONS } from '../TokenTable/TokenRow' ...@@ -15,9 +17,13 @@ import { HEADER_DESCRIPTIONS } from '../TokenTable/TokenRow'
export const StatWrapper = styled.div` export const StatWrapper = styled.div`
color: ${({ theme }) => theme.neutral2}; color: ${({ theme }) => theme.neutral2};
font-size: 14px; font-size: 14px;
min-width: 168px; min-width: 121px;
flex: 1; flex: 1;
padding: 24px 0px; padding: 24px 0px;
@media screen and (max-width: ${({ theme }) => theme.breakpoint.sm}px) {
min-width: 168px;
}
` `
const TokenStatsSection = styled.div` const TokenStatsSection = styled.div`
display: flex; display: flex;
...@@ -77,44 +83,100 @@ function Stat({ ...@@ -77,44 +83,100 @@ function Stat({
type StatsSectionProps = { type StatsSectionProps = {
chainId: ChainId chainId: ChainId
address: string address: string
priceLow52W?: NumericStat tokenQueryData: TokenQueryData
priceHigh52W?: NumericStat
TVL?: NumericStat
volume24H?: NumericStat
} }
export default function StatsSection(props: StatsSectionProps) { export default function StatsSection(props: StatsSectionProps) {
const { chainId, address, priceLow52W, priceHigh52W, TVL, volume24H } = props const { chainId, address, tokenQueryData } = props
const { label, infoLink } = getChainInfo(chainId) const { label, infoLink } = getChainInfo(chainId)
const isInfoTDPEnabled = useInfoTDPEnabled()
const tokenMarketInfo = tokenQueryData?.market
const tokenProjectMarketInfo = tokenQueryData?.project?.markets?.[0] // aggregated market price from CoinGecko
const FDV = tokenProjectMarketInfo?.fullyDilutedValuation?.value
const marketCap = tokenProjectMarketInfo?.marketCap?.value
const TVL = tokenMarketInfo?.totalValueLocked?.value
const volume24H = tokenMarketInfo?.volume24H?.value
const priceHigh52W = tokenMarketInfo?.priceHigh52W?.value
const priceLow52W = tokenMarketInfo?.priceLow52W?.value
const hasStats = isInfoTDPEnabled
? TVL || FDV || marketCap || volume24H
: TVL || volume24H || priceLow52W || priceHigh52W
if (TVL || volume24H || priceLow52W || priceHigh52W) { if (hasStats) {
return ( return (
<StatsWrapper data-testid="token-details-stats"> <StatsWrapper data-testid="token-details-stats">
<Header> <Header>
<Trans>Stats</Trans> <Trans>Stats</Trans>
</Header> </Header>
<TokenStatsSection> <TokenStatsSection>
<StatPair> {isInfoTDPEnabled ? (
<Stat <>
dataCy="tvl" <StatPair>
value={TVL} <Stat
description={HEADER_DESCRIPTIONS[TokenSortMethod.TOTAL_VALUE_LOCKED]} dataCy="tvl"
title={<Trans>TVL</Trans>} value={TVL}
/> description={HEADER_DESCRIPTIONS[TokenSortMethod.TOTAL_VALUE_LOCKED]}
<Stat title={<Trans>TVL</Trans>}
dataCy="volume-24h" />
value={volume24H} <Stat
description={ dataCy="market-cap"
<Trans> value={marketCap}
24H volume is the amount of the asset that has been traded on Uniswap v3 during the past 24 hours. description={
</Trans> <Trans>
} Market capitalization is the total market value of an asset&apos;s circulating supply.
title={<Trans>24H volume</Trans>} </Trans>
/> }
</StatPair> title={<Trans>Market cap</Trans>}
<StatPair> />
<Stat dataCy="52w-low" value={priceLow52W} title={<Trans>52W low</Trans>} /> </StatPair>
<Stat dataCy="52w-high" value={priceHigh52W} title={<Trans>52W high</Trans>} /> <StatPair>
</StatPair> <Stat
dataCy="fdv"
value={FDV}
description={HEADER_DESCRIPTIONS[TokenSortMethod.FULLY_DILUTED_VALUATION]}
title={<Trans>FDV</Trans>}
/>
<Stat
dataCy="volume-24h"
value={volume24H}
description={
<Trans>
1 day volume is the amount of the asset that has been traded on Uniswap v3 during the past 24
hours.
</Trans>
}
title={<Trans>1 day volume</Trans>}
/>
</StatPair>
</>
) : (
<>
<StatPair>
<Stat
dataCy="tvl"
value={TVL}
description={HEADER_DESCRIPTIONS[TokenSortMethod.TOTAL_VALUE_LOCKED]}
title={<Trans>TVL</Trans>}
/>
<Stat
dataCy="volume-24h"
value={volume24H}
description={
<Trans>
24H volume is the amount of the asset that has been traded on Uniswap v3 during the past 24 hours.
</Trans>
}
title={<Trans>24H volume</Trans>}
/>
</StatPair>
<StatPair>
<Stat dataCy="52w-low" value={priceLow52W} title={<Trans>52W low</Trans>} />
<Stat dataCy="52w-high" value={priceHigh52W} title={<Trans>52W high</Trans>} />
</StatPair>
</>
)}
</TokenStatsSection> </TokenStatsSection>
</StatsWrapper> </StatsWrapper>
) )
......
...@@ -237,14 +237,7 @@ export default function TokenDetails({ ...@@ -237,14 +237,7 @@ export default function TokenDetails({
</TokenInfoContainer> </TokenInfoContainer>
<ChartSection tokenPriceQuery={tokenPriceQuery} onChangeTimePeriod={onChangeTimePeriod} /> <ChartSection tokenPriceQuery={tokenPriceQuery} onChangeTimePeriod={onChangeTimePeriod} />
<StatsSection <StatsSection chainId={pageChainId} address={address} tokenQueryData={tokenQueryData} />
chainId={pageChainId}
address={address}
TVL={tokenQueryData?.market?.totalValueLocked?.value}
volume24H={tokenQueryData?.market?.volume24H?.value}
priceHigh52W={tokenQueryData?.market?.priceHigh52W?.value}
priceLow52W={tokenQueryData?.market?.priceLow52W?.value}
/>
<Hr /> <Hr />
<AboutSection <AboutSection
address={address} address={address}
......
...@@ -304,6 +304,12 @@ export const HEADER_DESCRIPTIONS: Record<TokenSortMethod, ReactNode | undefined> ...@@ -304,6 +304,12 @@ export const HEADER_DESCRIPTIONS: Record<TokenSortMethod, ReactNode | undefined>
Total value locked (TVL) is the aggregate amount of the asset available across all Uniswap v3 liquidity pools. Total value locked (TVL) is the aggregate amount of the asset available across all Uniswap v3 liquidity pools.
</Trans> </Trans>
), ),
[TokenSortMethod.FULLY_DILUTED_VALUATION]: (
<Trans>
Fully diluted valuation (FDV) is the market capitalization of an asset if maximum token supply were in
circulation.
</Trans>
),
[TokenSortMethod.VOLUME]: ( [TokenSortMethod.VOLUME]: (
<Trans>Volume is the amount of the asset that has been traded on Uniswap v3 during the selected time frame.</Trans> <Trans>Volume is the amount of the asset that has been traded on Uniswap v3 during the selected time frame.</Trans>
), ),
......
...@@ -4,6 +4,7 @@ import { atomWithReset } from 'jotai/utils' ...@@ -4,6 +4,7 @@ import { atomWithReset } from 'jotai/utils'
import { useCallback } from 'react' import { useCallback } from 'react'
export enum TokenSortMethod { export enum TokenSortMethod {
FULLY_DILUTED_VALUATION = 'FDV',
PRICE = 'Price', PRICE = 'Price',
PERCENT_CHANGE = 'Change', PERCENT_CHANGE = 'Change',
TOTAL_VALUE_LOCKED = 'TVL', TOTAL_VALUE_LOCKED = 'TVL',
......
...@@ -58,6 +58,18 @@ gql` ...@@ -58,6 +58,18 @@ gql`
chain chain
address address
} }
markets(currencies: [USD]) {
fullyDilutedValuation {
id
value
currency
}
marketCap {
id
value
currency
}
}
} }
} }
} }
......
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