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

feat: amm trade add shadow request 1% #10931

Merged
merged 12 commits into from
Nov 14, 2024
6 changes: 6 additions & 0 deletions apps/web/src/config/experimentalFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export enum EXPERIMENTAL_FEATURES {
SpeedQuote = 'routing-speed-quote',
PriceAPI = 'price-api',
PCSX = 'pcsx',
OPTIMIZED_AMM_TRADE = 'optimized-amm-trade',
}

export type EnumValues<T> = T extends { [key: string]: infer U } ? U : never
Expand Down Expand Up @@ -41,4 +42,9 @@ export const EXPERIMENTAL_FEATURE_CONFIGS: ExperimentalFeatureConfigs = [
percentage: 1,
whitelist: [],
},
{
feature: EXPERIMENTAL_FEATURES.OPTIMIZED_AMM_TRADE,
percentage: 0.01,
whitelist: [],
},
]
159 changes: 150 additions & 9 deletions apps/web/src/hooks/useBestAMMTrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ import { useCallback, useDeferredValue, useEffect, useMemo, useRef } from 'react
import { zeroAddress } from 'viem'
import { useAccount } from 'wagmi'

import { QUOTING_API, QUOTING_API_PREFIX } from 'config/constants/endpoints'
import { POOLS_FAST_REVALIDATE, POOLS_NORMAL_REVALIDATE } from 'config/pools'
import { useIsWrapping } from 'hooks/useWrapCallback'
import { useCurrentBlock } from 'state/block/hooks'
import { useFeeDataWithGasPrice } from 'state/user/hooks'
import { tracker } from 'utils/datadog'
import { basisPointsToPercent } from 'utils/exchange'
import { createViemPublicClientGetter } from 'utils/viem'
import { publicClient } from 'utils/wagmi'
import { basisPointsToPercent } from 'utils/exchange'

import { EXPERIMENTAL_FEATURES } from 'config/experimentalFeatures'
import useNativeCurrency from 'hooks/useNativeCurrency'

import { QUOTING_API } from 'config/constants/endpoints'
import {
CommonPoolsParams,
PoolsWithState,
Expand All @@ -39,7 +41,7 @@ import {
useCommonPools as useCommonPoolsWithTicks,
} from './useCommonPools'
import { useCurrencyUsdPrice } from './useCurrencyUsdPrice'
// import { useExperimentalFeatureEnabled } from './useExperimentalFeatureEnabled'
import { useExperimentalFeature } from './useExperimentalFeatureEnabled'
import { useMulticallGasLimit } from './useMulticallGasLimit'
import { useSpeedQuote } from './useSpeedQuote'
import { useTokenFee } from './useTokenFee'
Expand Down Expand Up @@ -667,6 +669,142 @@ export function useBestTradeFromApi({
})
}

