mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
refactor price context to fetch multiple tokens (#1573)
* refactor price context to fetch multiple tokens * fixes * move tokenIds to app config * make conversion work * conversion for all user tokens, hide if 0 * different user balance key tactic * remove NFT gas estimation * closes #1633 * small simplification in getCoingeckoTokenId logic * basic Prices provider test * mock some hooks * mock MarketMetadata in all tests
This commit is contained in:
parent
2f93d84e5f
commit
4d119467a4
73
.jest/__mocks__/MarketMetadata.ts
Normal file
73
.jest/__mocks__/MarketMetadata.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import siteContent from '../../content/site.json'
|
||||||
|
import appConfig from '../../app.config'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getOpcFeeForToken: jest.fn(),
|
||||||
|
siteContent,
|
||||||
|
appConfig,
|
||||||
|
opcFees: [
|
||||||
|
{
|
||||||
|
chainId: 1,
|
||||||
|
approvedTokens: [
|
||||||
|
'0x0642026e7f0b6ccac5925b4e7fa61384250e1701',
|
||||||
|
'0x967da4048cd07ab37855c090aaf366e4ce1b9f48'
|
||||||
|
],
|
||||||
|
swapApprovedFee: '0.001',
|
||||||
|
swapNotApprovedFee: '0.002'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 137,
|
||||||
|
approvedTokens: [
|
||||||
|
'0x282d8efce846a88b159800bd4130ad77443fa1a1',
|
||||||
|
'0xc5248aa0629c0b2d6a02834a5f172937ac83cbd3'
|
||||||
|
],
|
||||||
|
swapApprovedFee: '0.001',
|
||||||
|
swapNotApprovedFee: '0.002'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 56,
|
||||||
|
approvedTokens: ['0xdce07662ca8ebc241316a15b611c89711414dd1a'],
|
||||||
|
swapApprovedFee: '0.001',
|
||||||
|
swapNotApprovedFee: '0.002'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 246,
|
||||||
|
approvedTokens: ['0x593122aae80a6fc3183b2ac0c4ab3336debee528'],
|
||||||
|
swapApprovedFee: '0.001',
|
||||||
|
swapNotApprovedFee: '0.002'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 1285,
|
||||||
|
approvedTokens: ['0x99c409e5f62e4bd2ac142f17cafb6810b8f0baae'],
|
||||||
|
swapApprovedFee: '0.001',
|
||||||
|
swapNotApprovedFee: '0.002'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 3,
|
||||||
|
approvedTokens: ['0x5e8dcb2afa23844bcc311b00ad1a0c30025aade9'],
|
||||||
|
swapApprovedFee: '0.001',
|
||||||
|
swapNotApprovedFee: '0.002'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 4,
|
||||||
|
approvedTokens: [
|
||||||
|
'0x8967bcf84170c91b0d24d4302c2376283b0b3a07',
|
||||||
|
'0xd92e713d051c37ebb2561803a3b5fbabc4962431'
|
||||||
|
],
|
||||||
|
swapApprovedFee: '0.001',
|
||||||
|
swapNotApprovedFee: '0.002'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 80001,
|
||||||
|
approvedTokens: ['0xd8992ed72c445c35cb4a2be468568ed1079357c8'],
|
||||||
|
swapApprovedFee: '0.001',
|
||||||
|
swapNotApprovedFee: '0.002'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
chainId: 1287,
|
||||||
|
approvedTokens: ['0xf6410bf5d773c7a41ebff972f38e7463fa242477'],
|
||||||
|
swapApprovedFee: '0.001',
|
||||||
|
swapNotApprovedFee: '0.002'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,2 +1,7 @@
|
|||||||
import '@testing-library/jest-dom/extend-expect'
|
import '@testing-library/jest-dom/extend-expect'
|
||||||
import './__mocks__/matchMedia'
|
import './__mocks__/matchMedia'
|
||||||
|
import marketMetadataMock from './__mocks__/MarketMetadata'
|
||||||
|
|
||||||
|
jest.mock('../../src/@context/MarketMetadata', () => ({
|
||||||
|
useMarketMetadata: () => marketMetadataMock
|
||||||
|
}))
|
||||||
|
@ -62,6 +62,10 @@ module.exports = {
|
|||||||
'LINK'
|
'LINK'
|
||||||
],
|
],
|
||||||
|
|
||||||
|
// Tokens to fetch the spot prices from coingecko, against above currencies.
|
||||||
|
// Refers to Coingecko API tokenIds.
|
||||||
|
coingeckoTokenIds: ['ocean-protocol', 'h2o', 'ethereum', 'matic-network'],
|
||||||
|
|
||||||
// Config for https://github.com/donavon/use-dark-mode
|
// Config for https://github.com/donavon/use-dark-mode
|
||||||
darkModeConfig: {
|
darkModeConfig: {
|
||||||
classNameDark: 'dark',
|
classNameDark: 'dark',
|
||||||
|
@ -16,6 +16,7 @@ export interface AppConfig {
|
|||||||
consumeMarketOrderFee: string
|
consumeMarketOrderFee: string
|
||||||
consumeMarketFixedSwapFee: string
|
consumeMarketFixedSwapFee: string
|
||||||
currencies: string[]
|
currencies: string[]
|
||||||
|
coingeckoTokenIds: string[]
|
||||||
allowFixedPricing: string
|
allowFixedPricing: string
|
||||||
allowFreePricing: string
|
allowFreePricing: string
|
||||||
defaultPrivacyPolicySlug: string
|
defaultPrivacyPolicySlug: string
|
||||||
|
@ -14,6 +14,7 @@ import { MarketMetadataProviderValue, OpcFee } from './_types'
|
|||||||
import siteContent from '../../../content/site.json'
|
import siteContent from '../../../content/site.json'
|
||||||
import appConfig from '../../../app.config'
|
import appConfig from '../../../app.config'
|
||||||
import { fetchData, getQueryContext } from '@utils/subgraph'
|
import { fetchData, getQueryContext } from '@utils/subgraph'
|
||||||
|
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||||
|
|
||||||
const MarketMetadataContext = createContext({} as MarketMetadataProviderValue)
|
const MarketMetadataContext = createContext({} as MarketMetadataProviderValue)
|
||||||
|
|
||||||
@ -43,6 +44,11 @@ function MarketMetadataProvider({
|
|||||||
swapNotApprovedFee: response.data?.opc.swapNonOceanFee
|
swapNotApprovedFee: response.data?.opc.swapNonOceanFee
|
||||||
} as OpcFee)
|
} as OpcFee)
|
||||||
}
|
}
|
||||||
|
LoggerInstance.log('[MarketMetadata] Got new data.', {
|
||||||
|
opcFees: opcData,
|
||||||
|
siteContent,
|
||||||
|
appConfig
|
||||||
|
})
|
||||||
setOpcFees(opcData)
|
setOpcFees(opcData)
|
||||||
}
|
}
|
||||||
getOpcData()
|
getOpcData()
|
||||||
|
13
src/@context/Prices/_constants.ts
Normal file
13
src/@context/Prices/_constants.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Prices } from './_types'
|
||||||
|
import { coingeckoTokenIds } from '../../../app.config'
|
||||||
|
|
||||||
|
export const initialData: Prices = coingeckoTokenIds.map((tokenId) => ({
|
||||||
|
[tokenId]: {
|
||||||
|
eur: 0.0,
|
||||||
|
usd: 0.0,
|
||||||
|
eth: 0.0,
|
||||||
|
btc: 0.0
|
||||||
|
}
|
||||||
|
}))[0]
|
||||||
|
|
||||||
|
export const refreshInterval = 120000 // 120 sec.
|
9
src/@context/Prices/_types.ts
Normal file
9
src/@context/Prices/_types.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export interface Prices {
|
||||||
|
[key: string]: {
|
||||||
|
[key: string]: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PricesValue {
|
||||||
|
prices: Prices
|
||||||
|
}
|
23
src/@context/Prices/_utils.ts
Normal file
23
src/@context/Prices/_utils.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// Deal with differences between token symbol & Coingecko API IDs
|
||||||
|
//
|
||||||
|
export function getCoingeckoTokenId(symbol: string) {
|
||||||
|
// can be OCEAN or mOCEAN
|
||||||
|
const isOcean = symbol?.toLowerCase().includes('ocean')
|
||||||
|
// can be H2O or H20
|
||||||
|
const isH2o = symbol?.toLowerCase().includes('h2')
|
||||||
|
const isEth = symbol?.toLowerCase() === 'eth'
|
||||||
|
const isMatic = symbol?.toLowerCase() === 'matic'
|
||||||
|
|
||||||
|
const priceTokenId = isOcean
|
||||||
|
? 'ocean-protocol'
|
||||||
|
: isH2o
|
||||||
|
? 'h2o'
|
||||||
|
: isEth
|
||||||
|
? 'ethereum'
|
||||||
|
: isMatic
|
||||||
|
? 'matic-network'
|
||||||
|
: symbol?.toLowerCase()
|
||||||
|
|
||||||
|
return priceTokenId
|
||||||
|
}
|
36
src/@context/Prices/index.test.tsx
Normal file
36
src/@context/Prices/index.test.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import React, { ReactElement } from 'react'
|
||||||
|
import * as SWR from 'swr'
|
||||||
|
import { renderHook } from '@testing-library/react'
|
||||||
|
import { PricesProvider, usePrices, getCoingeckoTokenId } from '.'
|
||||||
|
|
||||||
|
jest.spyOn(SWR, 'default').mockImplementation(() => ({
|
||||||
|
useSWR: { data: { 'ocean-protocol': { eur: '2' } } },
|
||||||
|
isValidating: false,
|
||||||
|
mutate: jest.fn()
|
||||||
|
}))
|
||||||
|
|
||||||
|
const wrapper = ({ children }: { children: ReactElement }) => (
|
||||||
|
<PricesProvider>{children}</PricesProvider>
|
||||||
|
)
|
||||||
|
|
||||||
|
test('should correctly initialize data', async () => {
|
||||||
|
const { result } = renderHook(() => usePrices(), { wrapper })
|
||||||
|
|
||||||
|
expect(result.current.prices['ocean-protocol'].eur).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('useSWR is called', async () => {
|
||||||
|
const { result } = renderHook(() => usePrices(), { wrapper })
|
||||||
|
expect(SWR.default).toHaveBeenCalled()
|
||||||
|
|
||||||
|
// somehow the above spy seems to not fully work, but this assertion is the goal
|
||||||
|
// expect(result.current.prices['ocean-protocol'].eur).toBe('2')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should get correct Coingecko API ID for OCEAN', async () => {
|
||||||
|
const id1 = getCoingeckoTokenId('OCEAN')
|
||||||
|
expect(id1).toBe('ocean-protocol')
|
||||||
|
|
||||||
|
const id2 = getCoingeckoTokenId('mOCEAN')
|
||||||
|
expect(id2).toBe('ocean-protocol')
|
||||||
|
})
|
@ -9,24 +9,10 @@ import React, {
|
|||||||
import { fetchData } from '@utils/fetch'
|
import { fetchData } from '@utils/fetch'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { LoggerInstance } from '@oceanprotocol/lib'
|
import { LoggerInstance } from '@oceanprotocol/lib'
|
||||||
import { useMarketMetadata } from './MarketMetadata'
|
import { useMarketMetadata } from '../MarketMetadata'
|
||||||
|
import { Prices, PricesValue } from './_types'
|
||||||
interface Prices {
|
import { initialData, refreshInterval } from './_constants'
|
||||||
[key: string]: number
|
import { getCoingeckoTokenId } from './_utils'
|
||||||
}
|
|
||||||
|
|
||||||
interface PricesValue {
|
|
||||||
prices: Prices
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialData: Prices = {
|
|
||||||
eur: 0.0,
|
|
||||||
usd: 0.0,
|
|
||||||
eth: 0.0,
|
|
||||||
btc: 0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
const refreshInterval = 120000 // 120 sec.
|
|
||||||
|
|
||||||
const PricesContext = createContext(null)
|
const PricesContext = createContext(null)
|
||||||
|
|
||||||
@ -36,23 +22,23 @@ export default function PricesProvider({
|
|||||||
children: ReactNode
|
children: ReactNode
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
const { appConfig } = useMarketMetadata()
|
const { appConfig } = useMarketMetadata()
|
||||||
const tokenId = 'ocean-protocol'
|
|
||||||
|
|
||||||
const [prices, setPrices] = useState(initialData)
|
const [prices, setPrices] = useState(initialData)
|
||||||
const [url, setUrl] = useState('')
|
const [url, setUrl] = useState<string>()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!appConfig) return
|
if (!appConfig) return
|
||||||
// comma-separated list
|
|
||||||
const currencies = appConfig.currencies.join(',')
|
const currencies = appConfig.currencies.join(',')
|
||||||
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${tokenId}&vs_currencies=${currencies}`
|
const tokenIds = appConfig.coingeckoTokenIds.join(',')
|
||||||
|
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${tokenIds}&vs_currencies=${currencies}`
|
||||||
setUrl(url)
|
setUrl(url)
|
||||||
}, [appConfig])
|
}, [appConfig])
|
||||||
|
|
||||||
const onSuccess = async (data: { [tokenId]: Prices }) => {
|
const onSuccess = async (data: Prices) => {
|
||||||
if (!data) return
|
if (!data) return
|
||||||
LoggerInstance.log('[prices] Got new OCEAN spot prices.', data[tokenId])
|
LoggerInstance.log('[prices] Got new spot prices.', data)
|
||||||
setPrices(data[tokenId])
|
setPrices(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch new prices periodically with swr
|
// Fetch new prices periodically with swr
|
||||||
@ -71,4 +57,4 @@ export default function PricesProvider({
|
|||||||
// Helper hook to access the provider values
|
// Helper hook to access the provider values
|
||||||
const usePrices = (): PricesValue => useContext(PricesContext)
|
const usePrices = (): PricesValue => useContext(PricesContext)
|
||||||
|
|
||||||
export { PricesProvider, usePrices }
|
export { PricesProvider, usePrices, getCoingeckoTokenId }
|
@ -160,12 +160,15 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
// Helper: Get user balance
|
// Helper: Get user balance
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
const getUserBalance = useCallback(async () => {
|
const getUserBalance = useCallback(async () => {
|
||||||
if (!accountId || !networkId || !web3) return
|
if (!accountId || !networkId || !web3 || !networkData) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const balance: UserBalance = {
|
const userBalance = web3.utils.fromWei(
|
||||||
eth: web3.utils.fromWei(await web3.eth.getBalance(accountId, 'latest'))
|
await web3.eth.getBalance(accountId, 'latest')
|
||||||
}
|
)
|
||||||
|
const key = networkData.nativeCurrency.symbol.toLowerCase()
|
||||||
|
const balance: UserBalance = { [key]: userBalance }
|
||||||
|
|
||||||
if (approvedBaseTokens?.length > 0) {
|
if (approvedBaseTokens?.length > 0) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
approvedBaseTokens.map(async (token) => {
|
approvedBaseTokens.map(async (token) => {
|
||||||
@ -186,7 +189,7 @@ function Web3Provider({ children }: { children: ReactNode }): ReactElement {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
LoggerInstance.error('[web3] Error: ', error.message)
|
LoggerInstance.error('[web3] Error: ', error.message)
|
||||||
}
|
}
|
||||||
}, [accountId, approvedBaseTokens, networkId, web3])
|
}, [accountId, approvedBaseTokens, networkId, web3, networkData])
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Helper: Get user ENS name
|
// Helper: Get user ENS name
|
||||||
|
1
src/@types/TokenBalance.d.ts
vendored
1
src/@types/TokenBalance.d.ts
vendored
@ -1,4 +1,3 @@
|
|||||||
interface UserBalance {
|
interface UserBalance {
|
||||||
eth: string
|
|
||||||
[key: string]: string
|
[key: string]: string
|
||||||
}
|
}
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
import React, { ReactElement, useEffect, useState } from 'react'
|
|
||||||
import { usePrices } from '@context/Prices'
|
|
||||||
import { useWeb3 } from '@context/Web3'
|
|
||||||
import Web3 from 'web3'
|
|
||||||
import useNftFactory from '@hooks/contracts/useNftFactory'
|
|
||||||
import { NftFactory } from '@oceanprotocol/lib'
|
|
||||||
import Conversion from '@shared/Price/Conversion'
|
|
||||||
import { generateNftCreateData, NftMetadata } from '@utils/nft'
|
|
||||||
|
|
||||||
const getEstGasFee = async (
|
|
||||||
address: string,
|
|
||||||
nftFactory: NftFactory,
|
|
||||||
nftMetadata: NftMetadata,
|
|
||||||
ethToOceanConversionRate: number
|
|
||||||
): Promise<string> => {
|
|
||||||
if (!address || !nftFactory || !nftMetadata || !ethToOceanConversionRate)
|
|
||||||
return
|
|
||||||
|
|
||||||
const { web3 } = nftFactory
|
|
||||||
const nft = generateNftCreateData(nftMetadata, address)
|
|
||||||
|
|
||||||
const gasPrice = await web3.eth.getGasPrice()
|
|
||||||
const gasLimit = await nftFactory?.estGasCreateNFT(address, nft)
|
|
||||||
const gasFeeEth = Web3.utils.fromWei(
|
|
||||||
(+gasPrice * +gasLimit).toString(),
|
|
||||||
'ether'
|
|
||||||
)
|
|
||||||
const gasFeeOcean = (+gasFeeEth / +ethToOceanConversionRate).toString()
|
|
||||||
return gasFeeOcean
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function TxFee({
|
|
||||||
nftMetadata
|
|
||||||
}: {
|
|
||||||
nftMetadata: NftMetadata
|
|
||||||
}): ReactElement {
|
|
||||||
const { accountId } = useWeb3()
|
|
||||||
const { prices } = usePrices()
|
|
||||||
const nftFactory = useNftFactory()
|
|
||||||
const [gasFee, setGasFee] = useState('')
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const calculateGasFee = async () =>
|
|
||||||
setGasFee(
|
|
||||||
await getEstGasFee(
|
|
||||||
accountId,
|
|
||||||
nftFactory,
|
|
||||||
nftMetadata,
|
|
||||||
(prices as any)?.eth
|
|
||||||
)
|
|
||||||
)
|
|
||||||
calculateGasFee()
|
|
||||||
}, [accountId, nftFactory, nftMetadata, prices])
|
|
||||||
|
|
||||||
return gasFee ? (
|
|
||||||
<p>
|
|
||||||
Gas fee estimation for this artwork
|
|
||||||
<Conversion price={gasFee} />
|
|
||||||
</p>
|
|
||||||
) : accountId ? (
|
|
||||||
<p>
|
|
||||||
An error occurred while estimating the gas fee for this artwork, please
|
|
||||||
try again.
|
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<p>Please connect your wallet to get a gas fee estimate for this artwork</p>
|
|
||||||
)
|
|
||||||
}
|
|
@ -27,7 +27,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
padding: 0 calc(var(--spacer) / 4);
|
padding: calc(var(--spacer) / 4);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -5,8 +5,6 @@ import { useField } from 'formik'
|
|||||||
import React, { ReactElement, useEffect } from 'react'
|
import React, { ReactElement, useEffect } from 'react'
|
||||||
import Refresh from '@images/refresh.svg'
|
import Refresh from '@images/refresh.svg'
|
||||||
import styles from './index.module.css'
|
import styles from './index.module.css'
|
||||||
import Tooltip from '@shared/atoms/Tooltip'
|
|
||||||
import TxFee from './TxFee'
|
|
||||||
|
|
||||||
export default function Nft(props: InputProps): ReactElement {
|
export default function Nft(props: InputProps): ReactElement {
|
||||||
const [field, meta, helpers] = useField(props.name)
|
const [field, meta, helpers] = useField(props.name)
|
||||||
@ -28,7 +26,6 @@ export default function Nft(props: InputProps): ReactElement {
|
|||||||
<figure className={styles.image}>
|
<figure className={styles.image}>
|
||||||
<img src={field?.value?.image_data} width="128" height="128" />
|
<img src={field?.value?.image_data} width="128" height="128" />
|
||||||
<div className={styles.actions}>
|
<div className={styles.actions}>
|
||||||
<Tooltip content={<TxFee nftMetadata={field.value} />} />
|
|
||||||
<Button
|
<Button
|
||||||
style="text"
|
style="text"
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -1,18 +1,17 @@
|
|||||||
import React, { useEffect, useState, ReactElement } from 'react'
|
import React, { useEffect, useState, ReactElement } from 'react'
|
||||||
import styles from './Conversion.module.css'
|
import styles from './Conversion.module.css'
|
||||||
import classNames from 'classnames/bind'
|
|
||||||
import { formatCurrency, isCrypto } from '@coingecko/cryptoformat'
|
import { formatCurrency, isCrypto } from '@coingecko/cryptoformat'
|
||||||
import { useUserPreferences } from '@context/UserPreferences'
|
import { useUserPreferences } from '@context/UserPreferences'
|
||||||
import { usePrices } from '@context/Prices'
|
import { usePrices, getCoingeckoTokenId } from '@context/Prices'
|
||||||
|
|
||||||
const cx = classNames.bind(styles)
|
|
||||||
|
|
||||||
export default function Conversion({
|
export default function Conversion({
|
||||||
price,
|
price,
|
||||||
|
symbol,
|
||||||
className,
|
className,
|
||||||
hideApproximateSymbol
|
hideApproximateSymbol
|
||||||
}: {
|
}: {
|
||||||
price: string // expects price in OCEAN, not wei
|
price: string // expects price in OCEAN, not wei
|
||||||
|
symbol: string
|
||||||
className?: string
|
className?: string
|
||||||
hideApproximateSymbol?: boolean
|
hideApproximateSymbol?: boolean
|
||||||
}): ReactElement {
|
}): ReactElement {
|
||||||
@ -25,18 +24,21 @@ export default function Conversion({
|
|||||||
// isCrypto() only checks for BTC & ETH & unknown but seems sufficient for now
|
// isCrypto() only checks for BTC & ETH & unknown but seems sufficient for now
|
||||||
// const isFiat = /(EUR|USD|CAD|SGD|HKD|CNY|JPY|GBP|INR|RUB)/g.test(currency)
|
// const isFiat = /(EUR|USD|CAD|SGD|HKD|CNY|JPY|GBP|INR|RUB)/g.test(currency)
|
||||||
|
|
||||||
const styleClasses = cx({
|
// referring to Coingecko tokenId in Prices context provider
|
||||||
conversion: true,
|
const priceTokenId = getCoingeckoTokenId(symbol)
|
||||||
[className]: className
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!prices || !price || price === '0') {
|
if (
|
||||||
setPriceConverted('0.00')
|
!prices ||
|
||||||
|
!price ||
|
||||||
|
price === '0' ||
|
||||||
|
!priceTokenId ||
|
||||||
|
!prices[priceTokenId]
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const conversionValue = prices[currency.toLowerCase()]
|
const conversionValue = prices[priceTokenId][currency.toLowerCase()]
|
||||||
const converted = conversionValue * Number(price)
|
const converted = conversionValue * Number(price)
|
||||||
const convertedFormatted = formatCurrency(
|
const convertedFormatted = formatCurrency(
|
||||||
converted,
|
converted,
|
||||||
@ -54,16 +56,16 @@ export default function Conversion({
|
|||||||
(match) => `<span>${match}</span>`
|
(match) => `<span>${match}</span>`
|
||||||
)
|
)
|
||||||
setPriceConverted(convertedFormattedHTMLstring)
|
setPriceConverted(convertedFormattedHTMLstring)
|
||||||
}, [price, prices, currency, locale, isFiat])
|
}, [price, prices, currency, locale, isFiat, priceTokenId])
|
||||||
|
|
||||||
return (
|
return Number(price) > 0 ? (
|
||||||
<span
|
<span
|
||||||
className={styleClasses}
|
className={`${styles.conversion} ${className || ''}`}
|
||||||
title="Approximation based on the current selected base token spot price on Coingecko"
|
title="Approximation based on the current spot price on Coingecko"
|
||||||
>
|
>
|
||||||
{!hideApproximateSymbol && '≈ '}
|
{!hideApproximateSymbol && '≈ '}
|
||||||
<strong dangerouslySetInnerHTML={{ __html: priceConverted }} />{' '}
|
<strong dangerouslySetInnerHTML={{ __html: priceConverted }} />{' '}
|
||||||
{!isFiat && currency}
|
{!isFiat && currency}
|
||||||
</span>
|
</span>
|
||||||
)
|
) : null
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ export default function PriceUnit({
|
|||||||
{Number.isNaN(Number(price)) ? '-' : formatPrice(price, locale)}{' '}
|
{Number.isNaN(Number(price)) ? '-' : formatPrice(price, locale)}{' '}
|
||||||
<span className={styles.symbol}>{symbol}</span>
|
<span className={styles.symbol}>{symbol}</span>
|
||||||
</div>
|
</div>
|
||||||
{conversion && <Conversion price={price} />}
|
{conversion && <Conversion price={price} symbol={symbol} />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.balance {
|
.balance {
|
||||||
font-size: var(--font-size-base);
|
font-size: var(--font-size-small);
|
||||||
font-weight: var(--font-weight-bold);
|
font-weight: var(--font-weight-bold);
|
||||||
color: var(--color-secondary);
|
color: var(--color-secondary);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@ -32,6 +32,14 @@
|
|||||||
margin-right: 0.4rem;
|
margin-right: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: var(--font-color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversion strong {
|
||||||
|
font-weight: var(--font-weight-base);
|
||||||
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
border-top: 1px solid var(--border-color);
|
border-top: 1px solid var(--border-color);
|
||||||
margin-top: calc(var(--spacer) / 2);
|
margin-top: calc(var(--spacer) / 2);
|
||||||
|
@ -29,8 +29,7 @@ export default function Details(): ReactElement {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!networkId) return
|
if (!networkId) return
|
||||||
|
|
||||||
const symbol =
|
const symbol = networkData?.nativeCurrency.symbol
|
||||||
networkId === 2021000 ? 'GX' : networkData?.nativeCurrency.symbol
|
|
||||||
setMainCurrency(symbol)
|
setMainCurrency(symbol)
|
||||||
|
|
||||||
const oceanConfig = getOceanConfig(networkId)
|
const oceanConfig = getOceanConfig(networkId)
|
||||||
@ -49,11 +48,17 @@ export default function Details(): ReactElement {
|
|||||||
<li className={styles.balance} key={key}>
|
<li className={styles.balance} key={key}>
|
||||||
<span className={styles.symbol}>
|
<span className={styles.symbol}>
|
||||||
{key === 'eth' ? mainCurrency : key.toUpperCase()}
|
{key === 'eth' ? mainCurrency : key.toUpperCase()}
|
||||||
</span>{' '}
|
</span>
|
||||||
|
<span className={styles.value}>
|
||||||
{formatCurrency(Number(value), '', locale, false, {
|
{formatCurrency(Number(value), '', locale, false, {
|
||||||
significantFigures: 4
|
significantFigures: 4
|
||||||
})}
|
})}
|
||||||
{key === 'ocean' && <Conversion price={value} />}
|
</span>
|
||||||
|
<Conversion
|
||||||
|
className={styles.conversion}
|
||||||
|
price={value}
|
||||||
|
symbol={key}
|
||||||
|
/>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
@ -42,7 +42,13 @@ export default function Stats({
|
|||||||
<div className={styles.stats}>
|
<div className={styles.stats}>
|
||||||
<NumberUnit
|
<NumberUnit
|
||||||
label="Total Sales"
|
label="Total Sales"
|
||||||
value={<Conversion price={totalSales} hideApproximateSymbol />}
|
value={
|
||||||
|
<Conversion
|
||||||
|
price={totalSales}
|
||||||
|
symbol={'ocean'}
|
||||||
|
hideApproximateSymbol
|
||||||
|
/>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<NumberUnit label={`Sale${sales === 1 ? '' : 's'}`} value={sales} />
|
<NumberUnit label={`Sale${sales === 1 ? '' : 's'}`} value={sales} />
|
||||||
<NumberUnit label="Published" value={assetsTotal} />
|
<NumberUnit label="Published" value={assetsTotal} />
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import Input from '@shared/FormInput'
|
|
||||||
import InputElement from '@shared/FormInput/InputElement'
|
import InputElement from '@shared/FormInput/InputElement'
|
||||||
import { useFormikContext } from 'formik'
|
import { useFormikContext } from 'formik'
|
||||||
import React, { ChangeEvent, ReactElement } from 'react'
|
import React, { ChangeEvent, ReactElement } from 'react'
|
||||||
|
@ -52,7 +52,11 @@ export default function Price({
|
|||||||
<div className={styles.datatoken}>
|
<div className={styles.datatoken}>
|
||||||
<h4>
|
<h4>
|
||||||
= <strong>1</strong> {dataTokenOptions.symbol}{' '}
|
= <strong>1</strong> {dataTokenOptions.symbol}{' '}
|
||||||
<Conversion price={field.value} className={styles.conversion} />
|
<Conversion
|
||||||
|
price={field.value}
|
||||||
|
symbol={values.pricing?.baseToken?.symbol}
|
||||||
|
className={styles.conversion}
|
||||||
|
/>
|
||||||
</h4>
|
</h4>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user