chore: updating nft numbers (#7351)
* format price impact * format price * adding confirm swap modal * removing export * adding export back * half of test functions done * format numbers tests * formatting * making numberFormat local * making formatCurrencyAmount internal * price impact * formatSlippage * formatTickPrice * formatNumberOrString * formatFiatPrice * removing formatPreciseFloat * formatReviewSwapCurrencyAmount * correct active currency * explore table * deleting formatTickPrice * fixing explore table * nft assset details * removing all instancees of ethnumberstandardformatter * removing format wei impls * explore table * collectino stats * removing almost everything from nft/numbers * filter button * final nft fixes * removing put commas * explore page * listing page * extraneous functions * responding to comments * formatEhter * updating formatter names * dep array * comments --------- Co-authored-by: John Short <john.short@CORN-Jack-899.local>
This commit is contained in:
parent
802d56231a
commit
bd30721989
@ -8,10 +8,10 @@ import { NftCard } from 'nft/components/card'
|
||||
import { detailsHref } from 'nft/components/card/utils'
|
||||
import { VerifiedIcon } from 'nft/components/icons'
|
||||
import { WalletAsset } from 'nft/types'
|
||||
import { floorFormatter } from 'nft/utils'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
const FloorPrice = styled(Row)`
|
||||
opacity: 0;
|
||||
@ -83,6 +83,8 @@ export function NFT({
|
||||
}
|
||||
|
||||
function NFTDetails({ asset }: { asset: WalletAsset }) {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
|
||||
return (
|
||||
<Box overflow="hidden" width="full" flexWrap="nowrap">
|
||||
<Row gap="4px">
|
||||
@ -91,7 +93,9 @@ function NFTDetails({ asset }: { asset: WalletAsset }) {
|
||||
</Row>
|
||||
<FloorPrice>
|
||||
<ThemedText.BodySmall color="neutral2">
|
||||
{asset.floorPrice ? `${floorFormatter(asset.floorPrice)} ETH` : ' '}
|
||||
{asset.floorPrice
|
||||
? `${formatNumberOrString({ input: asset.floorPrice, type: NumberType.NFTTokenFloorPrice })} ETH`
|
||||
: ' '}
|
||||
</ThemedText.BodySmall>
|
||||
</FloorPrice>
|
||||
</Box>
|
||||
|
@ -13,13 +13,11 @@ import { Column, Row } from 'nft/components/Flex'
|
||||
import { VerifiedIcon } from 'nft/components/icons'
|
||||
import { vars } from 'nft/css/sprinkles.css'
|
||||
import { GenieCollection } from 'nft/types'
|
||||
import { ethNumberStandardFormatter } from 'nft/utils/currency'
|
||||
import { putCommas } from 'nft/utils/putCommas'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { useFormatter } from 'utils/formatNumbers'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
import { DeltaArrow, DeltaText } from '../Tokens/TokenDetails/Delta'
|
||||
import { useAddRecentlySearchedAsset } from './RecentlySearchedAssets'
|
||||
@ -49,6 +47,8 @@ export const CollectionRow = ({
|
||||
index,
|
||||
eventProperties,
|
||||
}: CollectionRowProps) => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
|
||||
const [brokenImage, setBrokenImage] = useState(false)
|
||||
const [loaded, setLoaded] = useState(false)
|
||||
|
||||
@ -102,13 +102,17 @@ export const CollectionRow = ({
|
||||
<Box className={styles.primaryText}>{collection.name}</Box>
|
||||
{collection.isVerified && <VerifiedIcon className={styles.suggestionIcon} />}
|
||||
</Row>
|
||||
<Box className={styles.secondaryText}>{putCommas(collection?.stats?.total_supply ?? 0)} items</Box>
|
||||
<Box className={styles.secondaryText}>
|
||||
{formatNumberOrString({ input: collection?.stats?.total_supply, type: NumberType.WholeNumber })} items
|
||||
</Box>
|
||||
</Column>
|
||||
</Row>
|
||||
{collection.stats?.floor_price ? (
|
||||
<Column className={styles.suggestionSecondaryContainer}>
|
||||
<Row gap="4">
|
||||
<Box className={styles.primaryText}>{ethNumberStandardFormatter(collection.stats?.floor_price)} ETH</Box>
|
||||
<Box className={styles.primaryText}>
|
||||
{formatNumberOrString({ input: collection.stats?.floor_price, type: NumberType.NFTToken })} ETH
|
||||
</Box>
|
||||
</Row>
|
||||
<Box className={styles.secondaryText}>Floor</Box>
|
||||
</Column>
|
||||
|
@ -31,12 +31,12 @@ import { useSubscribeTransactionState } from 'nft/hooks/useSubscribeTransactionS
|
||||
import { useTokenInput } from 'nft/hooks/useTokenInput'
|
||||
import { useWalletBalance } from 'nft/hooks/useWalletBalance'
|
||||
import { BagStatus } from 'nft/types'
|
||||
import { ethNumberStandardFormatter, formatWeiToDecimal } from 'nft/utils'
|
||||
import { PropsWithChildren, useEffect, useMemo, useState } from 'react'
|
||||
import { AlertTriangle, ChevronDown } from 'react-feather'
|
||||
import { InterfaceTrade, TradeFillType, TradeState } from 'state/routing/types'
|
||||
import styled, { useTheme } from 'styled-components'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
import { BuyButtonStateData, BuyButtonStates, getBuyButtonStateData } from './ButtonStates'
|
||||
|
||||
@ -189,10 +189,12 @@ const InputCurrencyValue = ({
|
||||
tradeState: TradeState
|
||||
trade?: InterfaceTrade
|
||||
}) => {
|
||||
const { formatEther, formatNumberOrString } = useFormatter()
|
||||
|
||||
if (!usingPayWithAnyToken) {
|
||||
return (
|
||||
<ThemedText.BodyPrimary lineHeight="20px" fontWeight="535">
|
||||
{formatWeiToDecimal(totalEthPrice.toString())}
|
||||
{formatEther({ input: totalEthPrice.toString(), type: NumberType.NFTToken })}
|
||||
{activeCurrency?.symbol ?? 'ETH'}
|
||||
</ThemedText.BodyPrimary>
|
||||
)
|
||||
@ -208,7 +210,7 @@ const InputCurrencyValue = ({
|
||||
|
||||
return (
|
||||
<ValueText color={tradeState === TradeState.LOADING ? 'neutral3' : 'neutral1'}>
|
||||
{ethNumberStandardFormatter(trade?.inputAmount.toExact())}
|
||||
{formatNumberOrString({ input: trade?.inputAmount.toExact(), type: NumberType.NFTToken })}
|
||||
</ValueText>
|
||||
)
|
||||
}
|
||||
@ -224,6 +226,8 @@ const FiatValue = ({
|
||||
tradeState: TradeState
|
||||
usingPayWithAnyToken: boolean
|
||||
}) => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
|
||||
if (!usdcValue) {
|
||||
if (usingPayWithAnyToken && (tradeState === TradeState.INVALID || tradeState === TradeState.NO_ROUTE_FOUND)) {
|
||||
return null
|
||||
@ -247,7 +251,7 @@ const FiatValue = ({
|
||||
</>
|
||||
)}
|
||||
<ThemedText.BodySmall color="neutral3" lineHeight="20px">
|
||||
{`${ethNumberStandardFormatter(usdcValue?.toExact(), true)}`}
|
||||
{`${formatNumberOrString({ input: usdcValue?.toExact(), type: NumberType.FiatNFTToken })}`}
|
||||
</ThemedText.BodySmall>
|
||||
</PriceImpactContainer>
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { formatEther } from '@ethersproject/units'
|
||||
import { formatEther as ethersFormatEther } from '@ethersproject/units'
|
||||
import clsx from 'clsx'
|
||||
import { ButtonEmphasis, ButtonSize, ThemeButton } from 'components/Button'
|
||||
import { TimedLoader } from 'nft/components/bag/TimedLoader'
|
||||
@ -18,10 +18,11 @@ import {
|
||||
import { bodySmall } from 'nft/css/common.css'
|
||||
import { loadingBlock } from 'nft/css/loading.css'
|
||||
import { GenieAsset, UpdatedGenieAsset } from 'nft/types'
|
||||
import { ethNumberStandardFormatter, formatWeiToDecimal, getAssetHref } from 'nft/utils'
|
||||
import { getAssetHref } from 'nft/utils'
|
||||
import { MouseEvent, useCallback, useEffect, useReducer, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
import * as styles from './BagRow.css'
|
||||
|
||||
@ -90,6 +91,7 @@ interface BagRowProps {
|
||||
}
|
||||
|
||||
export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, isMobile }: BagRowProps) => {
|
||||
const { formatEther, formatNumberOrString } = useFormatter()
|
||||
const [loadedImage, setImageLoaded] = useState(false)
|
||||
const [noImageAvailable, setNoImageAvailable] = useState(!asset.smallImageUrl)
|
||||
|
||||
@ -99,11 +101,11 @@ export const BagRow = ({ asset, usdPrice, removeAsset, showRemove, grayscale, is
|
||||
const showRemoveButton = Boolean(showRemove && cardHovered && !isMobile)
|
||||
|
||||
const assetEthPrice = asset.updatedPriceInfo ? asset.updatedPriceInfo.ETHPrice : asset.priceInfo.ETHPrice
|
||||
const assetEthPriceFormatted = formatWeiToDecimal(assetEthPrice)
|
||||
const assetUSDPriceFormatted = ethNumberStandardFormatter(
|
||||
usdPrice ? parseFloat(formatEther(assetEthPrice)) * usdPrice : usdPrice,
|
||||
true
|
||||
)
|
||||
const assetEthPriceFormatted = formatEther({ input: assetEthPrice, type: NumberType.NFTToken })
|
||||
const assetUSDPriceFormatted = formatNumberOrString({
|
||||
input: usdPrice ? parseFloat(ethersFormatEther(assetEthPrice)) * usdPrice : usdPrice,
|
||||
type: NumberType.FiatNFTToken,
|
||||
})
|
||||
|
||||
const handleRemoveClick = useCallback(
|
||||
(e: MouseEvent<HTMLElement>) => {
|
||||
@ -175,6 +177,7 @@ interface PriceChangeBagRowProps {
|
||||
}
|
||||
|
||||
export const PriceChangeBagRow = ({ asset, usdPrice, markAssetAsReviewed, top, isMobile }: PriceChangeBagRowProps) => {
|
||||
const { formatEther } = useFormatter()
|
||||
const isPriceIncrease = BigNumber.from(asset.updatedPriceInfo?.ETHPrice).gt(BigNumber.from(asset.priceInfo.ETHPrice))
|
||||
const handleRemove = useCallback(
|
||||
(e: MouseEvent<HTMLButtonElement>) => {
|
||||
@ -198,9 +201,10 @@ export const PriceChangeBagRow = ({ asset, usdPrice, markAssetAsReviewed, top, i
|
||||
<Column className={styles.priceChangeColumn} borderTopColor={top ? 'surface3' : 'transparent'}>
|
||||
<Row className={styles.priceChangeRow}>
|
||||
{isPriceIncrease ? <SquareArrowUpIcon /> : <SquareArrowDownIcon />}
|
||||
<Box>{`Price ${isPriceIncrease ? 'increased' : 'decreased'} from ${formatWeiToDecimal(
|
||||
asset.priceInfo.ETHPrice
|
||||
)} ETH`}</Box>
|
||||
<Box>{`Price ${isPriceIncrease ? 'increased' : 'decreased'} from ${formatEther({
|
||||
input: asset.priceInfo.ETHPrice,
|
||||
type: NumberType.NFTToken,
|
||||
})} ETH`}</Box>
|
||||
</Row>
|
||||
<Box style={{ marginLeft: '-8px', marginRight: '-8px' }}>
|
||||
<BagRow asset={asset} usdPrice={usdPrice} removeAsset={() => undefined} isMobile={isMobile} />
|
||||
|
@ -3,7 +3,8 @@ import { Column, Row } from 'nft/components/Flex'
|
||||
import { body, bodySmall } from 'nft/css/common.css'
|
||||
import { useBag } from 'nft/hooks'
|
||||
import { useBagTotalEthPrice, useBagTotalUsdPrice } from 'nft/hooks/useBagTotalEthPrice'
|
||||
import { ethNumberStandardFormatter, formatWeiToDecimal, roundAndPluralize } from 'nft/utils'
|
||||
import { roundAndPluralize } from 'nft/utils'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
import * as styles from './MobileHoverBag.css'
|
||||
export const MobileHoverBag = () => {
|
||||
@ -11,6 +12,7 @@ export const MobileHoverBag = () => {
|
||||
const toggleBag = useBag((state) => state.toggleBag)
|
||||
const totalEthPrice = useBagTotalEthPrice()
|
||||
const totalUsdPrice = useBagTotalUsdPrice()
|
||||
const { formatEther, formatNumberOrString } = useFormatter()
|
||||
|
||||
const shouldShowBag = itemsInBag.length > 0
|
||||
|
||||
@ -48,9 +50,11 @@ export const MobileHoverBag = () => {
|
||||
{roundAndPluralize(itemsInBag.length, 'NFT')}
|
||||
</Box>
|
||||
<Row gap="8">
|
||||
<Box className={body}>{`${formatWeiToDecimal(totalEthPrice.toString())}`} ETH</Box>
|
||||
<Box className={body}>
|
||||
{`${formatEther({ input: totalEthPrice.toString(), type: NumberType.NFTToken })}`} ETH
|
||||
</Box>
|
||||
<Box color="neutral2" className={bodySmall}>
|
||||
{ethNumberStandardFormatter(totalUsdPrice, true)}
|
||||
{formatNumberOrString({ input: totalUsdPrice, type: NumberType.FiatNFTToken })}
|
||||
</Box>
|
||||
</Row>
|
||||
</Column>
|
||||
|
@ -5,10 +5,10 @@ import { NftStandard } from 'graphql/data/__generated__/types-and-hooks'
|
||||
import { getMarketplaceIcon } from 'nft/components/card/utils'
|
||||
import { CollectionSelectedAssetIcon } from 'nft/components/icons'
|
||||
import { Markets } from 'nft/types'
|
||||
import { putCommas } from 'nft/utils'
|
||||
import { AlertTriangle, Check, Tag } from 'react-feather'
|
||||
import styled from 'styled-components'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
const StyledMarketplaceContainer = styled.div<{ isText?: boolean }>`
|
||||
position: absolute;
|
||||
@ -114,6 +114,8 @@ const RarityInfo = styled(ThemedText.BodySmall)`
|
||||
`
|
||||
|
||||
export const Ranking = ({ provider }: RankingProps) => {
|
||||
const { formatNumber } = useFormatter()
|
||||
|
||||
if (!provider.rank) {
|
||||
return null
|
||||
}
|
||||
@ -131,7 +133,7 @@ export const Ranking = ({ provider }: RankingProps) => {
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
# {putCommas(provider.rank)}
|
||||
# {formatNumber({ input: provider.rank, type: NumberType.WholeNumber })}
|
||||
</MouseoverTooltip>
|
||||
</RarityInfo>
|
||||
)
|
||||
|
@ -4,8 +4,8 @@ import { MediaContainer } from 'nft/components/card/media'
|
||||
import { detailsHref, getNftDisplayComponent, useSelectAsset } from 'nft/components/card/utils'
|
||||
import { useBag } from 'nft/hooks'
|
||||
import { GenieAsset, UniformAspectRatio, UniformAspectRatios, WalletAsset } from 'nft/types'
|
||||
import { floorFormatter } from 'nft/utils'
|
||||
import { ReactNode } from 'react'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
interface NftCardProps {
|
||||
asset: GenieAsset | WalletAsset
|
||||
@ -74,12 +74,15 @@ export const NftCard = ({
|
||||
setBagExpanded: state.setBagExpanded,
|
||||
}))
|
||||
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const collectionNft = 'marketplace' in asset
|
||||
const profileNft = 'asset_contract' in asset
|
||||
const tokenType = collectionNft ? asset.tokenType : profileNft ? asset.asset_contract.tokenType : undefined
|
||||
const marketplace = collectionNft ? asset.marketplace : undefined
|
||||
const listedPrice =
|
||||
profileNft && !isDisabled && asset.floor_sell_order_price ? floorFormatter(asset.floor_sell_order_price) : undefined
|
||||
profileNft && !isDisabled && asset.floor_sell_order_price
|
||||
? formatNumberOrString({ input: asset.floor_sell_order_price, type: NumberType.NFTTokenFloorPrice })
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<Card.Container
|
||||
|
@ -26,13 +26,12 @@ import {
|
||||
} from 'nft/types'
|
||||
import { getMarketplaceIcon } from 'nft/utils'
|
||||
import { buildActivityAsset } from 'nft/utils/buildActivityAsset'
|
||||
import { formatEth } from 'nft/utils/currency'
|
||||
import { getTimeDifference } from 'nft/utils/date'
|
||||
import { putCommas } from 'nft/utils/putCommas'
|
||||
import { MouseEvent, ReactNode, useMemo, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { ExternalLink } from 'theme/components'
|
||||
import { shortenAddress } from 'utils'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
|
||||
|
||||
import * as styles from './Activity.css'
|
||||
@ -185,7 +184,11 @@ const PriceTooltip = ({ price }: { price: string }) => (
|
||||
)
|
||||
|
||||
export const PriceCell = ({ marketplace, price }: { marketplace?: Markets | string; price?: string | number }) => {
|
||||
const formattedPrice = useMemo(() => (price ? formatEth(parseFloat(price?.toString())) : null), [price])
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const formattedPrice = useMemo(
|
||||
() => (price ? formatNumberOrString({ input: parseFloat(price?.toString()), type: NumberType.NFTToken }) : null),
|
||||
[formatNumberOrString, price]
|
||||
)
|
||||
|
||||
return (
|
||||
<Row display={{ sm: 'none', md: 'flex' }} gap="8">
|
||||
@ -257,7 +260,11 @@ export const EventCell = ({
|
||||
price,
|
||||
isMobile,
|
||||
}: EventCellProps) => {
|
||||
const formattedPrice = useMemo(() => (price ? formatEth(parseFloat(price?.toString())) : null), [price])
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const formattedPrice = useMemo(
|
||||
() => (price ? formatNumberOrString({ input: parseFloat(price?.toString()), type: NumberType.NFTToken }) : null),
|
||||
[formatNumberOrString, price]
|
||||
)
|
||||
return (
|
||||
<Column height="full" justifyContent="center" gap="4">
|
||||
<Row className={styles.eventDetail} color={eventColors(eventType)}>
|
||||
@ -318,6 +325,7 @@ interface RankingProps {
|
||||
}
|
||||
|
||||
const Ranking = ({ rarity, collectionName, rarityVerified }: RankingProps) => {
|
||||
const { formatNumber } = useFormatter()
|
||||
const rank = (rarity as TokenRarity).rank || (rarity as Rarity).providers?.[0].rank
|
||||
|
||||
if (!rank) return null
|
||||
@ -339,7 +347,7 @@ const Ranking = ({ rarity, collectionName, rarityVerified }: RankingProps) => {
|
||||
>
|
||||
<Box className={styles.rarityInfo}>
|
||||
<Box paddingTop="2" paddingBottom="2" display="flex">
|
||||
{putCommas(rank)}
|
||||
{formatNumber({ input: rank, type: NumberType.WholeNumber })}
|
||||
</Box>
|
||||
|
||||
<Box display="flex" height="16">
|
||||
|
@ -6,8 +6,8 @@ import { NftCard, NftCardDisplayProps } from 'nft/components/card'
|
||||
import { Ranking as RankingContainer, Suspicious as SuspiciousContainer } from 'nft/components/card/icons'
|
||||
import { useBag } from 'nft/hooks'
|
||||
import { GenieAsset, UniformAspectRatio } from 'nft/types'
|
||||
import { formatWeiToDecimal } from 'nft/utils'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
interface CollectionAssetProps {
|
||||
asset: GenieAsset
|
||||
@ -31,6 +31,7 @@ export const CollectionAsset = ({
|
||||
renderedHeight,
|
||||
setRenderedHeight,
|
||||
}: CollectionAssetProps) => {
|
||||
const { formatEther } = useFormatter()
|
||||
const bagManuallyClosed = useBag((state) => state.bagManuallyClosed)
|
||||
const addAssetsToBag = useBag((state) => state.addAssetsToBag)
|
||||
const removeAssetsFromBag = useBag((state) => state.removeAssetsFromBag)
|
||||
@ -76,12 +77,23 @@ export const CollectionAsset = ({
|
||||
primaryInfo: asset.name ? asset.name : `#${asset.tokenId}`,
|
||||
primaryInfoIcon: asset.susFlag ? <SuspiciousContainer /> : null,
|
||||
primaryInfoRight: asset.rarity && provider ? <RankingContainer provider={provider} /> : null,
|
||||
secondaryInfo: notForSale ? '' : `${formatWeiToDecimal(asset.priceInfo.ETHPrice, true)} ETH`,
|
||||
secondaryInfo: notForSale
|
||||
? ''
|
||||
: `${formatEther({ input: asset.priceInfo.ETHPrice, type: NumberType.NFTToken })} ETH`,
|
||||
selectedInfo: <Trans>Remove from bag</Trans>,
|
||||
notSelectedInfo: <Trans>Add to bag</Trans>,
|
||||
disabledInfo: <Trans>Not listed</Trans>,
|
||||
}
|
||||
}, [asset.name, asset.priceInfo.ETHPrice, asset.rarity, asset.susFlag, asset.tokenId, notForSale, provider])
|
||||
}, [
|
||||
asset.name,
|
||||
asset.priceInfo.ETHPrice,
|
||||
asset.rarity,
|
||||
asset.susFlag,
|
||||
asset.tokenId,
|
||||
formatEther,
|
||||
notForSale,
|
||||
provider,
|
||||
])
|
||||
|
||||
return (
|
||||
<NftCard
|
||||
|
@ -8,11 +8,12 @@ import { themeVars } from 'nft/css/sprinkles.css'
|
||||
import { useBag, useIsMobile } from 'nft/hooks'
|
||||
import { useIsCollectionLoading } from 'nft/hooks/useIsCollectionLoading'
|
||||
import { GenieCollection, TokenType } from 'nft/types'
|
||||
import { floorFormatter, quantityFormatter, roundWholePercentage, volumeFormatter } from 'nft/utils/numbers'
|
||||
import { roundWholePercentage } from 'nft/utils/numbers'
|
||||
import { ReactNode, useEffect, useReducer, useRef, useState } from 'react'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import styled, { css } from 'styled-components'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
import { DiscordIcon, EllipsisIcon, ExternalIcon, InstagramIcon, TwitterIcon, VerifiedIcon, XMarkIcon } from '../icons'
|
||||
import * as styles from './CollectionStats.css'
|
||||
@ -338,20 +339,30 @@ const statsLoadingSkeleton = (isMobile: boolean) =>
|
||||
))
|
||||
|
||||
const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMobile?: boolean } & BoxProps) => {
|
||||
const { formatNumberOrString, formatDelta } = useFormatter()
|
||||
|
||||
const uniqueOwnersPercentage = stats?.stats?.total_supply
|
||||
? roundWholePercentage(((stats.stats.num_owners ?? 0) / stats.stats.total_supply) * 100)
|
||||
: 0
|
||||
const totalSupplyStr = stats.stats ? quantityFormatter(stats.stats.total_supply ?? 0) : 0
|
||||
const totalSupplyStr = stats.stats
|
||||
? formatNumberOrString({ input: stats.stats.total_supply ?? 0, type: NumberType.NFTCollectionStats })
|
||||
: 0
|
||||
const listedPercentageStr = stats?.stats?.total_supply
|
||||
? roundWholePercentage(((stats.stats.total_listings ?? 0) / stats.stats.total_supply) * 100)
|
||||
: 0
|
||||
const isCollectionStatsLoading = useIsCollectionLoading((state) => state.isCollectionStatsLoading)
|
||||
|
||||
// round daily volume & floorPrice to 3 decimals or less
|
||||
const totalVolumeStr = volumeFormatter(Number(stats.stats?.total_volume) ?? 0)
|
||||
const floorPriceStr = floorFormatter(stats.stats?.floor_price ?? 0)
|
||||
const totalVolumeStr = formatNumberOrString({
|
||||
input: Number(stats.stats?.total_volume) ?? 0,
|
||||
type: NumberType.NFTCollectionStats,
|
||||
})
|
||||
const floorPriceStr = formatNumberOrString({
|
||||
input: stats.stats?.floor_price ?? 0,
|
||||
type: NumberType.NFTTokenFloorPrice,
|
||||
})
|
||||
// graphQL formatted %age values out of 100, whereas v3 endpoint did a decimal between 0 & 1
|
||||
const floorChangeStr = Math.round(Math.abs(stats?.stats?.one_day_floor_change ?? 0))
|
||||
const floorChangeStr = formatDelta(Math.round(Math.abs(stats?.stats?.one_day_floor_change ?? 0)))
|
||||
|
||||
const isBagExpanded = useBag((state) => state.bagExpanded)
|
||||
const isScreenSize = useScreenSize()
|
||||
@ -372,7 +383,7 @@ const StatsRow = ({ stats, isMobile, ...props }: { stats: GenieCollection; isMob
|
||||
<StatsItem label="Floor 24H" shouldHide={false}>
|
||||
<PercentChange isNegative={stats.stats.one_day_floor_change < 0}>
|
||||
<DeltaArrow delta={stats?.stats?.one_day_floor_change} />
|
||||
{floorChangeStr}%
|
||||
{floorChangeStr}
|
||||
</PercentChange>
|
||||
</StatsItem>
|
||||
) : null}
|
||||
|
@ -4,7 +4,8 @@ import * as styles from 'nft/components/collection/FilterButton.css'
|
||||
import { FilterIcon } from 'nft/components/icons'
|
||||
import { buttonTextMedium } from 'nft/css/common.css'
|
||||
import { breakpoints } from 'nft/css/sprinkles.css'
|
||||
import { pluralize, putCommas } from 'nft/utils'
|
||||
import { pluralize } from 'nft/utils'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
export const FilterButton = ({
|
||||
onClick,
|
||||
@ -17,6 +18,7 @@ export const FilterButton = ({
|
||||
onClick: () => void
|
||||
collectionCount?: number
|
||||
}) => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const hideResultsCount = window.innerWidth >= breakpoints.sm && window.innerWidth < breakpoints.md
|
||||
|
||||
return (
|
||||
@ -41,7 +43,10 @@ export const FilterButton = ({
|
||||
{' '}
|
||||
{!collectionCount || hideResultsCount
|
||||
? 'Filter'
|
||||
: `Filter • ${putCommas(collectionCount)} result${pluralize(collectionCount)}`}
|
||||
: `Filter • ${formatNumberOrString({
|
||||
input: collectionCount,
|
||||
type: NumberType.WholeNumber,
|
||||
})} result${pluralize(collectionCount)}`}
|
||||
</Box>
|
||||
) : null}
|
||||
</Box>
|
||||
|
@ -1,16 +1,17 @@
|
||||
import 'rc-slider/assets/index.css'
|
||||
|
||||
import { BigNumber } from '@ethersproject/bignumber'
|
||||
import { formatEther, parseEther } from '@ethersproject/units'
|
||||
import { formatEther as ethersFormatEther, parseEther } from '@ethersproject/units'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { SweepFetcherParams, useSweepNftAssets } from 'graphql/data/nft/Asset'
|
||||
import { useBag, useCollectionFilters } from 'nft/hooks'
|
||||
import { GenieAsset, isPooledMarket, Markets } from 'nft/types'
|
||||
import { calcPoolPrice, formatWeiToDecimal, isInSameSudoSwapPool } from 'nft/utils'
|
||||
import { calcPoolPrice, isInSameSudoSwapPool } from 'nft/utils'
|
||||
import { default as Slider } from 'rc-slider'
|
||||
import { useEffect, useMemo, useReducer, useState } from 'react'
|
||||
import styled, { useTheme } from 'styled-components'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
const SweepContainer = styled.div`
|
||||
display: flex;
|
||||
@ -160,6 +161,7 @@ interface SweepProps {
|
||||
|
||||
export const Sweep = ({ contractAddress, minPrice, maxPrice }: SweepProps) => {
|
||||
const theme = useTheme()
|
||||
const { formatEther } = useFormatter()
|
||||
|
||||
const [isItemsToggled, toggleSweep] = useReducer((state) => !state, true)
|
||||
const [sweepAmount, setSweepAmount] = useState<string>('')
|
||||
@ -374,8 +376,12 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice }: SweepProps) => {
|
||||
<StyledSlider
|
||||
defaultValue={0}
|
||||
min={0}
|
||||
max={isItemsToggled ? sortedAssets?.length ?? 0 : parseFloat(formatEther(sortedAssetsTotalEth).toString())}
|
||||
value={isItemsToggled ? sweepItemsInBag.length : parseFloat(formatWeiToDecimal(sweepEthPrice.toString()))}
|
||||
max={
|
||||
isItemsToggled
|
||||
? sortedAssets?.length ?? 0
|
||||
: parseFloat(ethersFormatEther(sortedAssetsTotalEth).toString())
|
||||
}
|
||||
value={isItemsToggled ? sweepItemsInBag.length : parseFloat(ethersFormatEther(sweepEthPrice.toString()))}
|
||||
step={isItemsToggled ? 1 : 0.01}
|
||||
trackStyle={{
|
||||
top: '3px',
|
||||
@ -424,9 +430,10 @@ export const Sweep = ({ contractAddress, minPrice, maxPrice }: SweepProps) => {
|
||||
</SweepSubContainer>
|
||||
</SweepLeftmostContainer>
|
||||
<SweepRightmostContainer>
|
||||
<ThemedText.SubHeader font-size="14px">{`${formatWeiToDecimal(
|
||||
sweepEthPrice.toString()
|
||||
)} ETH`}</ThemedText.SubHeader>
|
||||
<ThemedText.SubHeader font-size="14px">{`${formatEther({
|
||||
input: sweepEthPrice.toString(),
|
||||
type: NumberType.NFTToken,
|
||||
})} ETH`}</ThemedText.SubHeader>
|
||||
<NftDisplay nfts={sweepItemsInBag} />
|
||||
</SweepRightmostContainer>
|
||||
</SweepContainer>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { formatEther } from '@ethersproject/units'
|
||||
import { formatEther as ethersFormatEther } from '@ethersproject/units'
|
||||
import { Trans } from '@lingui/macro'
|
||||
import { InterfaceModalName, NFTEventName } from '@uniswap/analytics-events'
|
||||
import { Trace, useTrace } from 'analytics'
|
||||
@ -12,17 +12,11 @@ import { Overlay, stopPropagation } from 'nft/components/modals/Overlay'
|
||||
import { themeVars, vars } from 'nft/css/sprinkles.css'
|
||||
import { useIsMobile, useNativeUsdPrice, useSendTransaction, useTransactionResponse } from 'nft/hooks'
|
||||
import { TxResponse, TxStateType } from 'nft/types'
|
||||
import {
|
||||
formatEthPrice,
|
||||
formatUsdPrice,
|
||||
formatUSDPriceWithCommas,
|
||||
generateTweetForPurchase,
|
||||
getSuccessfulImageSize,
|
||||
parseTransactionResponse,
|
||||
} from 'nft/utils'
|
||||
import { generateTweetForPurchase, getSuccessfulImageSize, parseTransactionResponse } from 'nft/utils'
|
||||
import { formatAssetEventProperties } from 'nft/utils/formatEventProperties'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
import { ExplorerDataType, getExplorerLink } from 'utils/getExplorerLink'
|
||||
|
||||
import * as styles from './TransactionCompleteModal.css'
|
||||
@ -47,6 +41,7 @@ const UploadLink = styled.a`
|
||||
|
||||
const TxCompleteModal = () => {
|
||||
const ethUsdPrice = useNativeUsdPrice()
|
||||
const { formatEther, formatNumberOrString } = useFormatter()
|
||||
const [showUnavailable, setShowUnavailable] = useState(false)
|
||||
const txHash = useSendTransaction((state) => state.txHash)
|
||||
const purchasedWithErc20 = useSendTransaction((state) => state.purchasedWithErc20)
|
||||
@ -107,7 +102,7 @@ const TxCompleteModal = () => {
|
||||
name={NFTEventName.NFT_BUY_BAG_SUCCEEDED}
|
||||
properties={{
|
||||
buy_quantity: nftsPurchased.length,
|
||||
usd_value: parseFloat(formatEther(totalPurchaseValue)) * ethUsdPrice,
|
||||
usd_value: parseFloat(ethersFormatEther(totalPurchaseValue)) * ethUsdPrice,
|
||||
transaction_hash: txHash,
|
||||
using_erc20: purchasedWithErc20,
|
||||
...formatAssetEventProperties(nftsPurchased),
|
||||
@ -168,7 +163,13 @@ const TxCompleteModal = () => {
|
||||
<Box marginRight="16">
|
||||
{nftsPurchased.length} NFT{nftsPurchased.length === 1 ? '' : 's'}
|
||||
</Box>
|
||||
<Box>{formatEthPrice(totalPurchaseValue.toString())} ETH</Box>
|
||||
<Box>
|
||||
{formatEther({
|
||||
input: totalPurchaseValue.toString(),
|
||||
type: NumberType.NFTToken,
|
||||
})}{' '}
|
||||
ETH
|
||||
</Box>
|
||||
</Row>
|
||||
<a href={txHashUrl} target="_blank" rel="noreferrer" style={{ textDecoration: 'none' }}>
|
||||
<Box color="neutral2" fontWeight="book">
|
||||
@ -205,7 +206,13 @@ const TxCompleteModal = () => {
|
||||
<p className={styles.subtitle}>Instant Refund</p>
|
||||
<p className={styles.interStd}>
|
||||
Uniswap returned{' '}
|
||||
<span style={{ fontWeight: '535' }}>{formatEthPrice(totalRefundValue.toString())} ETH</span>{' '}
|
||||
<span style={{ fontWeight: '535' }}>
|
||||
{formatEther({
|
||||
input: totalRefundValue.toString(),
|
||||
type: NumberType.NFTToken,
|
||||
})}{' '}
|
||||
ETH
|
||||
</span>{' '}
|
||||
back to your wallet for unavailable items.
|
||||
</p>
|
||||
<Box
|
||||
@ -217,9 +224,15 @@ const TxCompleteModal = () => {
|
||||
position={{ sm: 'absolute', md: 'static' }}
|
||||
>
|
||||
<p className={styles.totalEthCost} style={{ marginBottom: '2px' }}>
|
||||
{formatEthPrice(totalRefundValue.toString())} ETH
|
||||
{formatEther({
|
||||
input: totalRefundValue.toString(),
|
||||
type: NumberType.NFTToken,
|
||||
})}{' '}
|
||||
ETH
|
||||
</p>
|
||||
<p className={styles.totalUsdRefund}>
|
||||
{formatNumberOrString({ input: totalUSDRefund, type: NumberType.FiatNFTToken })}
|
||||
</p>
|
||||
<p className={styles.totalUsdRefund}>{formatUSDPriceWithCommas(totalUSDRefund)}</p>
|
||||
<p className={styles.totalEthCost} style={{ width: '100%' }}>
|
||||
for {nftsNotPurchased.length} unavailable item
|
||||
{nftsNotPurchased.length === 1 ? '' : 's'}.
|
||||
@ -280,8 +293,9 @@ const TxCompleteModal = () => {
|
||||
`Selected item${
|
||||
nftsPurchased.length === 1 ? ' is' : 's are'
|
||||
} no longer available. Uniswap instantly refunded you for this incomplete transaction. `}
|
||||
{formatUsdPrice(txFeeFiat)} was used for gas in attempt to complete this transaction. For support,
|
||||
please visit our <a href="https://discord.gg/FCfyBSbCU5">Discord</a>
|
||||
{formatNumberOrString({ input: txFeeFiat, type: NumberType.FiatNFTToken })} was used for gas in
|
||||
attempt to complete this transaction. For support, please visit our{' '}
|
||||
<a href="https://discord.gg/FCfyBSbCU5">Discord</a>
|
||||
</p>
|
||||
<Box className={styles.allUnavailableAssets}>
|
||||
{nftsNotPurchased.length >= 3 && (
|
||||
@ -324,9 +338,12 @@ const TxCompleteModal = () => {
|
||||
<Box flexWrap="wrap" marginTop="4">
|
||||
<Box marginLeft="4" width="full" display="flex">
|
||||
<p className={styles.totalEthCost} style={{ marginBottom: '2px' }}>
|
||||
{formatEthPrice(
|
||||
asset.updatedPriceInfo ? asset.updatedPriceInfo.ETHPrice : asset.priceInfo.ETHPrice
|
||||
)}{' '}
|
||||
{formatEther({
|
||||
input: asset.updatedPriceInfo
|
||||
? asset.updatedPriceInfo.ETHPrice
|
||||
: asset.priceInfo.ETHPrice,
|
||||
type: NumberType.NFTToken,
|
||||
})}{' '}
|
||||
ETH
|
||||
</p>
|
||||
</Box>
|
||||
@ -339,9 +356,15 @@ const TxCompleteModal = () => {
|
||||
</Box>
|
||||
{showUnavailable && <Box className={styles.fullRefundOverflowFade} />}
|
||||
<p className={styles.totalEthCost} style={{ marginBottom: '2px' }}>
|
||||
{formatEthPrice(totalRefundValue.toString())} ETH
|
||||
{formatEther({
|
||||
input: totalRefundValue.toString(),
|
||||
type: NumberType.NFTToken,
|
||||
})}{' '}
|
||||
ETH
|
||||
</p>
|
||||
<p className={styles.totalUsdRefund}>
|
||||
{formatNumberOrString({ input: totalUSDRefund, type: NumberType.FiatNFTToken })}
|
||||
</p>
|
||||
<p className={styles.totalUsdRefund}>{formatUSDPriceWithCommas(totalUSDRefund)}</p>
|
||||
<Box className={styles.walletAddress} marginLeft="auto" marginRight="0">
|
||||
<a href={txHashUrl} target="_blank" rel="noreferrer">
|
||||
<Box className={styles.addressHash}>View on Etherscan</Box>
|
||||
|
@ -4,11 +4,11 @@ import { LoadingBubble } from 'components/Tokens/loading'
|
||||
import { EventCell } from 'nft/components/collection/ActivityCells'
|
||||
import { ActivityEvent } from 'nft/types'
|
||||
import { getMarketplaceIcon } from 'nft/utils'
|
||||
import { formatEth } from 'nft/utils/currency'
|
||||
import { getTimeDifference } from 'nft/utils/date'
|
||||
import { ReactNode } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { shortenAddress } from 'utils'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
const TR = styled.tr`
|
||||
border-bottom: ${({ theme }) => `1px solid ${theme.surface3}`};
|
||||
@ -148,12 +148,15 @@ export const LoadingAssetActivity = ({ rowCount }: { rowCount: number }) => {
|
||||
}
|
||||
|
||||
const AssetActivity = ({ events }: { events?: ActivityEvent[] }) => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
return (
|
||||
<ActivityTable>
|
||||
{events &&
|
||||
events.map((event, index) => {
|
||||
const { eventTimestamp, eventType, fromAddress, marketplace, price, toAddress, transactionHash } = event
|
||||
const formattedPrice = price ? formatEth(parseFloat(price ?? '')) : null
|
||||
const formattedPrice = price
|
||||
? formatNumberOrString({ input: parseFloat(price), type: NumberType.NFTToken })
|
||||
: null
|
||||
if (!eventType) return null
|
||||
return (
|
||||
<TR key={index}>
|
||||
|
@ -10,15 +10,14 @@ import { AssetPriceDetails } from 'nft/components/details/AssetPriceDetails'
|
||||
import { Center } from 'nft/components/Flex'
|
||||
import { themeVars, vars } from 'nft/css/sprinkles.css'
|
||||
import { ActivityEventType, CollectionInfoForAsset, GenieAsset } from 'nft/types'
|
||||
import { formatEth } from 'nft/utils/currency'
|
||||
import { isAudio } from 'nft/utils/isAudio'
|
||||
import { isVideo } from 'nft/utils/isVideo'
|
||||
import { putCommas } from 'nft/utils/putCommas'
|
||||
import { useCallback, useMemo, useReducer, useState } from 'react'
|
||||
import InfiniteScroll from 'react-infinite-scroll-component'
|
||||
import { Link as RouterLink } from 'react-router-dom'
|
||||
import styled from 'styled-components'
|
||||
import { shortenAddress } from 'utils/addresses'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
import AssetActivity, { LoadingAssetActivity } from './AssetActivity'
|
||||
import * as styles from './AssetDetails.css'
|
||||
@ -243,6 +242,7 @@ interface AssetDetailsProps {
|
||||
}
|
||||
|
||||
export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const [dominantColor] = useState<[number, number, number]>([0, 0, 0])
|
||||
|
||||
const { rarityProvider } = useMemo(
|
||||
@ -281,7 +281,9 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
|
||||
)
|
||||
|
||||
const weiPrice = gqlPriceData?.[0]?.price
|
||||
const formattedPrice = weiPrice ? formatEth(parseFloat(weiPrice)) : undefined
|
||||
const formattedPrice = weiPrice
|
||||
? formatNumberOrString({ input: parseFloat(weiPrice), type: NumberType.NFTToken })
|
||||
: undefined
|
||||
|
||||
const [activeFilters, filtersDispatch] = useReducer(reduceFilters, initialFilterState)
|
||||
const Filter = useCallback(
|
||||
@ -359,7 +361,9 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
<RarityWrap>Rarity: {putCommas(rarity.score)}</RarityWrap>
|
||||
<RarityWrap>
|
||||
Rarity: {formatNumberOrString({ input: rarity.score, type: NumberType.WholeNumber })}
|
||||
</RarityWrap>
|
||||
</MouseoverTooltip>
|
||||
) : null
|
||||
}
|
||||
|
@ -7,18 +7,13 @@ import { useNftBalance } from 'graphql/data/nft/NftBalance'
|
||||
import { CancelListingIcon, VerifiedIcon } from 'nft/components/icons'
|
||||
import { useBag, useNativeUsdPrice, useProfilePageState, useSellAsset, useUsdPriceofNftAsset } from 'nft/hooks'
|
||||
import { CollectionInfoForAsset, GenieAsset, ProfilePageStateType, WalletAsset } from 'nft/types'
|
||||
import {
|
||||
ethNumberStandardFormatter,
|
||||
formatEthPrice,
|
||||
generateTweetForAsset,
|
||||
getMarketplaceIcon,
|
||||
timeLeft,
|
||||
} from 'nft/utils'
|
||||
import { generateTweetForAsset, getMarketplaceIcon, timeLeft } from 'nft/utils'
|
||||
import { useMemo } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import styled, { css, useTheme } from 'styled-components'
|
||||
import { ExternalLink, ThemedText } from 'theme/components'
|
||||
import { shortenAddress } from 'utils/addresses'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
const TWITTER_WIDTH = 560
|
||||
const TWITTER_HEIGHT = 480
|
||||
@ -213,6 +208,7 @@ const OwnerContainer = ({ asset }: { asset: WalletAsset }) => {
|
||||
const setSellPageState = useProfilePageState((state) => state.setProfilePageState)
|
||||
const selectSellAsset = useSellAsset((state) => state.selectSellAsset)
|
||||
const resetSellAssets = useSellAsset((state) => state.reset)
|
||||
const { formatEther, formatNumberOrString } = useFormatter()
|
||||
|
||||
const listing = asset.sellOrders && asset.sellOrders.length > 0 ? asset.sellOrders[0] : undefined
|
||||
const expirationDate = listing?.endAt ? new Date(listing.endAt) : undefined
|
||||
@ -249,11 +245,15 @@ const OwnerContainer = ({ asset }: { asset: WalletAsset }) => {
|
||||
{listing ? (
|
||||
<>
|
||||
<ThemedText.MediumHeader fontSize="28px" lineHeight="36px">
|
||||
{formatEthPrice(asset.priceInfo?.ETHPrice)} ETH
|
||||
{formatEther({
|
||||
input: asset.priceInfo?.ETHPrice,
|
||||
type: NumberType.NFTToken,
|
||||
})}{' '}
|
||||
ETH
|
||||
</ThemedText.MediumHeader>
|
||||
{USDPrice && (
|
||||
<ThemedText.BodySecondary lineHeight="24px">
|
||||
{ethNumberStandardFormatter(USDPrice, true, true)}
|
||||
{formatNumberOrString({ input: USDPrice, type: NumberType.FiatNFTToken })}
|
||||
</ThemedText.BodySecondary>
|
||||
)}
|
||||
</>
|
||||
@ -311,6 +311,7 @@ const NotForSale = ({ collectionName, collectionUrl }: { collectionName: string;
|
||||
|
||||
export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps) => {
|
||||
const { account } = useWeb3React()
|
||||
const { formatEther, formatNumberOrString } = useFormatter()
|
||||
|
||||
const cheapestOrder = asset.sellorders && asset.sellorders.length > 0 ? asset.sellorders[0] : undefined
|
||||
const expirationDate = cheapestOrder?.endAt ? new Date(cheapestOrder.endAt) : undefined
|
||||
@ -376,11 +377,11 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
|
||||
</HeaderRow>
|
||||
<PriceRow>
|
||||
<ThemedText.MediumHeader fontSize="28px" lineHeight="36px">
|
||||
{formatEthPrice(asset.priceInfo.ETHPrice)} ETH
|
||||
{formatEther({ input: asset.priceInfo.ETHPrice, type: NumberType.NFTToken })} ETH
|
||||
</ThemedText.MediumHeader>
|
||||
{USDPrice && (
|
||||
<ThemedText.BodySecondary lineHeight="24px">
|
||||
{ethNumberStandardFormatter(USDPrice, true, true)}
|
||||
{formatNumberOrString({ input: USDPrice, type: NumberType.FiatNFTToken })}
|
||||
</ThemedText.BodySecondary>
|
||||
)}
|
||||
</PriceRow>
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { OpacityHoverState } from 'components/Common'
|
||||
import useCopyClipboard from 'hooks/useCopyClipboard'
|
||||
import { CollectionInfoForAsset, GenieAsset } from 'nft/types'
|
||||
import { putCommas } from 'nft/utils'
|
||||
import { useCallback } from 'react'
|
||||
import { Copy } from 'react-feather'
|
||||
import styled from 'styled-components'
|
||||
import { shortenAddress } from 'utils'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
const Details = styled.div`
|
||||
display: grid;
|
||||
@ -66,6 +66,7 @@ const GridItem = ({ header, body }: { header: string; body: React.ReactNode }) =
|
||||
const stringShortener = (text: string) => `${text.substring(0, 4)}...${text.substring(text.length - 4, text.length)}`
|
||||
|
||||
const DetailsContainer = ({ asset, collection }: { asset: GenieAsset; collection: CollectionInfoForAsset }) => {
|
||||
const { formatNumber } = useFormatter()
|
||||
const { address, tokenId, tokenType, creator } = asset
|
||||
const { totalSupply } = collection
|
||||
|
||||
@ -87,7 +88,10 @@ const DetailsContainer = ({ asset, collection }: { asset: GenieAsset; collection
|
||||
<GridItem header="Token ID" body={tokenId.length > 9 ? stringShortener(tokenId) : tokenId} />
|
||||
<GridItem header="Token standard" body={tokenType} />
|
||||
<GridItem header="Blockchain" body="Ethereum" />
|
||||
<GridItem header="Total supply" body={`${putCommas(totalSupply ?? 0)}`} />
|
||||
<GridItem
|
||||
header="Total supply"
|
||||
body={`${formatNumber({ input: totalSupply ?? 0, type: NumberType.WholeNumber })}`}
|
||||
/>
|
||||
<GridItem
|
||||
header="Creator"
|
||||
body={
|
||||
|
@ -3,7 +3,6 @@ import { LoadingBubble } from 'components/Tokens/loading'
|
||||
import { useCollection } from 'graphql/data/nft/Collection'
|
||||
import { UniswapMagentaIcon, VerifiedIcon } from 'nft/components/icons'
|
||||
import { Markets, TrendingCollection } from 'nft/types'
|
||||
import { ethNumberStandardFormatter } from 'nft/utils'
|
||||
import styled from 'styled-components'
|
||||
import { ThemedText } from 'theme/components/text'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
@ -239,6 +238,7 @@ const MARKETS_ENUM_TO_NAME = {
|
||||
|
||||
export const CarouselCard = ({ collection, onClick }: CarouselCardProps) => {
|
||||
const { data: gqlCollection, loading } = useCollection(collection.address ?? '')
|
||||
const { formatNumber } = useFormatter()
|
||||
|
||||
if (loading) return <LoadingCarouselCard />
|
||||
|
||||
@ -257,7 +257,7 @@ export const CarouselCard = ({ collection, onClick }: CarouselCardProps) => {
|
||||
<TableElement>
|
||||
{collection.floor && (
|
||||
<ThemedText.SubHeaderSmall color="userThemeColor">
|
||||
{ethNumberStandardFormatter(collection.floor)} ETH Floor
|
||||
{formatNumber({ input: collection.floor, type: NumberType.NFTToken })} ETH Floor
|
||||
</ThemedText.SubHeaderSmall>
|
||||
)}
|
||||
</TableElement>
|
||||
|
@ -3,10 +3,10 @@ import { DeltaArrow } from 'components/Tokens/TokenDetails/Delta'
|
||||
import { VerifiedIcon } from 'nft/components/icons'
|
||||
import { useIsMobile } from 'nft/hooks'
|
||||
import { Denomination } from 'nft/types'
|
||||
import { ethNumberStandardFormatter, volumeFormatter } from 'nft/utils'
|
||||
import { ReactNode } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
import * as styles from './Cells.css'
|
||||
|
||||
@ -93,9 +93,12 @@ export const CollectionTitleCell = ({ value }: CellProps) => {
|
||||
)
|
||||
}
|
||||
|
||||
export const DiscreteNumberCell = ({ value }: CellProps) => (
|
||||
<span>{value.value ? volumeFormatter(value.value) : '-'}</span>
|
||||
export const DiscreteNumberCell = ({ value }: CellProps) => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
return (
|
||||
<span>{value.value ? formatNumberOrString({ input: value.value, type: NumberType.NFTCollectionStats }) : '-'}</span>
|
||||
)
|
||||
}
|
||||
|
||||
const getDenominatedValue = (denomination: Denomination, inWei: boolean, value?: number, usdPrice?: number) => {
|
||||
if (denomination === Denomination.ETH) return value
|
||||
@ -113,12 +116,14 @@ export const EthCell = ({
|
||||
denomination: Denomination
|
||||
usdPrice?: number
|
||||
}) => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const denominatedValue = getDenominatedValue(denomination, false, value, usdPrice)
|
||||
const formattedValue = denominatedValue
|
||||
? denomination === Denomination.ETH
|
||||
? ethNumberStandardFormatter(denominatedValue.toString(), false, true, false) + ' ETH'
|
||||
: ethNumberStandardFormatter(denominatedValue, true, false, true)
|
||||
: '-'
|
||||
const ethDenomination = denomination === Denomination.ETH
|
||||
const formattedValue =
|
||||
formatNumberOrString({
|
||||
input: denominatedValue,
|
||||
type: ethDenomination ? NumberType.NFTToken : NumberType.FiatTokenStats,
|
||||
}) + (ethDenomination ? ' ETH' : '')
|
||||
|
||||
const isMobile = useIsMobile()
|
||||
const TextComponent = isMobile ? ThemedText.BodySmall : ThemedText.BodyPrimary
|
||||
@ -141,17 +146,19 @@ export const VolumeCell = ({
|
||||
denomination: Denomination
|
||||
usdPrice?: number
|
||||
}) => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const denominatedValue = getDenominatedValue(denomination, false, value, usdPrice)
|
||||
const ethDenomination = denomination === Denomination.ETH
|
||||
|
||||
const formattedValue = denominatedValue
|
||||
? denomination === Denomination.ETH
|
||||
? ethNumberStandardFormatter(denominatedValue.toString(), false, false, true) + ' ETH'
|
||||
: ethNumberStandardFormatter(denominatedValue, true, false, true)
|
||||
: '-'
|
||||
const formattedValue =
|
||||
formatNumberOrString({
|
||||
input: denominatedValue,
|
||||
type: ethDenomination ? NumberType.WholeNumber : NumberType.FiatTokenStats,
|
||||
}) + (ethDenomination ? ' ETH' : '')
|
||||
|
||||
return (
|
||||
<EthContainer>
|
||||
<ThemedText.BodyPrimary>{value ? formattedValue : '-'}</ThemedText.BodyPrimary>
|
||||
<ThemedText.BodyPrimary>{formattedValue}</ThemedText.BodyPrimary>
|
||||
</EthContainer>
|
||||
)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import { CollectionTableColumn, Denomination, TimePeriod, VolumeType } from 'nft
|
||||
import { useMemo, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { useFormatterLocales } from 'utils/formatNumbers'
|
||||
|
||||
import CollectionTable from './CollectionTable'
|
||||
|
||||
@ -84,6 +85,7 @@ function convertTimePeriodToHistoryDuration(timePeriod: TimePeriod): HistoryDura
|
||||
}
|
||||
|
||||
const TrendingCollections = () => {
|
||||
const { formatterLocalCurrency } = useFormatterLocales()
|
||||
const [timePeriod, setTimePeriod] = useState<TimePeriod>(TimePeriod.OneDay)
|
||||
const [isEthToggled, setEthToggled] = useState(true)
|
||||
|
||||
@ -151,7 +153,7 @@ const TrendingCollections = () => {
|
||||
</Selector>
|
||||
<Selector active={!isEthToggled}>
|
||||
<StyledSelectorText lineHeight="20px" active={!isEthToggled}>
|
||||
USD
|
||||
{formatterLocalCurrency}
|
||||
</StyledSelectorText>
|
||||
</Selector>
|
||||
</Filter>
|
||||
|
@ -1,8 +1,12 @@
|
||||
import { isNumber } from 'nft/utils/numbers'
|
||||
import { FormEvent, forwardRef } from 'react'
|
||||
|
||||
import { Box, BoxProps } from '../Box'
|
||||
|
||||
const isNumber = (s: string): boolean => {
|
||||
const reg = /^-?\d+\.?\d*$/
|
||||
return reg.test(s) && !isNaN(parseFloat(s)) && isFinite(parseFloat(s))
|
||||
}
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, BoxProps>((props, ref) => (
|
||||
<Box
|
||||
ref={ref}
|
||||
|
@ -18,7 +18,6 @@ import { useIsMobile, useNFTList, useProfilePageState, useSellAsset } from 'nft/
|
||||
import { LIST_PAGE_MARGIN, LIST_PAGE_MARGIN_MOBILE } from 'nft/pages/profile/shared'
|
||||
import { looksRareNonceFetcher } from 'nft/queries/looksRare'
|
||||
import { ProfilePageStateType } from 'nft/types'
|
||||
import { formatEth } from 'nft/utils'
|
||||
import { ListingMarkets } from 'nft/utils/listNfts'
|
||||
import { useEffect, useMemo, useReducer, useState } from 'react'
|
||||
import { ArrowLeft } from 'react-feather'
|
||||
@ -183,6 +182,7 @@ const EthValueWrapper = styled.span<{ totalEthListingValue: boolean }>`
|
||||
`
|
||||
|
||||
export const ListPage = () => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const { setProfilePageState: setSellPageState } = useProfilePageState()
|
||||
const { provider, chainId } = useWeb3React()
|
||||
const isMobile = useIsMobile()
|
||||
@ -295,7 +295,10 @@ export const ListPage = () => {
|
||||
<ProceedsAndButtonWrapper>
|
||||
<ProceedsWrapper>
|
||||
<EthValueWrapper totalEthListingValue={!!totalEthListingValue}>
|
||||
{totalEthListingValue > 0 ? formatEth(totalEthListingValue) : '-'} ETH
|
||||
{totalEthListingValue > 0
|
||||
? formatNumberOrString({ input: totalEthListingValue, type: NumberType.NFTToken })
|
||||
: '-'}{' '}
|
||||
ETH
|
||||
</EthValueWrapper>
|
||||
{!!usdcValue && <UsdValue>{usdcAmount}</UsdValue>}
|
||||
</ProceedsWrapper>
|
||||
|
@ -8,11 +8,11 @@ import { useSellAsset } from 'nft/hooks'
|
||||
import { useNativeUsdPrice } from 'nft/hooks/useUsdPrice'
|
||||
import { ListingMarket, WalletAsset } from 'nft/types'
|
||||
import { getMarketplaceIcon } from 'nft/utils'
|
||||
import { formatEth, formatUsdPrice } from 'nft/utils/currency'
|
||||
import { Dispatch, DispatchWithoutAction, useCallback, useEffect, useMemo, useReducer, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { BREAKPOINTS } from 'theme'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
import { PriceTextInput } from './PriceTextInput'
|
||||
import { RoyaltyTooltip } from './RoyaltyTooltip'
|
||||
@ -126,6 +126,7 @@ export const MarketplaceRow = ({
|
||||
toggleExpandMarketplaceRows,
|
||||
rowHovered,
|
||||
}: MarketplaceRowProps) => {
|
||||
const { formatNumberOrString, formatDelta } = useFormatter()
|
||||
const setAssetListPrice = useSellAsset((state) => state.setAssetListPrice)
|
||||
const removeAssetMarketplace = useSellAsset((state) => state.removeAssetMarketplace)
|
||||
const [marketIconHovered, toggleMarketIconHovered] = useReducer((s) => !s, false)
|
||||
@ -184,12 +185,17 @@ export const MarketplaceRow = ({
|
||||
<Row onMouseEnter={toggleMarketRowHovered} onMouseLeave={toggleMarketRowHovered}>
|
||||
<FloorPriceInfo>
|
||||
<ThemedText.BodyPrimary color="neutral2" lineHeight="24px">
|
||||
{asset.floorPrice ? `${asset.floorPrice.toFixed(3)} ETH` : '-'}
|
||||
{formatNumberOrString({
|
||||
input: asset.floorPrice,
|
||||
type: NumberType.NFTToken,
|
||||
}) + asset.floorPrice
|
||||
? ' ETH'
|
||||
: ''}
|
||||
</ThemedText.BodyPrimary>
|
||||
</FloorPriceInfo>
|
||||
<LastPriceInfo>
|
||||
<ThemedText.BodyPrimary color="neutral2" lineHeight="24px">
|
||||
{asset.lastPrice ? `${asset.lastPrice.toFixed(3)} ETH` : '-'}
|
||||
{asset.lastPrice ? `${formatNumberOrString({ input: asset.lastPrice, type: NumberType.NFTToken })} ETH` : '-'}
|
||||
</ThemedText.BodyPrimary>
|
||||
</LastPriceInfo>
|
||||
|
||||
@ -235,7 +241,7 @@ export const MarketplaceRow = ({
|
||||
>
|
||||
<FeeWrapper>
|
||||
<ThemedText.BodyPrimary color="neutral2">
|
||||
{fees > 0 ? `${fees.toFixed(2)}${selectedMarkets.length > 1 ? t`% max` : '%'}` : '--%'}
|
||||
{fees > 0 ? `${formatDelta(fees)}${selectedMarkets.length > 1 ? t`max` : ''}` : '--%'}
|
||||
</ThemedText.BodyPrimary>
|
||||
</FeeWrapper>
|
||||
</MouseoverTooltip>
|
||||
@ -249,6 +255,7 @@ export const MarketplaceRow = ({
|
||||
}
|
||||
|
||||
const EthPriceDisplay = ({ ethPrice = 0 }: { ethPrice?: number }) => {
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const ethUsdPrice = useNativeUsdPrice()
|
||||
|
||||
return (
|
||||
@ -256,8 +263,10 @@ const EthPriceDisplay = ({ ethPrice = 0 }: { ethPrice?: number }) => {
|
||||
<ThemedText.BodyPrimary lineHeight="24px" color={ethPrice ? 'neutral1' : 'neutral2'} textAlign="right">
|
||||
{ethPrice !== 0 ? (
|
||||
<Column>
|
||||
<span>{formatEth(ethPrice)} ETH</span>
|
||||
<ThemedText.BodyPrimary color="neutral2">{formatUsdPrice(ethPrice * ethUsdPrice)}</ThemedText.BodyPrimary>
|
||||
<span>{formatNumberOrString({ input: ethPrice, type: NumberType.NFTToken })} ETH</span>
|
||||
<ThemedText.BodyPrimary color="neutral2">
|
||||
{formatNumberOrString({ input: ethPrice * ethUsdPrice, type: NumberType.FiatNFTToken })}
|
||||
</ThemedText.BodyPrimary>
|
||||
</Column>
|
||||
) : (
|
||||
'- ETH'
|
||||
|
@ -10,6 +10,7 @@ import styled, { useTheme } from 'styled-components'
|
||||
import { BREAKPOINTS } from 'theme'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { Z_INDEX } from 'theme/zIndex'
|
||||
import { useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
const ModalWrapper = styled(Column)`
|
||||
position: fixed;
|
||||
@ -82,6 +83,7 @@ export const BelowFloorWarningModal = ({
|
||||
startListing: () => void
|
||||
}) => {
|
||||
const theme = useTheme()
|
||||
const { formatDelta } = useFormatter()
|
||||
const clickContinue = (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@ -103,10 +105,9 @@ export const BelowFloorWarningModal = ({
|
||||
<ThemedText.BodyPrimary textAlign="center">
|
||||
<Plural
|
||||
value={listingsBelowFloor.length !== 1 ? 2 : 1}
|
||||
_1={t`One NFT is listed ${(
|
||||
(1 - (listingsBelowFloor[0][1].price ?? 0) / (listingsBelowFloor[0][0].floorPrice ?? 0)) *
|
||||
100
|
||||
).toFixed(0)}% `}
|
||||
_1={t`One NFT is listed ${formatDelta(
|
||||
(1 - (listingsBelowFloor[0][1].price ?? 0) / (listingsBelowFloor[0][0].floorPrice ?? 0)) * 100
|
||||
)} `}
|
||||
other={t`${listingsBelowFloor.length} NFTs are listed significantly `}
|
||||
/>
|
||||
|
||||
|
@ -8,7 +8,7 @@ import useNativeCurrency from 'lib/hooks/useNativeCurrency'
|
||||
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
|
||||
import { getTotalEthValue } from 'nft/components/profile/list/utils'
|
||||
import { useSellAsset } from 'nft/hooks'
|
||||
import { formatEth, generateTweetForList, pluralize } from 'nft/utils'
|
||||
import { generateTweetForList, pluralize } from 'nft/utils'
|
||||
import { useMemo } from 'react'
|
||||
import { Twitter, X } from 'react-feather'
|
||||
import styled, { css, useTheme } from 'styled-components'
|
||||
@ -77,6 +77,7 @@ const TweetRow = styled(Row)`
|
||||
|
||||
export const SuccessScreen = ({ overlayClick }: { overlayClick: () => void }) => {
|
||||
const theme = useTheme()
|
||||
const { formatNumberOrString } = useFormatter()
|
||||
const sellAssets = useSellAsset((state) => state.sellAssets)
|
||||
const { chainId } = useWeb3React()
|
||||
const nativeCurrency = useNativeCurrency(chainId)
|
||||
@ -109,7 +110,9 @@ export const SuccessScreen = ({ overlayClick }: { overlayClick: () => void }) =>
|
||||
<Trans>Proceeds if sold</Trans>
|
||||
</ThemedText.SubHeader>
|
||||
<ProceedsColumn>
|
||||
<ThemedText.SubHeader>{formatEth(totalEthListingValue)} ETH</ThemedText.SubHeader>
|
||||
<ThemedText.SubHeader>
|
||||
{formatNumberOrString({ input: totalEthListingValue, type: NumberType.NFTToken })} ETH
|
||||
</ThemedText.SubHeader>
|
||||
{usdcValue && (
|
||||
<ThemedText.BodySmall lineHeight="20px" color="neutral2">
|
||||
{formatCurrencyAmount({
|
||||
|
@ -7,12 +7,12 @@ import { useUpdateInputAndWarnings } from 'nft/components/profile/list/utils'
|
||||
import { body } from 'nft/css/common.css'
|
||||
import { useSellAsset } from 'nft/hooks'
|
||||
import { WalletAsset } from 'nft/types'
|
||||
import { formatEth } from 'nft/utils/currency'
|
||||
import { Dispatch, useRef, useState } from 'react'
|
||||
import { AlertTriangle, Link } from 'react-feather'
|
||||
import styled, { useTheme } from 'styled-components'
|
||||
import { BREAKPOINTS } from 'theme'
|
||||
import { colors } from 'theme/colors'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
import { WarningType } from './shared'
|
||||
|
||||
@ -104,6 +104,7 @@ export const PriceTextInput = ({
|
||||
globalOverride,
|
||||
asset,
|
||||
}: PriceTextInputProps) => {
|
||||
const { formatNumberOrString, formatDelta } = useFormatter()
|
||||
const [warningType, setWarningType] = useState(WarningType.NONE)
|
||||
const removeSellAsset = useSellAsset((state) => state.removeSellAsset)
|
||||
const showResolveIssues = useSellAsset((state) => state.showResolveIssues)
|
||||
@ -159,10 +160,14 @@ export const PriceTextInput = ({
|
||||
<WarningRow>
|
||||
<AlertTriangle height={16} width={16} color={warningColor} />
|
||||
<span>
|
||||
{warningType === WarningType.BELOW_FLOOR && `${percentBelowFloor.toFixed(0)}% `}
|
||||
{warningType === WarningType.BELOW_FLOOR && `${formatDelta(percentBelowFloor)} `}
|
||||
{getWarningMessage(warningType)}
|
||||
|
||||
{warningType === WarningType.ALREADY_LISTED && `${formatEth(asset?.floor_sell_order_price ?? 0)} ETH`}
|
||||
{warningType === WarningType.ALREADY_LISTED &&
|
||||
`${formatNumberOrString({
|
||||
input: asset?.floor_sell_order_price ?? 0,
|
||||
type: NumberType.NFTToken,
|
||||
})} ETH`}
|
||||
</span>
|
||||
<WarningAction
|
||||
onClick={() => {
|
||||
|
@ -3,9 +3,10 @@ import Column from 'components/Column'
|
||||
import Row from 'components/Row'
|
||||
import { getRoyalty } from 'nft/components/profile/list/utils'
|
||||
import { ListingMarket, WalletAsset } from 'nft/types'
|
||||
import { formatEth, getMarketplaceIcon } from 'nft/utils'
|
||||
import { getMarketplaceIcon } from 'nft/utils'
|
||||
import styled, { css } from 'styled-components'
|
||||
import { ThemedText } from 'theme/components'
|
||||
import { NumberType, useFormatter } from 'utils/formatNumbers'
|
||||
|
||||
const FeeWrap = styled(Row)`
|
||||
margin-bottom: 4px;
|
||||
@ -56,7 +57,8 @@ export const RoyaltyTooltip = ({
|
||||
asset: WalletAsset
|
||||
fees?: number
|
||||
}) => {
|
||||
const maxRoyalty = Math.max(...selectedMarkets.map((market) => getRoyalty(market, asset) ?? 0)).toFixed(2)
|
||||
const { formatNumberOrString, formatDelta } = useFormatter()
|
||||
const maxRoyalty = Math.max(...selectedMarkets.map((market) => getRoyalty(market, asset) ?? 0))
|
||||
return (
|
||||
<RoyaltyContainer>
|
||||
{selectedMarkets.map((market) => (
|
||||
@ -68,7 +70,7 @@ export const RoyaltyTooltip = ({
|
||||
<Trans>fee</Trans>
|
||||
</ThemedText.BodySmall>
|
||||
</Row>
|
||||
<FeePercent>{market.fee}%</FeePercent>
|
||||
<FeePercent>{formatDelta(market.fee)}</FeePercent>
|
||||
</FeeWrap>
|
||||
))}
|
||||
<FeeWrap>
|
||||
@ -85,7 +87,7 @@ export const RoyaltyTooltip = ({
|
||||
<Trans>Max fees</Trans>
|
||||
</ThemedText.BodySmall>
|
||||
<ThemedText.BodySmall lineHeight="16px" color={fees ? 'neutral1' : 'neutral2'}>
|
||||
{fees ? formatEth(fees) : '-'} ETH
|
||||
{fees ? formatNumberOrString({ input: fees, type: NumberType.NFTToken }) : '-'} ETH
|
||||
</ThemedText.BodySmall>
|
||||
</MaxFeeContainer>
|
||||
</RoyaltyContainer>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Percent } from '@uniswap/sdk-core'
|
||||
import { useMemo } from 'react'
|
||||
import { ClassicTrade } from 'state/routing/types'
|
||||
import { useTheme } from 'styled-components'
|
||||
import { useFormatter } from 'utils/formatNumbers'
|
||||
import { computeRealizedPriceImpact, getPriceImpactWarning } from 'utils/prices'
|
||||
|
||||
export interface PriceImpact {
|
||||
@ -16,6 +16,7 @@ interface PriceImpactSeverity {
|
||||
|
||||
export function usePriceImpact(trade?: ClassicTrade): PriceImpact | undefined {
|
||||
const theme = useTheme()
|
||||
const { formatPercent } = useFormatter()
|
||||
|
||||
return useMemo(() => {
|
||||
const marketPriceImpact = trade ? computeRealizedPriceImpact(trade) : undefined
|
||||
@ -33,18 +34,8 @@ export function usePriceImpact(trade?: ClassicTrade): PriceImpact | undefined {
|
||||
type: priceImpactWarning,
|
||||
color: warningColor,
|
||||
},
|
||||
displayPercentage: () => toHumanReadablePercent(marketPriceImpact),
|
||||
displayPercentage: () => formatPercent(marketPriceImpact),
|
||||
}
|
||||
: undefined
|
||||
}, [theme.critical, theme.deprecated_accentWarning, trade])
|
||||
}
|
||||
|
||||
function toHumanReadablePercent(priceImpact: Percent): string {
|
||||
const sign = priceImpact.lessThan(0) ? '+' : ''
|
||||
const exactFloat = (Number(priceImpact.numerator) / Number(priceImpact.denominator)) * 100
|
||||
if (exactFloat < 0.005) {
|
||||
return '0.00%'
|
||||
}
|
||||
const number = parseFloat(priceImpact.multiply(-1)?.toFixed(2))
|
||||
return `${sign}${number}%`
|
||||
}, [formatPercent, theme.critical, theme.deprecated_accentWarning, trade])
|
||||
}
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { parseEther } from 'ethers/lib/utils'
|
||||
import { ActivityEvent, GenieAsset } from 'nft/types'
|
||||
|
||||
import { formatEth } from './currency'
|
||||
|
||||
export const buildActivityAsset = (event: ActivityEvent, collectionName: string, ethPriceInUSD: number): GenieAsset => {
|
||||
const assetUsdPrice = event.price ? formatEth(parseFloat(event.price) * ethPriceInUSD) : '0'
|
||||
const assetUsdPrice = event.price ? parseFloat(event.price) * ethPriceInUSD : '0'
|
||||
|
||||
const weiPrice = event.price ? parseEther(event.price) : ''
|
||||
|
||||
|
@ -1,74 +1,3 @@
|
||||
import { formatEther } from '@ethersproject/units'
|
||||
|
||||
export const formatUsdPrice = (price: number) => {
|
||||
if (price > 1000000) {
|
||||
return `$${(price / 1000000).toFixed(1)}M`
|
||||
} else if (price > 1000) {
|
||||
return `$${(price / 1000).toFixed(1)}K`
|
||||
} else {
|
||||
return `$${price.toFixed(2)}`
|
||||
}
|
||||
}
|
||||
|
||||
export const formatEth = (price: number) => {
|
||||
if (price > 1000000) {
|
||||
return `${Math.round(price / 1000000)}M`
|
||||
} else if (price > 1000) {
|
||||
return `${Math.round(price / 1000)}K`
|
||||
} else if (price < 0.001) {
|
||||
return '<0.001'
|
||||
} else {
|
||||
return `${Math.round(price * 1000 + Number.EPSILON) / 1000}`
|
||||
}
|
||||
}
|
||||
|
||||
export const formatUSDPriceWithCommas = (price: number) => {
|
||||
return `$${Math.round(price)
|
||||
.toString()
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`
|
||||
}
|
||||
|
||||
export const formatEthPrice = (price: string | undefined) => {
|
||||
if (!price) return 0
|
||||
|
||||
const formattedPrice = parseFloat(formatEther(String(price)))
|
||||
return (
|
||||
Math.round(formattedPrice * (formattedPrice >= 1 ? 100 : 1000) + Number.EPSILON) /
|
||||
(formattedPrice >= 1 ? 100 : 1000)
|
||||
)
|
||||
}
|
||||
|
||||
export const ethNumberStandardFormatter = (
|
||||
amount: string | number | undefined,
|
||||
includeDollarSign = false,
|
||||
removeZeroes = false,
|
||||
roundToNearestWholeNumber = false
|
||||
): string => {
|
||||
if (!amount) return '-'
|
||||
|
||||
const amountInDecimals = parseFloat(amount.toString())
|
||||
const conditionalDollarSign = includeDollarSign ? '$' : ''
|
||||
|
||||
if (amountInDecimals <= 0) return '-'
|
||||
if (amountInDecimals < 0.0001) return `< ${conditionalDollarSign}0.00001`
|
||||
if (amountInDecimals < 1) return `${conditionalDollarSign}${parseFloat(amountInDecimals.toFixed(3))}`
|
||||
const formattedPrice = (
|
||||
removeZeroes
|
||||
? parseFloat(amountInDecimals.toFixed(2))
|
||||
: roundToNearestWholeNumber
|
||||
? Math.round(amountInDecimals)
|
||||
: amountInDecimals.toFixed(2)
|
||||
)
|
||||
.toString()
|
||||
.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
return conditionalDollarSign + formattedPrice
|
||||
}
|
||||
|
||||
export const formatWeiToDecimal = (amount: string, removeZeroes = false) => {
|
||||
if (!amount) return '-'
|
||||
return ethNumberStandardFormatter(formatEther(amount), false, removeZeroes, false)
|
||||
}
|
||||
|
||||
// prevent BigNumber overflow by properly handling scientific notation and comma delimited values
|
||||
export function wrapScientificNotation(value: string | number): string {
|
||||
return parseFloat(value.toString())
|
||||
|
@ -10,21 +10,11 @@ export { blocklistedCollections } from './blocklist'
|
||||
export { buildNftTradeInputFromBagItems } from './buildSellObject'
|
||||
export { calculateCardIndex, calculateFirstCardIndex, calculateRank } from './carousel'
|
||||
export { isInSameMarketplaceCollection, isInSameSudoSwapPool } from './collection'
|
||||
export {
|
||||
ethNumberStandardFormatter,
|
||||
formatEth,
|
||||
formatEthPrice,
|
||||
formatUsdPrice,
|
||||
formatUSDPriceWithCommas,
|
||||
formatWeiToDecimal,
|
||||
wrapScientificNotation,
|
||||
} from './currency'
|
||||
export { wrapScientificNotation } from './currency'
|
||||
export { formatAssetEventProperties } from './formatEventProperties'
|
||||
export { isAudio } from './isAudio'
|
||||
export { isVideo } from './isVideo'
|
||||
export { floorFormatter, volumeFormatter } from './numbers'
|
||||
export { calcAvgGroupPoolPrice, calcPoolPrice, recalculateBagUsingPooledAssets } from './pooledAssets'
|
||||
export { putCommas } from './putCommas'
|
||||
export { pluralize, roundAndPluralize } from './roundAndPluralize'
|
||||
export { timeLeft } from './time'
|
||||
export { getSuccessfulImageSize, parseTransactionResponse } from './transactionResponse'
|
||||
|
@ -1,104 +1,3 @@
|
||||
import { DEFAULT_LOCALE } from 'constants/locales'
|
||||
import numbro from 'numbro'
|
||||
|
||||
export const isNumber = (s: string): boolean => {
|
||||
const reg = /^-?\d+\.?\d*$/
|
||||
return reg.test(s) && !isNaN(parseFloat(s)) && isFinite(parseFloat(s))
|
||||
}
|
||||
|
||||
export const floorFormatter = (n: number): string => {
|
||||
if (n === 0) return '0.00'
|
||||
if (!n) return ''
|
||||
if (n < 0.001) {
|
||||
return '<0.001'
|
||||
}
|
||||
if (n >= 0.001 && n < 1) {
|
||||
return `${parseFloat(n.toFixed(3)).toLocaleString(DEFAULT_LOCALE, {
|
||||
minimumFractionDigits: 1,
|
||||
maximumFractionDigits: 3,
|
||||
})}`
|
||||
}
|
||||
if (n >= 1 && n < 1e6) {
|
||||
return `${parseFloat(n.toPrecision(6)).toLocaleString(DEFAULT_LOCALE, {
|
||||
minimumFractionDigits: 0,
|
||||
maximumFractionDigits: 2,
|
||||
})}`
|
||||
}
|
||||
if (n >= 1e6 && n < 1e15) {
|
||||
return numbro(n)
|
||||
.format({
|
||||
average: true,
|
||||
mantissa: 2,
|
||||
optionalMantissa: true,
|
||||
abbreviations: {
|
||||
million: 'M',
|
||||
billion: 'B',
|
||||
trillion: 'T',
|
||||
},
|
||||
})
|
||||
.toUpperCase()
|
||||
}
|
||||
if (n >= 1e15) {
|
||||
return `${n.toExponential(3).replace(/(\.[0-9]*[1-9])0*|(\.0*)/, '$1')}`
|
||||
}
|
||||
return `${Number(n.toFixed(2)).toLocaleString(DEFAULT_LOCALE, { minimumFractionDigits: 2 })}`
|
||||
}
|
||||
|
||||
export const volumeFormatter = (n: number): string => {
|
||||
if (n === 0) return '0.00'
|
||||
if (!n) return ''
|
||||
if (n < 0.01) {
|
||||
return '<0.01'
|
||||
}
|
||||
if (n >= 0.01 && n < 1) {
|
||||
return `${parseFloat(n.toFixed(2)).toLocaleString(DEFAULT_LOCALE)}`
|
||||
}
|
||||
if (n >= 1 && n < 1000) {
|
||||
return `${Number(Math.round(n).toLocaleString(DEFAULT_LOCALE))}`
|
||||
}
|
||||
if (n >= 1000) {
|
||||
return numbro(n)
|
||||
.format({
|
||||
average: true,
|
||||
mantissa: 1,
|
||||
optionalMantissa: true,
|
||||
abbreviations: {
|
||||
thousand: 'K',
|
||||
million: 'M',
|
||||
billion: 'B',
|
||||
trillion: 'T',
|
||||
},
|
||||
})
|
||||
.toUpperCase()
|
||||
}
|
||||
return `${Number(n.toFixed(1)).toLocaleString(DEFAULT_LOCALE, { minimumFractionDigits: 1 })}`
|
||||
}
|
||||
|
||||
export const quantityFormatter = (n: number): string => {
|
||||
if (n === 0) return '0.00'
|
||||
if (!n) return ''
|
||||
if (n >= 1 && n < 1000) {
|
||||
return `${Number(Math.round(n).toLocaleString(DEFAULT_LOCALE))}`
|
||||
}
|
||||
if (n >= 1000) {
|
||||
return numbro(n)
|
||||
.format({
|
||||
average: true,
|
||||
mantissa: 1,
|
||||
thousandSeparated: true,
|
||||
optionalMantissa: true,
|
||||
abbreviations: {
|
||||
thousand: 'K',
|
||||
million: 'M',
|
||||
billion: 'B',
|
||||
trillion: 'T',
|
||||
},
|
||||
})
|
||||
.toUpperCase()
|
||||
}
|
||||
return `${Number(n.toFixed(2)).toLocaleString(DEFAULT_LOCALE, { minimumFractionDigits: 2 })}`
|
||||
}
|
||||
|
||||
export const roundWholePercentage = (n: number): string => {
|
||||
if (n === 0) return '0'
|
||||
if (!n) return ''
|
||||
|
@ -1,8 +0,0 @@
|
||||
export const putCommas = (value: number) => {
|
||||
try {
|
||||
if (!value) return value
|
||||
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
|
||||
} catch (err) {
|
||||
return value
|
||||
}
|
||||
}
|
@ -1,10 +1,6 @@
|
||||
import { formatEther } from '@ethersproject/units'
|
||||
import { BuyItem, GenieAsset, isPooledMarket, Markets, PriceInfo, RoutingItem, UpdatedGenieAsset } from 'nft/types'
|
||||
import {
|
||||
calcAvgGroupPoolPrice,
|
||||
formatWeiToDecimal,
|
||||
isInSameMarketplaceCollection,
|
||||
isInSameSudoSwapPool,
|
||||
} from 'nft/utils'
|
||||
import { calcAvgGroupPoolPrice, isInSameMarketplaceCollection, isInSameSudoSwapPool } from 'nft/utils'
|
||||
|
||||
const isTheSame = (item: GenieAsset, routeAsset: BuyItem | PriceInfo) => {
|
||||
// if route asset has id, match by id
|
||||
@ -21,7 +17,7 @@ const isTheSame = (item: GenieAsset, routeAsset: BuyItem | PriceInfo) => {
|
||||
|
||||
const getPriceDiff = (oldPrice: string, newPrice: string): { hasPriceDiff: boolean; hasVisiblePriceDiff: boolean } => {
|
||||
const hasPriceDiff = oldPrice !== newPrice
|
||||
const hasVisiblePriceDiff = formatWeiToDecimal(oldPrice) !== formatWeiToDecimal(newPrice)
|
||||
const hasVisiblePriceDiff = formatEther(oldPrice) !== formatEther(newPrice)
|
||||
|
||||
return { hasPriceDiff, hasVisiblePriceDiff }
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { formatEther as ethersFormatEther } from '@ethersproject/units'
|
||||
import { Currency, CurrencyAmount, Percent, Price, Token } from '@uniswap/sdk-core'
|
||||
import {
|
||||
DEFAULT_LOCAL_CURRENCY,
|
||||
@ -67,11 +68,23 @@ const THREE_DECIMALS_CURRENCY: NumberFormatOptions = {
|
||||
style: 'currency',
|
||||
}
|
||||
|
||||
const THREE_DECIMALS_NO_TRAILING_ZEROS_CURRENCY: NumberFormatOptions = {
|
||||
...THREE_DECIMALS_NO_TRAILING_ZEROS,
|
||||
currency: 'USD',
|
||||
style: 'currency',
|
||||
}
|
||||
|
||||
const TWO_DECIMALS_NO_TRAILING_ZEROS: NumberFormatOptions = {
|
||||
notation: 'standard',
|
||||
maximumFractionDigits: 2,
|
||||
}
|
||||
|
||||
const TWO_DECIMALS_NO_TRAILING_ZEROS_CURRENCY: NumberFormatOptions = {
|
||||
...TWO_DECIMALS_NO_TRAILING_ZEROS,
|
||||
currency: 'USD',
|
||||
style: 'currency',
|
||||
}
|
||||
|
||||
const TWO_DECIMALS: NumberFormatOptions = {
|
||||
notation: 'standard',
|
||||
maximumFractionDigits: 2,
|
||||
@ -97,6 +110,12 @@ const SHORTHAND_TWO_DECIMALS_NO_TRAILING_ZEROS: NumberFormatOptions = {
|
||||
maximumFractionDigits: 2,
|
||||
}
|
||||
|
||||
const SHORTHAND_TWO_DECIMALS_NO_TRAILING_ZEROS_CURRENCY: NumberFormatOptions = {
|
||||
...SHORTHAND_TWO_DECIMALS_NO_TRAILING_ZEROS,
|
||||
currency: 'USD',
|
||||
style: 'currency',
|
||||
}
|
||||
|
||||
const SHORTHAND_ONE_DECIMAL: NumberFormatOptions = {
|
||||
notation: 'compact',
|
||||
minimumFractionDigits: 1,
|
||||
@ -317,6 +336,42 @@ const ntfCollectionStatsFormatter: FormatterRule[] = [
|
||||
{ upperBound: Infinity, formatterOptions: SHORTHAND_ONE_DECIMAL },
|
||||
]
|
||||
|
||||
const nftTokenFormatter: FormatterRule[] = [
|
||||
{ exact: 0, hardCodedInput: { hardcodedOutput: '-' }, formatterOptions: FIVE_DECIMALS_MAX_TWO_DECIMALS_MIN },
|
||||
{
|
||||
upperBound: 0.0001,
|
||||
hardCodedInput: { input: 0.0001, prefix: '<' },
|
||||
formatterOptions: FIVE_DECIMALS_MAX_TWO_DECIMALS_MIN,
|
||||
},
|
||||
{ upperBound: 1.0, formatterOptions: THREE_DECIMALS },
|
||||
{ upperBound: 1000, formatterOptions: TWO_DECIMALS_NO_TRAILING_ZEROS },
|
||||
{ upperBound: 1e15, formatterOptions: SHORTHAND_TWO_DECIMALS_NO_TRAILING_ZEROS },
|
||||
{
|
||||
upperBound: Infinity,
|
||||
hardCodedInput: { input: 999_000_000_000_000, prefix: '>' },
|
||||
formatterOptions: SHORTHAND_TWO_DECIMALS_NO_TRAILING_ZEROS,
|
||||
},
|
||||
]
|
||||
|
||||
const fiatNftTokenFormatter: FormatterRule[] = [
|
||||
{ exact: 0, hardCodedInput: { hardcodedOutput: '-' }, formatterOptions: NO_DECIMALS },
|
||||
{
|
||||
upperBound: 0.0001,
|
||||
hardCodedInput: { input: 0.0001, prefix: '<' },
|
||||
formatterOptions: ONE_SIG_FIG_CURRENCY,
|
||||
},
|
||||
{ upperBound: 1.0, formatterOptions: THREE_DECIMALS_NO_TRAILING_ZEROS_CURRENCY },
|
||||
{ upperBound: 1000, formatterOptions: TWO_DECIMALS_NO_TRAILING_ZEROS_CURRENCY },
|
||||
{ upperBound: 1e15, formatterOptions: SHORTHAND_TWO_DECIMALS_NO_TRAILING_ZEROS_CURRENCY },
|
||||
{
|
||||
upperBound: Infinity,
|
||||
hardCodedInput: { input: 999_000_000_000_000, prefix: '>' },
|
||||
formatterOptions: SHORTHAND_TWO_DECIMALS_NO_TRAILING_ZEROS_CURRENCY,
|
||||
},
|
||||
]
|
||||
|
||||
const wholeNumberFormatter: FormatterRule[] = [{ upperBound: Infinity, formatterOptions: NO_DECIMALS }]
|
||||
|
||||
export enum NumberType {
|
||||
// used for token quantities in non-transaction contexts (e.g. portfolio balances)
|
||||
TokenNonTx = 'token-non-tx',
|
||||
@ -360,6 +415,15 @@ export enum NumberType {
|
||||
|
||||
// nft floor price with trailing zeros
|
||||
NFTTokenFloorPriceTrailingZeros = 'nft-token-floor-price-trailing-zeros',
|
||||
|
||||
// nft token price in currency
|
||||
NFTToken = 'nft-token',
|
||||
|
||||
// nft token price in local fiat currency
|
||||
FiatNFTToken = 'fiat-nft-token',
|
||||
|
||||
// whole number formatting
|
||||
WholeNumber = 'whole-number',
|
||||
}
|
||||
|
||||
type FormatterType = NumberType | FormatterRule[]
|
||||
@ -378,6 +442,9 @@ const TYPE_TO_FORMATTER_RULES = {
|
||||
[NumberType.NFTTokenFloorPrice]: ntfTokenFloorPriceFormatter,
|
||||
[NumberType.NFTTokenFloorPriceTrailingZeros]: ntfTokenFloorPriceFormatterTrailingZeros,
|
||||
[NumberType.NFTCollectionStats]: ntfCollectionStatsFormatter,
|
||||
[NumberType.NFTToken]: nftTokenFormatter,
|
||||
[NumberType.FiatNFTToken]: fiatNftTokenFormatter,
|
||||
[NumberType.WholeNumber]: wholeNumberFormatter,
|
||||
}
|
||||
|
||||
function getFormatterRule(input: number, type: FormatterType, conversionRate?: number): FormatterRule {
|
||||
@ -563,6 +630,18 @@ function formatNumberOrString({
|
||||
return formatNumber({ input, type, locale, localCurrency, conversionRate })
|
||||
}
|
||||
|
||||
interface FormatEtherOptions {
|
||||
input: Nullish<number | string>
|
||||
type: FormatterType
|
||||
locale?: SupportedLocale
|
||||
localCurrency?: SupportedLocalCurrency
|
||||
}
|
||||
|
||||
function formatEther({ input, type, locale, localCurrency }: FormatEtherOptions) {
|
||||
if (input === null || input === undefined) return '-'
|
||||
return formatNumber({ input: parseFloat(ethersFormatEther(input.toString())), type, locale, localCurrency })
|
||||
}
|
||||
|
||||
interface FormatFiatPriceOptions {
|
||||
price: Nullish<number | string>
|
||||
type?: FormatterType
|
||||
@ -733,9 +812,20 @@ export function useFormatter() {
|
||||
[formatterLocale]
|
||||
)
|
||||
|
||||
const formatEtherwithLocales = useCallback(
|
||||
(options: Omit<FormatEtherOptions, LocalesType>) =>
|
||||
formatEther({
|
||||
...options,
|
||||
locale: formatterLocale,
|
||||
localCurrency: currencyToFormatWith,
|
||||
}),
|
||||
[currencyToFormatWith, formatterLocale]
|
||||
)
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
formatCurrencyAmount: formatCurrencyAmountWithLocales,
|
||||
formatEther: formatEtherwithLocales,
|
||||
formatFiatPrice: formatFiatPriceWithLocales,
|
||||
formatNumber: formatNumberWithLocales,
|
||||
formatNumberOrString: formatNumberOrStringWithLocales,
|
||||
@ -747,6 +837,7 @@ export function useFormatter() {
|
||||
}),
|
||||
[
|
||||
formatCurrencyAmountWithLocales,
|
||||
formatEtherwithLocales,
|
||||
formatFiatPriceWithLocales,
|
||||
formatNumberOrStringWithLocales,
|
||||
formatNumberWithLocales,
|
||||
|
Loading…
Reference in New Issue
Block a user