export function useBestTradeFromApiShadow(
{
// baseCurrency,
amount,
currency,
maxHops,
maxSplits,
stableSwap,
trackPerf,
tradeType = TradeType.EXACT_INPUT,
v2Swap,
v3Swap,
retry = false,
}: Options,
queryType: 'quote-api-ori' | 'quote-api-opt',
) {
const QUOTING_API_PREFIX_ORIGINAL = 'https://pcsx-order-price-api-test-master-viosr.ondigitalocean.app'
const QUOTING_API_PREFIX_OPTIMIZED = 'https://pcsx-order-price-api-test-branch-nfu7y.ondigitalocean.app'
const prefix = queryType === 'quote-api-ori' ? QUOTING_API_PREFIX_ORIGINAL : QUOTING_API_PREFIX_OPTIMIZED
const { enabled: featureFlag } = useExperimentalFeature(EXPERIMENTAL_FEATURES.OPTIMIZED_AMM_TRADE)

const [slippage] = useUserSlippage()
const poolTypes = useMemo(() => {
const types: PoolType[] = []
if (v2Swap) {
types.push(PoolType.V2)
}
if (v3Swap) {
types.push(PoolType.V3)
}
if (stableSwap) {
types.push(PoolType.STABLE)
}
if (types.length === 0) {
return undefined
}
return types
}, [v2Swap, v3Swap, stableSwap])

const deferQuotientRaw = useDeferredValue(amount?.quotient?.toString())

const deferQuotient = useDebounce(deferQuotientRaw, 500)
const enabled = featureFlag && !!(amount && currency?.chainId && deferQuotient && poolTypes?.length)
useTradeApiPrefetch(
{
currencyA: amount?.currency,
currencyB: currency,
enabled,
poolTypes,
},
prefix,
queryType,
)
const { address } = useAccount()
const { gasPrice } = useFeeDataWithGasPrice()

const previousEnabled = usePreviousValue(Boolean(currency?.chainId))

return useQuery({
enabled: featureFlag && !!(amount && currency?.chainId && deferQuotient && poolTypes?.length),
refetchInterval: POOLS_FAST_REVALIDATE[currency?.chainId as keyof typeof POOLS_FAST_REVALIDATE] ?? 10_000,
queryKey: [
queryType,
address,
currency?.chainId,
amount?.currency?.symbol,
currency?.symbol,
tradeType,
deferQuotient,
maxHops,
maxSplits,
poolTypes,
slippage,
] as const,
retry,
queryFn: async ({ signal, queryKey }) => {
const [key] = queryKey
if (!amount || !amount.currency || !currency || !deferQuotient) {
throw new Error('Invalid amount or currency')
}

const startTime = performance.now()

const body = getRequestBody({
amount,
quoteCurrency: currency,
tradeType,
slippage: basisPointsToPercent(slippage),
amm: { maxHops, maxSplits, poolTypes, gasPriceWei: gasPrice },
x: {
useSyntheticQuotes: true,
swapper: address,
},
})

const serverRes = await fetch(`${prefix}/get-price-with-amm`, {
method: 'POST',
signal,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
const serializedRes = await serverRes.json()

const isExactIn = tradeType === TradeType.EXACT_INPUT
const result = parseQuoteResponse(serializedRes, {
chainId: currency.chainId,
currencyIn: isExactIn ? amount.currency : currency,
currencyOut: isExactIn ? currency : amount.currency,
tradeType,
})

const duration = Math.floor(performance.now() - startTime)

if (trackPerf) {
tracker.log(`[PERF] ${key} duration:${duration}ms`, {
chainId: currency.chainId,
label: key,
duration,
})
}

return result
},
placeholderData: (previousData, previousQuery) => {
const queryKey = previousQuery?.queryKey

if (!queryKey) return undefined
if (!previousEnabled) return undefined

return previousData
},
})
}

export const useBestAMMTradeFromQuoterApi = bestTradeHookFactory<V4Router.V4TradeWithoutGraph<TradeType>>({
key: 'useBestAMMTradeFromPriceAPI',
useCommonPools: useCommonPoolsLite,
Expand Down Expand Up @@ -801,20 +939,23 @@ type PrefetchParams = {
function getCurrencyIdentifierForApi(currency: Currency) {
return currency.isNative ? zeroAddress : currency.address
}

export function useTradeApiPrefetch({ currencyA, currencyB, poolTypes, enabled = true }: PrefetchParams) {
export function useTradeApiPrefetch(
{ currencyA, currencyB, poolTypes, enabled }: PrefetchParams,
prefix: string,
queryType: string,
) {
return useQuery({
enabled: !!(currencyA && currencyB && poolTypes?.length && enabled),
queryKey: ['quote-api-prefetch', currencyA?.chainId, currencyA?.symbol, currencyB?.symbol, poolTypes] as const,
queryKey: [`${queryType}-prefetch`, currencyA?.chainId, currencyA?.symbol, currencyB?.symbol, poolTypes] as const,
queryFn: async ({ signal }) => {
if (!currencyA || !currencyB || !poolTypes?.length) {
throw new Error('Invalid prefetch params')
}

const serverRes = await fetch(
`${QUOTING_API_PREFIX}/_pools/${currencyA.chainId}/${getCurrencyIdentifierForApi(
currencyA,
)}/${getCurrencyIdentifierForApi(currencyB)}?${qs.stringify({ protocols: poolTypes.map(getPoolTypeKey) })}`,
`${prefix}/_pools/${currencyA.chainId}/${getCurrencyIdentifierForApi(currencyA)}/${getCurrencyIdentifierForApi(
currencyB,
)}?${qs.stringify({ protocols: poolTypes.map(getPoolTypeKey) })}`,
{
method: 'GET',
signal,
Expand Down
10 changes: 7 additions & 3 deletions apps/web/src/views/Swap/V3Swap/hooks/useSwapBestTrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import tryParseAmount from '@pancakeswap/utils/tryParseAmount'
import { useUserSingleHopOnly } from '@pancakeswap/utils/user'

import { useCurrency } from 'hooks/Tokens'
import { useBestAMMTrade, useBestTradeFromApi } from 'hooks/useBestAMMTrade'
import { useBestAMMTrade, useBestTradeFromApi, useBestTradeFromApiShadow } from 'hooks/useBestAMMTrade'
import { usePCSXEnabledOnChain } from 'hooks/usePCSX'
import { useCallback, useDeferredValue, useMemo, useState } from 'react'
import { Field } from 'state/swap/actions'
Expand Down Expand Up @@ -45,7 +45,7 @@ export function useSwapBestOrder({ maxHops }: Options = {}) {
return stableSwap && isExactIn
}, [stableSwap, isExactIn])

const { fetchStatus, data, isStale, error, refetch } = useBestTradeFromApi({
const bestTradeOptions = {
enabled,
amount,
currency: dependentCurrency,
Expand All @@ -58,7 +58,11 @@ export function useSwapBestOrder({ maxHops }: Options = {}) {
stableSwap: stableSwapEnable,
trackPerf: true,
retry: 1,
})
}
const { fetchStatus, data, isStale, error, refetch } = useBestTradeFromApi(bestTradeOptions)
useBestTradeFromApiShadow(bestTradeOptions, 'quote-api-ori')
useBestTradeFromApiShadow(bestTradeOptions, 'quote-api-opt')
Comment on lines +63 to +64
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's enable for all chains. Previously the api is only enabled on arb and eth

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


const [loading, setLoading] = useState(false)
const refresh = useCallback(async () => {
try {
Expand Down
Loading