Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: faster prediction prices #10154

Merged
merged 13 commits into from
Aug 20, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ export const AIPrediction = ({
</Text>
</Box>
<Link
external
fontSize={['12px', '12px', '14px']}
style={{ display: 'inline-flex', alignItems: 'center', whiteSpace: 'nowrap' }}
href={learnMoreLink}
color="primary"
fontSize={['12px', '12px', '12px']}
style={{ display: 'flex', alignItems: 'center', whiteSpace: 'nowrap' }}
href={ctaLink}
>
{t('Learn More')}
<ArrowForwardIcon width="14px" mt="2px" color="primary" />
{t('Participate Now')}
<ArrowForwardIcon width="12px" color="primary" style={{ marginRight: '-8px' }} />
</Link>
</Column>
</Flex>
Expand Down
6 changes: 3 additions & 3 deletions apps/web/src/pages/api/prediction/price/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ const handler: NextApiHandler = async (req, res) => {
price: string
} = await response.json()

res.setHeader('Cache-Control', 's-maxage=10, stale-while-revalidate=10')
res.setHeader('Vercel-CDN-Cache-Control', 'max-age=10')
res.setHeader('CDN-Cache-Control', 'max-age=10')
res.setHeader('Cache-Control', 's-maxage=5, stale-while-revalidate=5')
res.setHeader('Vercel-CDN-Cache-Control', 'max-age=5')
res.setHeader('CDN-Cache-Control', 'max-age=5')

return res.status(response.status).json({
currencyA,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { BetPosition } from '@pancakeswap/prediction'
import { Card, CardBody, Flex, PlayCircleOutlineIcon, Text, useTooltip } from '@pancakeswap/uikit'
import { formatBigInt, formatNumber } from '@pancakeswap/utils/formatBalance'
import RoundProgress from 'components/RoundProgress'
import { useEffect, useMemo, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { getHasRoundFailed } from 'state/predictions/helpers'
import { useGetBufferSeconds } from 'state/predictions/hooks'
import { NodeLedger, NodeRound } from 'state/types'
Expand Down Expand Up @@ -111,7 +111,11 @@ export const AILiveRoundCard: React.FC<React.PropsWithChildren<AILiveRoundCardPr
const secondsToClose = closeTimestamp ? closeTimestamp - getNowInSeconds() : 0
if (secondsToClose > 0) {
const refreshPriceTimeout = setTimeout(() => {
refetch()
// Fetch live price before round ends
if (config?.ai?.useAlternateSource) {
config.ai.useAlternateSource.current = true
refetch()
}
}, (secondsToClose - REFRESH_PRICE_BEFORE_SECONDS_TO_CLOSE) * 1000)

const calculatingPhaseTimeout = setTimeout(() => {
Expand All @@ -121,10 +125,13 @@ export const AILiveRoundCard: React.FC<React.PropsWithChildren<AILiveRoundCardPr
return () => {
clearTimeout(refreshPriceTimeout)
clearTimeout(calculatingPhaseTimeout)
if (config?.ai?.useAlternateSource) {
config.ai.useAlternateSource.current = false
}
}
}
return undefined
}, [refetch, closeTimestamp])
}, [closeTimestamp, config, refetch])

if (hasRoundFailed) {
return <CanceledRoundCard round={round} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import makeStore from 'contexts/LocalRedux/makeStore'
import { useActiveChainId } from 'hooks/useActiveChainId'
import _toUpper from 'lodash/toUpper'
import { useRouter } from 'next/router'
import { useEffect, useMemo, useState } from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import reducers, { initialState } from 'state/predictions'
import { usePredictionConfigs } from 'views/Predictions/hooks/usePredictionConfigs'
import { usePredictionToken } from 'views/Predictions/hooks/usePredictionToken'
Expand All @@ -17,6 +17,7 @@ const PredictionConfigProviders = ({ children }) => {
const predictionConfigs = usePredictionConfigs()
const [selectedPickedToken, setSelectedPickedToken] = useState('')
const [prevSelectedToken, setPrevSelectedToken] = usePredictionToken()
const useAlternateSourceRef = useRef(false)

const supportedSymbol = useMemo(() => (predictionConfigs ? Object.keys(predictionConfigs) : []), [predictionConfigs])

Expand Down Expand Up @@ -63,7 +64,16 @@ const PredictionConfigProviders = ({ children }) => {
return selectedPickedToken || supportedSymbol?.[0]
}, [chainId, prevSelectedToken, selectedPickedToken, supportedSymbol])

const config = useMemo(() => predictionConfigs?.[selectedToken], [predictionConfigs, selectedToken])
const config = useMemo(() => {
const selected = predictionConfigs?.[selectedToken]
if (selected?.ai) {
return {
...selected,
ai: { ...selected.ai, useAlternateSource: useAlternateSourceRef },
}
}
return selected
}, [predictionConfigs, selectedToken])

const store = useMemo(() => makeStore(reducers, initialState, config), [config])

Expand Down
33 changes: 24 additions & 9 deletions apps/web/src/views/Predictions/hooks/usePredictionPrice.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useQuery } from '@tanstack/react-query'
import { FAST_INTERVAL } from 'config/constants'
import { PREDICTION_PRICE_API } from 'config/constants/endpoints'
import { BINANCE_DATA_API, PREDICTION_PRICE_API } from 'config/constants/endpoints'
import { PriceApiWhitelistedCurrency } from 'config/constants/prediction/price'
import { useConfig } from '../context/ConfigProvider'

interface UsePredictionPriceParameters {
/** Default: ETH */
Expand All @@ -10,7 +10,7 @@ interface UsePredictionPriceParameters {
/** Default: USDT */
currencyB?: PriceApiWhitelistedCurrency | (string & NonNullable<unknown>)

/** Default: 10,000 milliseconds */
/** Default: 5,000 milliseconds */
pollingInterval?: number

enabled?: boolean
Expand All @@ -23,17 +23,32 @@ interface PriceResponse {
currencyB: PriceApiWhitelistedCurrency | (string & NonNullable<unknown>)
}

const DEFAULT_CURRENCY_A: PriceApiWhitelistedCurrency = 'ETH'
const DEFAULT_CURRENCY_B: PriceApiWhitelistedCurrency = 'USDT'
const DEFAULT_POLLING_INTERVAL = 5_000

export const usePredictionPrice = ({
currencyA = 'ETH',
currencyB = 'USDT',
pollingInterval = FAST_INTERVAL,
currencyA = DEFAULT_CURRENCY_A,
currencyB = DEFAULT_CURRENCY_B,
pollingInterval = DEFAULT_POLLING_INTERVAL,
enabled = true,
}: UsePredictionPriceParameters = {}) => {
const config = useConfig()

return useQuery<PriceResponse>({
memoyil marked this conversation as resolved.
Show resolved Hide resolved
queryKey: ['price', currencyA, currencyB],
Copy link
Collaborator

@memoyil memoyil Aug 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to ensure smooth price transition and to see the new price all the components that use this hook, the same querykey will be used

queryFn: async () =>
fetch(`${PREDICTION_PRICE_API}/?currencyA=${currencyA}&currencyB=${currencyB}`).then((res) => res.json()),
refetchInterval: pollingInterval,
queryFn: async () => {
return config?.ai?.useAlternateSource?.current
? fetch(`${BINANCE_DATA_API}/v3/ticker/price?symbol=${currencyA}${currencyB}`)
.then((res) => res.json())
.then((result) => ({
price: parseFloat(result.price),
currencyA,
currencyB,
}))
: fetch(`${PREDICTION_PRICE_API}?currencyA=${currencyA}&currencyB=${currencyB}`).then((res) => res.json())
},
refetchInterval: () => (config?.ai?.useAlternateSource?.current ? false : pollingInterval),
retry: 2,
initialData: {
price: 0,
Expand Down
5 changes: 5 additions & 0 deletions packages/prediction/src/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ export enum PredictionsChartView {
Pyth = 'Pyth Oracle',
}

interface MutableRefObject<T> {
current: T
}

type AIPredictionConfig = {
aiPriceDecimals?: number
useAlternateSource?: MutableRefObject<boolean>
}

export interface PredictionConfig {
Expand Down
Loading