Commit ef57ff76 authored by Charles Bachmeier's avatar Charles Bachmeier Committed by GitHub

refactor: NFT Assets GraphQL Integration (#4928)

* add demo Asset Fetcher

* new file

* update fetcher

* update query name

* beginning integration type

* uncomment

* working mutant apes

* comment out debug logging

* pass in inputs to query

* update collections to handle inf scroll

* paginated query first attempt

* wrapped assetQuery

* building pagination, needs spread

* working pagination

* working sort

* use cacheconfig

* change query source in Collection page

* passed in filters

* fetch schema from main endpoint

* delete unused relayenv

* rename token_url

* easy GenieAsset refactoring

* add rarity

* update price info

* remove logging

* remove redundancy

* refactor usd price fetching for assets

* update standard and address

* remove unused cacheconfig

* dont repeat ethprice calc

* unmemo bools

* reduce duplicated usd price logic

* cleanup imports

* useUsd price hook

* resolve merge conflict
Co-authored-by: default avatarCharles Bachmeier <charlie@genie.xyz>
parent bed0b3ab
import ms from 'ms.macro'
import { Variables } from 'react-relay'
import { CacheConfig, Environment, Network, RecordSource, RequestParameters, Store } from 'relay-runtime'
import { Environment, Network, RecordSource, RequestParameters, Store } from 'relay-runtime'
import RelayQueryResponseCache from 'relay-runtime/lib/network/RelayQueryResponseCache'
import fetchGraphQL from './fetchGraphQL'
......@@ -10,17 +10,13 @@ const size = 250
const ttl = ms`5m`
export const cache = new RelayQueryResponseCache({ size, ttl })
const fetchQuery = async function wrappedFetchQuery(
params: RequestParameters,
variables: Variables,
cacheConfig: CacheConfig
) {
const fetchQuery = async function wrappedFetchQuery(params: RequestParameters, variables: Variables) {
const queryID = params.name
const cachedData = cache.get(queryID, variables)
if (cachedData !== null) return cachedData
return fetchGraphQL(params, variables, cacheConfig).then((data) => {
return fetchGraphQL(params, variables).then((data) => {
if (params.operationKind !== 'mutation') {
cache.set(queryID, variables, data)
}
......
import { Variables } from 'react-relay'
import { CacheConfig, GraphQLResponse, RequestParameters } from 'relay-runtime'
import { GraphQLResponse, RequestParameters } from 'relay-runtime'
const URL = process.env.REACT_APP_AWS_API_ENDPOINT
const TOKEN_URL = process.env.REACT_APP_AWS_API_ENDPOINT
const NFT_URL = process.env.REACT_APP_NFT_AWS_API_ENDPOINT ?? ''
if (!URL) {
if (!TOKEN_URL) {
throw new Error('AWS URL MISSING FROM ENVIRONMENT')
}
......@@ -16,17 +16,18 @@ const nftHeaders = {
'x-api-key': process.env.REACT_APP_NFT_AWS_X_API_KEY ?? '',
}
const fetchQuery = (
params: RequestParameters,
variables: Variables,
cacheConfig: CacheConfig
): Promise<GraphQLResponse> => {
const { metadata: { isNFT } = { isNFT: false } } = cacheConfig
// The issue below prevented using a custom var in metadata to gate which queries are for the nft endpoint vs base endpoint
// This is a temporary solution before the two endpoints merge
// https://github.com/relay-tools/relay-hooks/issues/215
const NFT_QUERIES = ['AssetQuery', 'AssetPaginationQuery']
const fetchQuery = (params: RequestParameters, variables: Variables): Promise<GraphQLResponse> => {
const isNFT = NFT_QUERIES.includes(params.name)
const body = JSON.stringify({
query: params.text, // GraphQL text from input
variables,
})
const url = isNFT ? NFT_URL : URL
const url = isNFT ? NFT_URL : TOKEN_URL
const headers = isNFT ? nftHeaders : baseHeaders
return fetch(url, { method: 'POST', body, headers })
......
import graphql from 'babel-plugin-relay/macro'
import { parseEther } from 'ethers/lib/utils'
import { GenieAsset } from 'nft/types'
import { loadQuery, usePaginationFragment, usePreloadedQuery } from 'react-relay'
import RelayEnvironment from '../RelayEnvironment'
import { AssetPaginationQuery } from './__generated__/AssetPaginationQuery.graphql'
import { AssetQuery, NftAssetsFilterInput, NftAssetSortableField } from './__generated__/AssetQuery.graphql'
const assetPaginationQuery = graphql`
fragment AssetQuery_nftAssets on Query @refetchable(queryName: "AssetPaginationQuery") {
nftAssets(
address: $address
orderBy: $orderBy
asc: $asc
filter: $filter
first: $first
after: $after
last: $last
before: $before
) @connection(key: "AssetQuery_nftAssets") {
edges {
node {
id
name
ownerAddress
image {
url
}
smallImage {
url
}
originalImage {
url
}
tokenId
description
animationUrl
suspiciousFlag
collection {
name
isVerified
image {
url
}
creator {
address
profileImage {
url
}
isVerified
}
nftContracts {
address
standard
}
}
listings(first: 1) {
edges {
node {
address
createdAt
endAt
id
maker
marketplace
marketplaceUrl
orderHash
price {
currency
value
}
quantity
startAt
status
taker
tokenId
type
}
cursor
}
}
rarities {
provider
rank
score
}
metadataUrl
}
}
}
}
`
const assetQuery = graphql`
query AssetQuery(
$address: String!
$orderBy: NftAssetSortableField
$asc: Boolean
$filter: NftAssetsFilterInput
$first: Int
$after: String
$last: Int
$before: String
) {
...AssetQuery_nftAssets
}
`
export function useAssetsQuery(
address: string,
orderBy: NftAssetSortableField,
asc: boolean,
filter: NftAssetsFilterInput,
first?: number,
after?: string,
last?: number,
before?: string
) {
const assetsQueryReference = loadQuery<AssetQuery>(RelayEnvironment, assetQuery, {
address,
orderBy,
asc,
filter,
first,
after,
last,
before,
})
const queryData = usePreloadedQuery<AssetQuery>(assetQuery, assetsQueryReference)
const { data, hasNext, loadNext, isLoadingNext } = usePaginationFragment<AssetPaginationQuery, any>(
assetPaginationQuery,
queryData
)
const assets: GenieAsset[] = data.nftAssets?.edges?.map((queryAsset: { node: any }) => {
const asset = queryAsset.node
const ethPrice = parseEther(asset.listings?.edges[0].node.price.value?.toString() ?? '0').toString()
return {
id: asset.id,
address: asset.collection.nftContracts[0].address,
notForSale: asset.listings === null,
collectionName: asset.collection?.name,
collectionSymbol: asset.collection?.image?.url,
imageUrl: asset.image?.url,
animationUrl: asset.animationUrl,
marketplace: asset.listings?.edges[0].node.marketplace.toLowerCase(),
name: asset.name,
priceInfo: asset.listings
? {
ETHPrice: ethPrice,
baseAsset: 'ETH',
baseDecimals: '18',
basePrice: ethPrice,
}
: undefined,
susFlag: asset.suspiciousFlag,
sellorders: asset.listings?.edges,
smallImageUrl: asset.smallImage?.url,
tokenId: asset.tokenId,
tokenType: asset.collection.nftContracts[0].standard,
// totalCount?: number, // TODO waiting for BE changes
collectionIsVerified: asset.collection?.isVerified,
rarity: {
primaryProvider: 'Rarity Sniper', // TODO update when backend adds more providers
providers: asset.rarities,
},
owner: asset.ownerAddress,
creator: {
profile_img_url: asset.collection?.creator?.profileImage?.url,
address: asset.collection?.creator?.address,
},
metadataUrl: asset.metadataUrl,
}
})
return { assets, hasNext, isLoadingNext, loadNext }
}
......@@ -52,7 +52,7 @@ export const CollectionAsset = ({
let notForSale = true
let assetMediaType = AssetMediaType.Image
notForSale = asset.notForSale || BigNumber.from(asset.currentEthPrice ? asset.currentEthPrice : 0).lt(0)
notForSale = asset.notForSale || BigNumber.from(asset.priceInfo.ETHPrice ? asset.priceInfo.ETHPrice : 0).lt(0)
if (isAudio(asset.animationUrl)) {
assetMediaType = AssetMediaType.Audio
} else if (isVideo(asset.animationUrl)) {
......@@ -96,7 +96,7 @@ export const CollectionAsset = ({
<Card.PrimaryRow>
<Card.PrimaryDetails>
<Card.PrimaryInfo>{asset.name ? asset.name : `#${asset.tokenId}`}</Card.PrimaryInfo>
{asset.openseaSusFlag && <Card.Suspicious />}
{asset.susFlag && <Card.Suspicious />}
</Card.PrimaryDetails>
{asset.rarity && provider && provider.rank && (
<Card.Ranking
......@@ -110,7 +110,7 @@ export const CollectionAsset = ({
<Card.SecondaryRow>
<Card.SecondaryDetails>
<Card.SecondaryInfo>
{notForSale ? '' : `${formatWeiToDecimal(asset.currentEthPrice, true)} ETH`}
{notForSale ? '' : `${formatWeiToDecimal(asset.priceInfo.ETHPrice, true)} ETH`}
</Card.SecondaryInfo>
{(asset.marketplace === Markets.NFTX || asset.marketplace === Markets.NFT20) && <Card.Pool />}
</Card.SecondaryDetails>
......
......@@ -3,6 +3,10 @@ import { ElementName, Event, EventName } from 'analytics/constants'
import { TraceEvent } from 'analytics/TraceEvent'
import clsx from 'clsx'
import { loadingAnimation } from 'components/Loader/styled'
import { parseEther } from 'ethers/lib/utils'
import { NftGraphQlVariant, useNftGraphQlFlag } from 'featureFlags/flags/nftGraphQl'
import { NftAssetTraitInput, NftMarketplace } from 'graphql/data/nft/__generated__/AssetQuery.graphql'
import { useAssetsQuery } from 'graphql/data/nft/Asset'
import useDebounce from 'hooks/useDebounce'
import { AnimatedBox, Box } from 'nft/components/Box'
import { CollectionSearch, FilterButton } from 'nft/components/collection'
......@@ -17,6 +21,7 @@ import {
CollectionFilters,
initialCollectionFilterState,
SortBy,
SortByQueries,
useBag,
useCollectionFilters,
useFiltersExpanded,
......@@ -42,6 +47,11 @@ import { marketPlaceItems } from './MarketplaceSelect'
import { Sweep } from './Sweep'
import { TraitChip } from './TraitChip'
const EmptyCollectionWrapper = styled.div`
display: block;
textalign: center;
`
interface CollectionNftsProps {
contractAddress: string
collectionStats: GenieCollection
......@@ -106,6 +116,8 @@ export const LoadingButton = styled.div`
background-size: 400%;
`
const DEFAULT_ASSET_QUERY_AMOUNT = 25
export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerified }: CollectionNftsProps) => {
const { chainId } = useWeb3React()
const traits = useCollectionFilters((state) => state.traits)
......@@ -130,6 +142,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
const reset = useCollectionFilters((state) => state.reset)
const setMin = useCollectionFilters((state) => state.setMinPrice)
const setMax = useCollectionFilters((state) => state.setMaxPrice)
const isNftGraphQl = useNftGraphQlFlag() === NftGraphQlVariant.Enabled
const toggleBag = useBag((state) => state.toggleBag)
const bagExpanded = useBag((state) => state.bagExpanded)
......@@ -197,7 +210,7 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
},
{
getNextPageParam: (lastPage, pages) => {
return lastPage?.flat().length === 25 ? pages.length : null
return lastPage?.flat().length === DEFAULT_ASSET_QUERY_AMOUNT ? pages.length : null
},
refetchOnReconnect: false,
refetchOnWindowFocus: false,
......@@ -205,10 +218,30 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
refetchInterval: 5000,
}
)
useEffect(() => {
setIsCollectionNftsLoading(isLoading)
}, [isLoading, setIsCollectionNftsLoading])
const {
assets: nftQueryAssets,
loadNext,
hasNext,
isLoadingNext,
} = useAssetsQuery(
isNftGraphQl ? contractAddress : '',
SortByQueries[sortBy].field,
SortByQueries[sortBy].asc,
{
listed: buyNow,
marketplaces: markets.length > 0 ? markets.map((market) => market.toUpperCase() as NftMarketplace) : undefined,
maxPrice: debouncedMaxPrice ? parseEther(debouncedMaxPrice).toString() : undefined,
minPrice: debouncedMinPrice ? parseEther(debouncedMinPrice).toString() : undefined,
tokenSearchQuery: debouncedSearchByNameText,
traits:
traits.length > 0
? traits.map((trait) => {
return { name: trait.trait_type, values: [trait.trait_value] } as unknown as NftAssetTraitInput
})
: undefined,
},
DEFAULT_ASSET_QUERY_AMOUNT
)
const [uniformHeight, setUniformHeight] = useState<UniformHeight>(UniformHeights.unset)
const [currentTokenPlayingMedia, setCurrentTokenPlayingMedia] = useState<string | undefined>()
......@@ -217,12 +250,24 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
const isMobile = useIsMobile()
const collectionNfts = useMemo(() => {
if (!collectionAssets || !AssetsFetchSuccess) return undefined
if (
(isNftGraphQl && !nftQueryAssets && !isLoadingNext) ||
(!isNftGraphQl && !collectionAssets) ||
!AssetsFetchSuccess
)
return undefined
return isNftGraphQl ? nftQueryAssets : collectionAssets?.pages.flat()
}, [AssetsFetchSuccess, collectionAssets, isLoadingNext, isNftGraphQl, nftQueryAssets])
return collectionAssets.pages.flat()
}, [collectionAssets, AssetsFetchSuccess])
const wrappedLoadingState = isNftGraphQl ? isLoadingNext : isLoading
const wrappedHasNext = isNftGraphQl ? hasNext : hasNextPage ?? false
const loadingAssets = useMemo(() => <>{new Array(25).fill(<CollectionAssetLoading />)}</>, [])
useEffect(() => {
setIsCollectionNftsLoading(wrappedLoadingState)
}, [wrappedLoadingState, setIsCollectionNftsLoading])
const loadingAssets = useMemo(() => <>{new Array(DEFAULT_ASSET_QUERY_AMOUNT).fill(<CollectionAssetLoading />)}</>, [])
const hasRarity = getRarityStatus(rarityStatusCache, collectionStats?.address, collectionNfts)
const sortDropDownOptions: DropDownOption[] = useMemo(
......@@ -469,27 +514,27 @@ export const CollectionNfts = ({ contractAddress, collectionStats, rarityVerifie
</Box>
</AnimatedBox>
<InfiniteScroll
next={fetchNextPage}
hasMore={hasNextPage ?? false}
loader={hasNextPage ? loadingAssets : null}
next={() => (isNftGraphQl ? loadNext(DEFAULT_ASSET_QUERY_AMOUNT) : fetchNextPage())}
hasMore={wrappedHasNext}
loader={wrappedHasNext ? loadingAssets : null}
dataLength={collectionNfts?.length ?? 0}
style={{ overflow: 'unset' }}
className={hasNfts || isLoading ? styles.assetList : undefined}
className={hasNfts || wrappedLoadingState ? styles.assetList : undefined}
>
{hasNfts
? Nfts
: isLoading
? loadingAssets
: !isLoading && (
<Center width="full" color="textSecondary" style={{ height: '60vh' }}>
<div style={{ display: 'block', textAlign: 'center' }}>
<p className={headlineMedium}>No NFTS found</p>
<Box className={clsx(bodySmall, buttonTextMedium)} color="blue" cursor="pointer">
View full collection
</Box>
</div>
</Center>
)}
{hasNfts ? (
Nfts
) : wrappedLoadingState ? (
loadingAssets
) : (
<Center width="full" color="textSecondary" style={{ height: '60vh' }}>
<EmptyCollectionWrapper>
<p className={headlineMedium}>No NFTS found</p>
<Box className={clsx(bodySmall, buttonTextMedium)} color="blue" cursor="pointer">
View full collection
</Box>
</EmptyCollectionWrapper>
</Center>
)}
</InfiniteScroll>
</>
)
......
......@@ -265,21 +265,21 @@ export const Sweep = ({ contractAddress, collectionStats, minPrice, maxPrice, sh
let jointCollections = [...nftxCollectionAssets, ...nft20CollectionAssets]
jointCollections.forEach((asset) => {
if (!asset.openseaSusFlag) {
if (!asset.susFlag) {
const isNFTX = asset.marketplace === Markets.NFTX
asset.currentEthPrice = calcPoolPrice(asset, isNFTX ? counterNFTX : counterNFT20)
BigNumber.from(asset.currentEthPrice).gte(0) && (isNFTX ? counterNFTX++ : counterNFT20++)
asset.priceInfo.ETHPrice = calcPoolPrice(asset, isNFTX ? counterNFTX : counterNFT20)
BigNumber.from(asset.priceInfo.ETHPrice).gte(0) && (isNFTX ? counterNFTX++ : counterNFT20++)
}
})
jointCollections = collectionAssets.concat(jointCollections)
jointCollections.sort((a, b) => {
return BigNumber.from(a.currentEthPrice).gt(BigNumber.from(b.currentEthPrice)) ? 1 : -1
return BigNumber.from(a.priceInfo.ETHPrice).gt(BigNumber.from(b.priceInfo.ETHPrice)) ? 1 : -1
})
let validAssets = jointCollections.filter(
(asset) => BigNumber.from(asset.currentEthPrice).gte(0) && !asset.openseaSusFlag
(asset) => BigNumber.from(asset.priceInfo.ETHPrice).gte(0) && !asset.susFlag
)
validAssets = validAssets.slice(
......
......@@ -14,6 +14,7 @@ import { themeVars } from 'nft/css/sprinkles.css'
import { useBag } from 'nft/hooks'
import { useTimeout } from 'nft/hooks/useTimeout'
import { CollectionInfoForAsset, GenieAsset, SellOrder } from 'nft/types'
import { useUsdPrice } from 'nft/utils'
import { shortenAddress } from 'nft/utils/address'
import { formatEthPrice } from 'nft/utils/currency'
import { isAssetOwnedByUser } from 'nft/utils/isAssetOwnedByUser'
......@@ -176,6 +177,8 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
}
}, [asset, address, provider])
const USDPrice = useUsdPrice(asset)
return (
<AnimatedBox
style={{
......@@ -270,7 +273,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
</Row>
</Row>
<Row as="h1" marginTop="0" marginBottom="12" gap="2" className={headlineMedium}>
{asset.openseaSusFlag && (
{asset.susFlag && (
<Box marginTop="8">
<MouseoverTooltip text={<Box fontWeight="normal">{SUSPICIOUS_TEXT}</Box>}>
<SuspiciousIcon height="30" width="30" viewBox="0 0 16 17" />
......@@ -366,9 +369,11 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
<Row as="span" className={subhead} color="textPrimary">
{formatEthPrice(asset.priceInfo.ETHPrice)} <Eth2Icon />
</Row>
<Box as="span" color="textSecondary" className={bodySmall}>
${toSignificant(asset.priceInfo.USDPrice)}
</Box>
{USDPrice && (
<Box as="span" color="textSecondary" className={bodySmall}>
${toSignificant(USDPrice)}
</Box>
)}
</Row>
{asset.sellorders?.[0].orderClosingDate ? <CountdownTimer sellOrder={asset.sellorders[0]} /> : null}
</Column>
......@@ -413,7 +418,7 @@ export const AssetDetails = ({ asset, collection }: AssetDetailsProps) => {
tokenId={asset.tokenId}
tokenType={asset.tokenType}
blockchain="Ethereum"
metadataUrl={asset.externalLink}
metadataUrl={asset.metadataUrl}
totalSupply={collection.totalSupply}
/>
)}
......
......@@ -2,7 +2,7 @@ import { useWeb3React } from '@web3-react/core'
import { CancelListingIcon, MinusIcon, PlusIcon } from 'nft/components/icons'
import { useBag } from 'nft/hooks'
import { CollectionInfoForAsset, GenieAsset, TokenType } from 'nft/types'
import { ethNumberStandardFormatter, formatEthPrice, getMarketplaceIcon, timeLeft } from 'nft/utils'
import { ethNumberStandardFormatter, formatEthPrice, getMarketplaceIcon, timeLeft, useUsdPrice } from 'nft/utils'
import { useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import styled, { useTheme } from 'styled-components/macro'
......@@ -111,6 +111,7 @@ const DiscoveryContainer = styled.div`
export const OwnerContainer = ({ asset }: { asset: GenieAsset }) => {
const listing = asset.sellorders && asset.sellorders.length > 0 ? asset.sellorders[0] : undefined
const expirationDate = listing ? new Date(listing.orderClosingDate) : undefined
const USDPrice = useUsdPrice(asset)
const navigate = useNavigate()
......@@ -129,9 +130,11 @@ export const OwnerContainer = ({ asset }: { asset: GenieAsset }) => {
<ThemedText.MediumHeader fontSize={'28px'} lineHeight={'36px'}>
{formatEthPrice(asset.priceInfo.ETHPrice)}
</ThemedText.MediumHeader>
<ThemedText.BodySecondary lineHeight={'24px'}>
{ethNumberStandardFormatter(asset.priceInfo.USDPrice, true, true)}
</ThemedText.BodySecondary>
{USDPrice && (
<ThemedText.BodySecondary lineHeight={'24px'}>
{ethNumberStandardFormatter(USDPrice, true, true)}
</ThemedText.BodySecondary>
)}
</>
) : (
<ThemedText.BodySecondary fontSize="14px" lineHeight={'20px'}>
......@@ -189,6 +192,8 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
const itemsInBag = useBag((s) => s.itemsInBag)
const addAssetsToBag = useBag((s) => s.addAssetsToBag)
const removeAssetsFromBag = useBag((s) => s.removeAssetsFromBag)
const USDPrice = useUsdPrice(asset)
const isErc1555 = asset.tokenType === TokenType.ERC1155
const { quantity, assetInBag } = useMemo(() => {
......@@ -223,9 +228,11 @@ export const AssetPriceDetails = ({ asset, collection }: AssetPriceDetailsProps)
<ThemedText.MediumHeader fontSize={'28px'} lineHeight={'36px'}>
{formatEthPrice(asset.priceInfo.ETHPrice)}
</ThemedText.MediumHeader>
<ThemedText.BodySecondary lineHeight={'24px'}>
{ethNumberStandardFormatter(asset.priceInfo.USDPrice, true, true)}
</ThemedText.BodySecondary>
{USDPrice && (
<ThemedText.BodySecondary lineHeight={'24px'}>
{ethNumberStandardFormatter(USDPrice, true, true)}
</ThemedText.BodySecondary>
)}
</PriceRow>
{expirationDate && (
<ThemedText.BodySecondary fontSize={'14px'}>Sale ends: {timeLeft(expirationDate)}</ThemedText.BodySecondary>
......
import { NftAssetSortableField } from 'graphql/data/nft/__generated__/AssetPaginationQuery.graphql'
import create from 'zustand'
import { devtools } from 'zustand/middleware'
......@@ -14,6 +15,16 @@ export const SortByPointers = {
[SortBy.RareToCommon]: 'rare',
[SortBy.CommonToRare]: 'common',
}
interface QueryInfo {
field: NftAssetSortableField
asc: boolean
}
export const SortByQueries = {
[SortBy.HighToLow]: { field: 'PRICE', asc: false } as QueryInfo,
[SortBy.LowToHigh]: { field: 'PRICE', asc: true } as QueryInfo,
[SortBy.RareToCommon]: { field: 'RARITY', asc: true } as QueryInfo,
[SortBy.CommonToRare]: { field: 'RARITY', asc: false } as QueryInfo,
}
export type Trait = {
trait_type: string
......
......@@ -53,13 +53,13 @@ const buildRouteItem = (item: GenieAsset): RouteItem => {
id: item.id,
symbol: item.priceInfo.baseAsset,
name: item.name,
decimals: item.decimals || 0, // 0 for fungible items
decimals: parseFloat(item.priceInfo.baseDecimals),
address: item.address,
tokenType: item.tokenType,
tokenId: item.tokenId,
marketplace: item.marketplace,
collectionName: item.collectionName,
amount: item.amount || 1, // default 1 for a single asset
amount: 1,
priceInfo: {
basePrice: item.priceInfo.basePrice,
baseAsset: item.priceInfo.baseAsset,
......
......@@ -72,7 +72,7 @@ export interface AssetSellOrder {
export interface Rarity {
primaryProvider: string
providers: { provider: string; rank: number; url: string; score: number }[]
providers: { provider: string; rank: number; url?: string; score: number }[]
}
export interface GenieAsset {
......@@ -81,27 +81,22 @@ export interface GenieAsset {
notForSale: boolean
collectionName: string
collectionSymbol: string
currentEthPrice: string
currentUsdPrice: string
imageUrl: string
animationUrl: string
marketplace: Markets
name: string
priceInfo: PriceInfo
openseaSusFlag: boolean
susFlag: boolean
sellorders: SellOrder[]
smallImageUrl: string
tokenId: string
tokenType: TokenType
url: string
totalCount?: number // The totalCount from the query to /assets
amount?: number
decimals?: number
collectionIsVerified?: boolean
rarity?: Rarity
owner: string
creator: OpenSeaUser
externalLink: string
metadataUrl: string
traits?: {
trait_type: string
value: string
......
......@@ -14,15 +14,13 @@ export const buildActivityAsset = (event: ActivityEvent, collectionName: string,
return {
address: event.collectionAddress,
collectionName,
currentEthPrice: event.price,
imageUrl: event.tokenMetadata?.imageUrl,
marketplace: event.marketplace,
name: event.tokenMetadata?.name,
tokenId: event.tokenId,
openseaSusFlag: event.tokenMetadata?.suspiciousFlag,
susFlag: event.tokenMetadata?.suspiciousFlag,
smallImageUrl: event.tokenMetadata?.smallImageUrl,
collectionSymbol: event.symbol,
currentUsdPrice: assetUsdPrice,
priceInfo: {
USDPrice: assetUsdPrice,
ETHPrice: event.price,
......
......@@ -113,7 +113,7 @@ export const recalculateBagUsingPooledAssets = (uncheckedItemsInBag: BagItem[])
if (isPriceChangedAsset && item.asset.updatedPriceInfo)
item.asset.updatedPriceInfo.ETHPrice = item.asset.updatedPriceInfo.basePrice = calculatedPrice
else item.asset.currentEthPrice = item.asset.priceInfo.ETHPrice = calculatedPrice
else item.asset.priceInfo.ETHPrice = calculatedPrice
}
})
......
import { formatEther } from '@ethersproject/units'
import { GenieAsset } from 'nft/types'
import { useQuery } from 'react-query'
export enum Currency {
ETH = 'ETH',
LOOKS = 'LOOKS',
......@@ -13,3 +17,8 @@ export const fetchPrice = async (currency: Currency = Currency.ETH): Promise<num
return
}
}
export function useUsdPrice(asset: GenieAsset): string {
const { data: fetchedPriceData } = useQuery(['fetchPrice', {}], () => fetchPrice(), {})
return fetchedPriceData ? (parseFloat(formatEther(asset.priceInfo.ETHPrice)) * fetchedPriceData).toString() : ''
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment