feat: useUSDPrice for nft eth usd price (#7232)

* feat: useStableCointValue for nft eth usd price

* add new file

* useUSDPrice

* rename
This commit is contained in:
Charles Bachmeier 2023-08-28 09:09:30 -07:00 committed by GitHub
parent b47cebd40b
commit 5478cb0c7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 51 additions and 86 deletions

@ -2,11 +2,10 @@ import { NFTEventName } from '@uniswap/analytics-events'
import { sendAnalyticsEvent, Trace } from 'analytics' import { sendAnalyticsEvent, Trace } from 'analytics'
import { BagRow, PriceChangeBagRow, UnavailableAssetsHeaderRow } from 'nft/components/bag/BagRow' import { BagRow, PriceChangeBagRow, UnavailableAssetsHeaderRow } from 'nft/components/bag/BagRow'
import { Column } from 'nft/components/Flex' import { Column } from 'nft/components/Flex'
import { useBag, useIsMobile } from 'nft/hooks' import { useBag, useIsMobile, useNativeUsdPrice } from 'nft/hooks'
import { BagItemStatus, BagStatus } from 'nft/types' import { BagItemStatus, BagStatus } from 'nft/types'
import { fetchPrice, formatAssetEventProperties, recalculateBagUsingPooledAssets } from 'nft/utils' import { formatAssetEventProperties, recalculateBagUsingPooledAssets } from 'nft/utils'
import { useEffect, useMemo } from 'react' import { useEffect, useMemo } from 'react'
import { useQuery } from 'react-query'
export const BagContent = () => { export const BagContent = () => {
const bagStatus = useBag((s) => s.bagStatus) const bagStatus = useBag((s) => s.bagStatus)
@ -24,7 +23,7 @@ export const BagContent = () => {
return recalculateBagUsingPooledAssets(uncheckedItemsInBag) return recalculateBagUsingPooledAssets(uncheckedItemsInBag)
}, [uncheckedItemsInBag]) }, [uncheckedItemsInBag])
const { data: fetchedPriceData } = useQuery(['fetchPrice', {}], () => fetchPrice(), {}) const ethUsdPrice = useNativeUsdPrice()
const { unchangedAssets, priceChangedAssets, unavailableAssets, availableItems } = useMemo(() => { const { unchangedAssets, priceChangedAssets, unavailableAssets, availableItems } = useMemo(() => {
const unchangedAssets = itemsInBag const unchangedAssets = itemsInBag
@ -47,7 +46,7 @@ export const BagContent = () => {
if (hasAssetsInReview) if (hasAssetsInReview)
sendAnalyticsEvent(NFTEventName.NFT_BUY_BAG_CHANGED, { sendAnalyticsEvent(NFTEventName.NFT_BUY_BAG_CHANGED, {
usd_value: fetchedPriceData, usd_value: ethUsdPrice,
bag_quantity: itemsInBag, bag_quantity: itemsInBag,
...formatAssetEventProperties(priceChangedAssets), ...formatAssetEventProperties(priceChangedAssets),
}) })
@ -60,7 +59,7 @@ export const BagContent = () => {
if (bagStatus === BagStatus.CONFIRM_REVIEW && !hasAssets) { if (bagStatus === BagStatus.CONFIRM_REVIEW && !hasAssets) {
setBagStatus(BagStatus.ADDING_TO_BAG) setBagStatus(BagStatus.ADDING_TO_BAG)
} }
}, [bagStatus, itemsInBag, priceChangedAssets, setBagStatus, fetchedPriceData]) }, [bagStatus, itemsInBag, priceChangedAssets, setBagStatus, ethUsdPrice])
return ( return (
<> <>
@ -69,7 +68,7 @@ export const BagContent = () => {
<Trace <Trace
name={NFTEventName.NFT_BUY_BAG_CHANGED} name={NFTEventName.NFT_BUY_BAG_CHANGED}
properties={{ properties={{
usd_value: fetchedPriceData, usd_value: ethUsdPrice,
bag_quantity: itemsInBag.length, bag_quantity: itemsInBag.length,
...formatAssetEventProperties(unavailableAssets), ...formatAssetEventProperties(unavailableAssets),
}} }}
@ -77,7 +76,7 @@ export const BagContent = () => {
> >
<UnavailableAssetsHeaderRow <UnavailableAssetsHeaderRow
assets={unavailableAssets} assets={unavailableAssets}
usdPrice={fetchedPriceData} usdPrice={ethUsdPrice}
clearUnavailableAssets={() => setItemsInBag(availableItems)} clearUnavailableAssets={() => setItemsInBag(availableItems)}
didOpenUnavailableAssets={didOpenUnavailableAssets} didOpenUnavailableAssets={didOpenUnavailableAssets}
setDidOpenUnavailableAssets={setDidOpenUnavailableAssets} setDidOpenUnavailableAssets={setDidOpenUnavailableAssets}
@ -89,7 +88,7 @@ export const BagContent = () => {
<PriceChangeBagRow <PriceChangeBagRow
key={asset.id} key={asset.id}
asset={asset} asset={asset}
usdPrice={fetchedPriceData} usdPrice={ethUsdPrice}
markAssetAsReviewed={markAssetAsReviewed} markAssetAsReviewed={markAssetAsReviewed}
top={index === 0 && unavailableAssets.length === 0} top={index === 0 && unavailableAssets.length === 0}
isMobile={isMobile} isMobile={isMobile}
@ -104,7 +103,7 @@ export const BagContent = () => {
<BagRow <BagRow
key={asset.id} key={asset.id}
asset={asset} asset={asset}
usdPrice={fetchedPriceData} usdPrice={ethUsdPrice}
removeAsset={removeAssetsFromBag} removeAsset={removeAssetsFromBag}
showRemove={true} showRemove={true}
isMobile={isMobile} isMobile={isMobile}

@ -4,9 +4,9 @@ import { useNftActivity } from 'graphql/data/nft/NftActivity'
import { Box } from 'nft/components/Box' import { Box } from 'nft/components/Box'
import { Column, Row } from 'nft/components/Flex' import { Column, Row } from 'nft/components/Flex'
import { useBag, useIsMobile } from 'nft/hooks' import { useBag, useIsMobile } from 'nft/hooks'
import { useNativeUsdPrice } from 'nft/hooks/useUsdPrice'
import { ActivityEvent, ActivityEventType } from 'nft/types' import { ActivityEvent, ActivityEventType } from 'nft/types'
import { fetchPrice } from 'nft/utils/fetchPrice' import { useCallback, useReducer } from 'react'
import { useCallback, useEffect, useReducer, useState } from 'react'
import InfiniteScroll from 'react-infinite-scroll-component' import InfiniteScroll from 'react-infinite-scroll-component'
import styled from 'styled-components' import styled from 'styled-components'
@ -87,13 +87,7 @@ export const Activity = ({ contractAddress, rarityVerified, collectionName, chai
const cartExpanded = useBag((state) => state.bagExpanded) const cartExpanded = useBag((state) => state.bagExpanded)
const toggleCart = useBag((state) => state.toggleBag) const toggleCart = useBag((state) => state.toggleBag)
const isMobile = useIsMobile() const isMobile = useIsMobile()
const [ethPriceInUSD, setEthPriceInUSD] = useState(0) const ethPriceInUSD = useNativeUsdPrice()
useEffect(() => {
fetchPrice().then((price) => {
setEthPriceInUSD(price || 0)
})
}, [])
const Filter = useCallback( const Filter = useCallback(
function ActivityFilter({ eventType }: { eventType: ActivityEventType }) { function ActivityFilter({ eventType }: { eventType: ActivityEventType }) {

@ -10,10 +10,9 @@ import { Row } from 'nft/components/Flex'
import { BackArrowIcon, ChevronUpIcon, LightningBoltIcon, TwitterIcon, UniIcon } from 'nft/components/icons' import { BackArrowIcon, ChevronUpIcon, LightningBoltIcon, TwitterIcon, UniIcon } from 'nft/components/icons'
import { Overlay, stopPropagation } from 'nft/components/modals/Overlay' import { Overlay, stopPropagation } from 'nft/components/modals/Overlay'
import { themeVars, vars } from 'nft/css/sprinkles.css' import { themeVars, vars } from 'nft/css/sprinkles.css'
import { useIsMobile, useSendTransaction, useTransactionResponse } from 'nft/hooks' import { useIsMobile, useNativeUsdPrice, useSendTransaction, useTransactionResponse } from 'nft/hooks'
import { TxResponse, TxStateType } from 'nft/types' import { TxResponse, TxStateType } from 'nft/types'
import { import {
fetchPrice,
formatEthPrice, formatEthPrice,
formatUsdPrice, formatUsdPrice,
formatUSDPriceWithCommas, formatUSDPriceWithCommas,
@ -47,7 +46,7 @@ const UploadLink = styled.a`
` `
const TxCompleteModal = () => { const TxCompleteModal = () => {
const [ethPrice, setEthPrice] = useState(3000) const ethUsdPrice = useNativeUsdPrice()
const [showUnavailable, setShowUnavailable] = useState(false) const [showUnavailable, setShowUnavailable] = useState(false)
const txHash = useSendTransaction((state) => state.txHash) const txHash = useSendTransaction((state) => state.txHash)
const purchasedWithErc20 = useSendTransaction((state) => state.purchasedWithErc20) const purchasedWithErc20 = useSendTransaction((state) => state.purchasedWithErc20)
@ -70,8 +69,8 @@ const TxCompleteModal = () => {
totalUSDRefund, totalUSDRefund,
txFeeFiat, txFeeFiat,
} = useMemo(() => { } = useMemo(() => {
return parseTransactionResponse(transactionResponse, ethPrice) return parseTransactionResponse(transactionResponse, ethUsdPrice)
}, [transactionResponse, ethPrice]) }, [transactionResponse, ethUsdPrice])
const toggleShowUnavailable = () => { const toggleShowUnavailable = () => {
setShowUnavailable(!showUnavailable) setShowUnavailable(!showUnavailable)
@ -82,12 +81,6 @@ const TxCompleteModal = () => {
setTxState(TxStateType.New) setTxState(TxStateType.New)
} }
useEffect(() => {
fetchPrice().then((price) => {
setEthPrice(price ?? 0)
})
}, [])
useEffect(() => { useEffect(() => {
useSendTransaction.subscribe((state) => (transactionStateRef.current = state.state)) useSendTransaction.subscribe((state) => (transactionStateRef.current = state.state))
}, []) }, [])
@ -114,7 +107,7 @@ const TxCompleteModal = () => {
name={NFTEventName.NFT_BUY_BAG_SUCCEEDED} name={NFTEventName.NFT_BUY_BAG_SUCCEEDED}
properties={{ properties={{
buy_quantity: nftsPurchased.length, buy_quantity: nftsPurchased.length,
usd_value: parseFloat(formatEther(totalPurchaseValue)) * ethPrice, usd_value: parseFloat(formatEther(totalPurchaseValue)) * ethUsdPrice,
transaction_hash: txHash, transaction_hash: txHash,
using_erc20: purchasedWithErc20, using_erc20: purchasedWithErc20,
...formatAssetEventProperties(nftsPurchased), ...formatAssetEventProperties(nftsPurchased),

@ -5,19 +5,16 @@ import { OpacityHoverState } from 'components/Common'
import { Share } from 'components/Icons/Share' import { Share } from 'components/Icons/Share'
import { useNftBalance } from 'graphql/data/nft/NftBalance' import { useNftBalance } from 'graphql/data/nft/NftBalance'
import { CancelListingIcon, VerifiedIcon } from 'nft/components/icons' import { CancelListingIcon, VerifiedIcon } from 'nft/components/icons'
import { useBag, useProfilePageState, useSellAsset } from 'nft/hooks' import { useBag, useNativeUsdPrice, useProfilePageState, useSellAsset, useUsdPriceofNftAsset } from 'nft/hooks'
import { CollectionInfoForAsset, GenieAsset, ProfilePageStateType, WalletAsset } from 'nft/types' import { CollectionInfoForAsset, GenieAsset, ProfilePageStateType, WalletAsset } from 'nft/types'
import { import {
ethNumberStandardFormatter, ethNumberStandardFormatter,
fetchPrice,
formatEthPrice, formatEthPrice,
generateTweetForAsset, generateTweetForAsset,
getMarketplaceIcon, getMarketplaceIcon,
timeLeft, timeLeft,
useUsdPrice,
} from 'nft/utils' } from 'nft/utils'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useQuery } from 'react-query'
import { Link, useNavigate } from 'react-router-dom' import { Link, useNavigate } from 'react-router-dom'
import styled, { css, useTheme } from 'styled-components' import styled, { css, useTheme } from 'styled-components'
import { ExternalLink, ThemedText } from 'theme' import { ExternalLink, ThemedText } from 'theme'
@ -212,7 +209,7 @@ const MarketplaceIcon = styled(ExternalLink)`
const OwnerContainer = ({ asset }: { asset: WalletAsset }) => { const OwnerContainer = ({ asset }: { asset: WalletAsset }) => {
const navigate = useNavigate() const navigate = useNavigate()
const { data: USDValue } = useQuery(['fetchPrice', {}], () => fetchPrice(), {}) const ethUsdPrice = useNativeUsdPrice()
const setSellPageState = useProfilePageState((state) => state.setProfilePageState) const setSellPageState = useProfilePageState((state) => state.setProfilePageState)
const selectSellAsset = useSellAsset((state) => state.selectSellAsset) const selectSellAsset = useSellAsset((state) => state.selectSellAsset)
const resetSellAssets = useSellAsset((state) => state.reset) const resetSellAssets = useSellAsset((state) => state.reset)
@ -221,8 +218,8 @@ const OwnerContainer = ({ asset }: { asset: WalletAsset }) => {
const expirationDate = listing?.endAt ? new Date(listing.endAt) : undefined const expirationDate = listing?.endAt ? new Date(listing.endAt) : undefined
const USDPrice = useMemo( const USDPrice = useMemo(
() => (USDValue && asset.floor_sell_order_price ? USDValue * asset.floor_sell_order_price : undefined), () => (ethUsdPrice && asset.floor_sell_order_price ? ethUsdPrice * asset.floor_sell_order_price : undefined),
[USDValue, asset.floor_sell_order_price] [ethUsdPrice, asset.floor_sell_order_price]
) )
const trace = useTrace() const trace = useTrace()
@ -324,7 +321,7 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
const toggleBag = useBag((s) => s.toggleBag) const toggleBag = useBag((s) => s.toggleBag)
const bagExpanded = useBag((s) => s.bagExpanded) const bagExpanded = useBag((s) => s.bagExpanded)
const USDPrice = useUsdPrice(asset) const USDPrice = useUsdPriceofNftAsset(asset)
const assetsFilter = [{ address: asset.address, tokenId: asset.tokenId }] const assetsFilter = [{ address: asset.address, tokenId: asset.tokenId }]
const { walletAssets: ownerAssets } = useNftBalance(account ?? '', [], assetsFilter, 1) const { walletAssets: ownerAssets } = useNftBalance(account ?? '', [], assetsFilter, 1)

@ -1,11 +1,9 @@
import { OpacityHoverState } from 'components/Common' import { OpacityHoverState } from 'components/Common'
import { HistoryDuration } from 'graphql/data/__generated__/types-and-hooks' import { HistoryDuration } from 'graphql/data/__generated__/types-and-hooks'
import { useTrendingCollections } from 'graphql/data/nft/TrendingCollections' import { useTrendingCollections } from 'graphql/data/nft/TrendingCollections'
import ms from 'ms' import { useNativeUsdPrice } from 'nft/hooks'
import { CollectionTableColumn, Denomination, TimePeriod, VolumeType } from 'nft/types' import { CollectionTableColumn, Denomination, TimePeriod, VolumeType } from 'nft/types'
import { fetchPrice } from 'nft/utils'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { useQuery } from 'react-query'
import styled from 'styled-components' import styled from 'styled-components'
import { ThemedText } from 'theme' import { ThemedText } from 'theme'
@ -94,12 +92,7 @@ const TrendingCollections = () => {
convertTimePeriodToHistoryDuration(timePeriod) convertTimePeriodToHistoryDuration(timePeriod)
) )
const { data: usdPrice } = useQuery(['fetchPrice', {}], () => fetchPrice(), { const ethUsdPrice = useNativeUsdPrice()
refetchOnReconnect: false,
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchInterval: ms(`1m`),
})
const trendingCollectionColumns = useMemo(() => { const trendingCollectionColumns = useMemo(() => {
if (!trendingCollectionsAreLoading && trendingCollections) { if (!trendingCollectionsAreLoading && trendingCollections) {
@ -126,10 +119,10 @@ const TrendingCollections = () => {
sales: d.sales, sales: d.sales,
totalSupply: d.totalSupply, totalSupply: d.totalSupply,
denomination: isEthToggled ? Denomination.ETH : Denomination.USD, denomination: isEthToggled ? Denomination.ETH : Denomination.USD,
usdPrice, usdPrice: ethUsdPrice,
})) }))
} else return [] as CollectionTableColumn[] } else return [] as CollectionTableColumn[]
}, [trendingCollections, trendingCollectionsAreLoading, isEthToggled, usdPrice]) }, [trendingCollections, trendingCollectionsAreLoading, isEthToggled, ethUsdPrice])
return ( return (
<ExploreContainer> <ExploreContainer>

@ -5,10 +5,10 @@ import { MouseoverTooltip } from 'components/Tooltip'
import { RowsCollpsedIcon, RowsExpandedIcon } from 'nft/components/icons' import { RowsCollpsedIcon, RowsExpandedIcon } from 'nft/components/icons'
import { getRoyalty, useHandleGlobalPriceToggle, useSyncPriceWithGlobalMethod } from 'nft/components/profile/list/utils' import { getRoyalty, useHandleGlobalPriceToggle, useSyncPriceWithGlobalMethod } from 'nft/components/profile/list/utils'
import { useSellAsset } from 'nft/hooks' import { useSellAsset } from 'nft/hooks'
import { useNativeUsdPrice } from 'nft/hooks/useUsdPrice'
import { ListingMarket, WalletAsset } from 'nft/types' import { ListingMarket, WalletAsset } from 'nft/types'
import { getMarketplaceIcon } from 'nft/utils' import { getMarketplaceIcon } from 'nft/utils'
import { formatEth, formatUsdPrice } from 'nft/utils/currency' import { formatEth, formatUsdPrice } from 'nft/utils/currency'
import { fetchPrice } from 'nft/utils/fetchPrice'
import { Dispatch, DispatchWithoutAction, useCallback, useEffect, useMemo, useReducer, useState } from 'react' import { Dispatch, DispatchWithoutAction, useCallback, useEffect, useMemo, useReducer, useState } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { BREAKPOINTS, ThemedText } from 'theme' import { BREAKPOINTS, ThemedText } from 'theme'
@ -248,12 +248,7 @@ export const MarketplaceRow = ({
} }
const EthPriceDisplay = ({ ethPrice = 0 }: { ethPrice?: number }) => { const EthPriceDisplay = ({ ethPrice = 0 }: { ethPrice?: number }) => {
const [ethConversion, setEthConversion] = useState(3000) const ethUsdPrice = useNativeUsdPrice()
useEffect(() => {
fetchPrice().then((price) => {
setEthConversion(price ?? 0)
})
}, [])
return ( return (
<Row width="100%" justify="flex-end"> <Row width="100%" justify="flex-end">
@ -261,7 +256,7 @@ const EthPriceDisplay = ({ ethPrice = 0 }: { ethPrice?: number }) => {
{ethPrice !== 0 ? ( {ethPrice !== 0 ? (
<Column> <Column>
<span>{formatEth(ethPrice)} ETH</span> <span>{formatEth(ethPrice)} ETH</span>
<ThemedText.BodyPrimary color="neutral2">{formatUsdPrice(ethPrice * ethConversion)}</ThemedText.BodyPrimary> <ThemedText.BodyPrimary color="neutral2">{formatUsdPrice(ethPrice * ethUsdPrice)}</ThemedText.BodyPrimary>
</Column> </Column>
) : ( ) : (
'- ETH' '- ETH'

@ -14,5 +14,6 @@ export * from './useSendTransaction'
export * from './useSubscribeScrollState' export * from './useSubscribeScrollState'
export * from './useSweep' export * from './useSweep'
export * from './useTransactionResponse' export * from './useTransactionResponse'
export * from './useUsdPrice'
export * from './useWalletBalance' export * from './useWalletBalance'
export * from './useWalletCollections' export * from './useWalletCollections'

@ -0,0 +1,21 @@
import { formatEther } from '@ethersproject/units'
import { ChainId } from '@uniswap/sdk-core'
import { useUSDPrice } from 'hooks/useUSDPrice'
import useNativeCurrency from 'lib/hooks/useNativeCurrency'
import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount'
import { GenieAsset } from 'nft/types'
export const useNativeUsdPrice = (chainId: number = ChainId.MAINNET): number => {
const nativeCurrency = useNativeCurrency(chainId)
const parsedAmount = tryParseCurrencyAmount('1', nativeCurrency)
const usdcValue = useUSDPrice(parsedAmount)?.data ?? 0
return usdcValue
}
export function useUsdPriceofNftAsset(asset: GenieAsset): string | undefined {
const fetchedPriceData = useNativeUsdPrice()
return fetchedPriceData && asset?.priceInfo?.ETHPrice
? (parseFloat(formatEther(asset?.priceInfo?.ETHPrice)) * fetchedPriceData).toString()
: ''
}

@ -1,27 +0,0 @@
import { formatEther } from '@ethersproject/units'
import { GenieAsset } from 'nft/types'
import { useQuery } from 'react-query'
export enum Currency {
ETH = 'ETH',
LOOKS = 'LOOKS',
MATIC = 'MATIC',
}
export const fetchPrice = async (currency: Currency = Currency.ETH): Promise<number | undefined> => {
try {
const response = await fetch(`https://api.coinbase.com/v2/exchange-rates?currency=${currency}`)
return response.json().then((j) => j.data.rates.USD)
} catch (e) {
console.error(e)
return
}
}
export function useUsdPrice(asset: GenieAsset): string | undefined {
const { data: fetchedPriceData } = useQuery(['fetchPrice', {}], () => fetchPrice(), {})
return fetchedPriceData && asset?.priceInfo?.ETHPrice
? (parseFloat(formatEther(asset?.priceInfo?.ETHPrice)) * fetchedPriceData).toString()
: ''
}

@ -4,7 +4,6 @@ export * from './buildSellObject'
export * from './carousel' export * from './carousel'
export * from './collection' export * from './collection'
export * from './currency' export * from './currency'
export * from './fetchPrice'
export * from './formatEventProperties' export * from './formatEventProperties'
export * from './isAudio' export * from './isAudio'
export * from './isVideo' export * from './isVideo'