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: Connect farm api #10829

Draft
wants to merge 22 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions apps/web/src/hooks/useFarm.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { ComputedFarmConfigV3, createFarmFetcherV3, fetchTokenUSDValues } from '@pancakeswap/farms'
import {
ComputedFarmConfigV3,
createFarmFetcherV3,
defineFarmV3ConfigsFromUniversalFarm,
fetchTokenUSDValues,
fetchUniversalFarms,
Protocol,
UniversalFarmConfigV3,
} from '@pancakeswap/farms'
import { priceHelperTokens } from '@pancakeswap/farms/constants/common'
import { Currency, ERC20Token } from '@pancakeswap/sdk'
import { FeeAmount, Pool } from '@pancakeswap/v3-sdk'
import { useQuery } from '@tanstack/react-query'
import { useMemo } from 'react'

import { legacyFarmsV3ConfigChainMap } from '@pancakeswap/farms/constants/v3'
import { FAST_INTERVAL } from 'config/constants'
import { useEffect, useMemo, useState } from 'react'
import { getViemClients } from 'utils/viem'

const farmFetcherV3 = createFarmFetcherV3(getViemClients)
Expand All @@ -19,18 +25,31 @@ interface FarmParams {

export function useFarm({ currencyA, currencyB, feeAmount }: FarmParams) {
const chainId = currencyA?.chainId
const [farms, setFarms] = useState<ComputedFarmConfigV3[]>([])

useEffect(() => {
const fetchFarmV3Config = async () => {
if (chainId) {
const farmsV3 = await fetchUniversalFarms(chainId, Protocol.V3)
setFarms(defineFarmV3ConfigsFromUniversalFarm(farmsV3 as UniversalFarmConfigV3[]))
}
}

fetchFarmV3Config()
}, [chainId])

const farmConfig = useMemo(() => {
if (!chainId || !currencyA || !currencyB || !feeAmount) {
return null
}
const farms: ComputedFarmConfigV3[] = legacyFarmsV3ConfigChainMap[chainId]

if (!farms) {
return null
}
const lpAddress = Pool.getAddress(currencyA.wrapped, currencyB.wrapped, feeAmount)
const farm = farms.find((f) => f.lpAddress === lpAddress)
return farm ?? null
}, [chainId, currencyA, currencyB, feeAmount])
}, [chainId, currencyA, currencyB, farms, feeAmount])

return useQuery({
queryKey: [chainId, farmConfig?.token0.symbol, farmConfig?.token1.symbol, farmConfig?.feeAmount],
Expand Down
5 changes: 3 additions & 2 deletions apps/web/src/pages/api/configs/farms/v2/[chain].ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChainId, chainNames, chainNameToChainId } from '@pancakeswap/chains'
import { formatUniversalFarmToSerializedFarm, UNIVERSAL_FARMS } from '@pancakeswap/farms'
import { fetchAllUniversalFarms, formatUniversalFarmToSerializedFarm } from '@pancakeswap/farms'
import { NextApiHandler } from 'next'
import { stringify } from 'viem'
import { enum as enum_, nativeEnum } from 'zod'
Expand All @@ -24,7 +24,8 @@ const handler: NextApiHandler = async (req, res) => {
}

try {
const farmConfig = UNIVERSAL_FARMS.filter((farm) => farm.chainId === chainId)
const fetchFarmConfig = await fetchAllUniversalFarms()
const farmConfig = fetchFarmConfig.filter((farm) => farm.chainId === chainId)
const legacyFarmConfig = formatUniversalFarmToSerializedFarm(farmConfig)
// cache for long time, it should revalidate on every deployment
res.setHeader('Cache-Control', `max-age=10800, s-maxage=31536000`)
Expand Down
6 changes: 2 additions & 4 deletions apps/web/src/pages/api/configs/farms/v2/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { formatUniversalFarmToSerializedFarm, UNIVERSAL_FARMS, UNIVERSAL_FARMS_WITH_TESTNET } from '@pancakeswap/farms'
import { fetchAllUniversalFarms, formatUniversalFarmToSerializedFarm } from '@pancakeswap/farms'
import { NextApiHandler } from 'next'
import { stringify } from 'viem'

const handler: NextApiHandler = async (req, res) => {
const includeTestnet = !!req.query.includeTestnet

try {
const farmConfig = includeTestnet ? UNIVERSAL_FARMS : UNIVERSAL_FARMS_WITH_TESTNET
const farmConfig = await fetchAllUniversalFarms()
const legacyFarmConfig = formatUniversalFarmToSerializedFarm(farmConfig)
// cache for long time, it should revalidate on every deployment
res.setHeader('Cache-Control', `max-age=10800, s-maxage=31536000`)
Expand Down
14 changes: 11 additions & 3 deletions apps/web/src/pages/api/v3/[chainId]/farms/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { ChainId } from '@pancakeswap/chains'
import { createFarmFetcherV3, fetchCommonTokenUSDValue } from '@pancakeswap/farms'
import {
createFarmFetcherV3,
defineFarmV3ConfigsFromUniversalFarm,
fetchCommonTokenUSDValue,
fetchUniversalFarms,
Protocol,
UniversalFarmConfigV3,
} from '@pancakeswap/farms'
import { priceHelperTokens } from '@pancakeswap/farms/constants/common'
import { legacyFarmsV3ConfigChainMap } from '@pancakeswap/farms/constants/v3'
import { NextApiHandler } from 'next'
import { getViemClients } from 'utils/viem.server'
import { nativeEnum as zNativeEnum } from 'zod'
Expand All @@ -22,7 +28,9 @@ const handler: NextApiHandler = async (req, res) => {
if (!farmFetcherV3.isChainSupported(chainId)) {
return res.status(400).json({ error: 'Chain not supported' })
}
const farms = legacyFarmsV3ConfigChainMap[chainId]

const fetchFarmsV3 = await fetchUniversalFarms(chainId, Protocol.V3)
const farms = defineFarmV3ConfigsFromUniversalFarm(fetchFarmsV3 as UniversalFarmConfigV3[])

const commonPrice = await fetchCommonTokenUSDValue(priceHelperTokens[chainId])

Expand Down
9 changes: 6 additions & 3 deletions apps/web/src/state/farmsV3/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import {
FarmsV3Response,
IPendingCakeByTokenId,
PositionDetails,
Protocol,
SerializedFarmsV3Response,
UniversalFarmConfigV3,
bCakeSupportedChainId,
createFarmFetcherV3,
defineFarmV3ConfigsFromUniversalFarm,
fetchUniversalFarms,
supportedChainIdV3,
} from '@pancakeswap/farms'
import { priceHelperTokens } from '@pancakeswap/farms/constants/common'
import { legacyFarmsV3ConfigChainMap } from '@pancakeswap/farms/constants/v3'
import { bCakeFarmBoosterVeCakeABI } from '@pancakeswap/farms/constants/v3/abi/bCakeFarmBoosterVeCake'
import { TvlMap, fetchCommonTokenUSDValue } from '@pancakeswap/farms/src/fetchFarmsV3'
import { deserializeToken } from '@pancakeswap/token-lists'
Expand Down Expand Up @@ -83,8 +86,8 @@ export const useFarmsV3Public = () => {
}

// direct copy from api routes, the client side fetch is preventing cache due to migration phase we want fresh data
const farms = legacyFarmsV3ConfigChainMap[chainId as ChainId]

const fetchFarmsV3 = await fetchUniversalFarms(chainId, Protocol.V3)
const farms = defineFarmV3ConfigsFromUniversalFarm(fetchFarmsV3 as UniversalFarmConfigV3[])
const commonPrice = await fetchCommonTokenUSDValue(priceHelperTokens[chainId ?? -1])

try {
Expand Down
50 changes: 31 additions & 19 deletions apps/web/src/state/farmsV4/state/accountPositions/fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { BCakeWrapperFarmConfig, Protocol, UNIVERSAL_FARMS, UNIVERSAL_FARMS_MAP } from '@pancakeswap/farms'
import { BCakeWrapperFarmConfig, Protocol, fetchAllUniversalFarms, fetchAllUniversalFarmsMap } from '@pancakeswap/farms'
import { CurrencyAmount, ERC20Token, Pair, Token, pancakePairV2ABI } from '@pancakeswap/sdk'
import { LegacyStableSwapPair } from '@pancakeswap/smart-router/legacy-router'
import { deserializeToken } from '@pancakeswap/token-lists'
Expand Down Expand Up @@ -66,18 +66,20 @@ type ITokenPair = [ERC20Token, ERC20Token]
// for v2 pools, we cannot fetch all positions from one contract
// so we simple get the most used pairs for fetch LP position
export const getTrackedV2LpTokens = memoize(
(
async (
chainId: number,
presetTokens: { [address: Address]: ERC20Token },
userSavedPairs: AppState['user']['pairs'],
): [ERC20Token, ERC20Token][] => {
): Promise<[ERC20Token, ERC20Token][]> => {
const pairTokens: ITokenPair[] = []
const fetchFarmConfig = await fetchAllUniversalFarms()

// from farms
UNIVERSAL_FARMS.filter(
(farm) => farm.protocol === 'v2' && farm.bCakeWrapperAddress && farm.chainId === chainId,
).forEach((farm) => {
pairTokens.push(farm.token0.sortsBefore(farm.token1) ? [farm.token0, farm.token1] : [farm.token1, farm.token0])
})
fetchFarmConfig
.filter((farm) => farm.protocol === 'v2' && farm.bCakeWrapperAddress && farm.chainId === chainId)
.forEach((farm) => {
pairTokens.push(farm.token0.sortsBefore(farm.token1) ? [farm.token0, farm.token1] : [farm.token1, farm.token0])
})
// from pinned pairs
if (PINNED_PAIRS[chainId]) {
PINNED_PAIRS[chainId].forEach((tokens: ITokenPair) => {
Expand Down Expand Up @@ -113,11 +115,10 @@ export const getTrackedV2LpTokens = memoize(
`${chainId}:${Object.keys(presetTokens).length}:${Object.values(userSavedPairs).length}`,
)

const V2_UNIVERSAL_FARMS = UNIVERSAL_FARMS.filter((farm) => farm.protocol === Protocol.V2)
const STABLE_UNIVERSAL_FARMS = UNIVERSAL_FARMS.filter((farm) => farm.protocol === Protocol.STABLE)
export const getBCakeWrapperAddress = async (lpAddress: Address, chainId: number) => {
const fetchUniversalFarmsMap = await fetchAllUniversalFarmsMap()

export const getBCakeWrapperAddress = (lpAddress: Address, chainId: number) => {
const f = UNIVERSAL_FARMS_MAP[`${chainId}:${lpAddress}`] as V2PoolInfo | StablePoolInfo | undefined
const f = fetchUniversalFarmsMap[`${chainId}:${lpAddress}`] as V2PoolInfo | StablePoolInfo | undefined

return f?.bCakeWrapperAddress ?? '0x'
}
Expand All @@ -136,10 +137,13 @@ export const getAccountV2LpDetails = async (

const validLpTokens = lpTokens.filter((token) => token.chainId === chainId)

const bCakeWrapperAddresses = validReserveTokens.map((tokens) => {
const lpAddress = getV2LiquidityToken(tokens).address
return getBCakeWrapperAddress(lpAddress, chainId)
})
const bCakeWrapperAddresses = await Promise.all(
validReserveTokens.map(async (tokens) => {
const lpAddress = getV2LiquidityToken(tokens).address
const bCakeWrapperAddress = await getBCakeWrapperAddress(lpAddress, chainId)
return bCakeWrapperAddress
}),
)

const balanceCalls = validLpTokens.map((token) => {
return {
Expand Down Expand Up @@ -208,6 +212,9 @@ export const getAccountV2LpDetails = async (
return [...acc, result]
}, [] as Array<readonly [bigint, bigint, bigint, bigint, bigint] | undefined>)

const farmConfig = await fetchAllUniversalFarms()
const V2_UNIVERSAL_FARMS = farmConfig.filter((farm) => farm.protocol === Protocol.V2)

return balances
.map((result, index) => {
const { result: _balance = 0n, status } = result
Expand Down Expand Up @@ -269,9 +276,11 @@ export const getStablePairDetails = async (

if (!account || !client || !validStablePairs.length) return []

const bCakeWrapperAddresses = validStablePairs.reduce((acc, pair) => {
return [...acc, getBCakeWrapperAddress(pair.lpAddress, chainId)]
}, [] as Array<Address>)
const bCakeWrapperAddresses = await Promise.all(
validStablePairs.reduce((acc, pair) => {
return [...acc, getBCakeWrapperAddress(pair.lpAddress, chainId)]
}, [] as Array<Promise<Address>>),
)

const balanceCalls = validStablePairs.map((pair) => {
return {
Expand Down Expand Up @@ -359,6 +368,8 @@ export const getStablePairDetails = async (
.then((res) => res.map((item) => item.result ?? [0n, 0n])),
])

const farmConfig = await fetchAllUniversalFarms()

const result = validStablePairs.map((pair, index) => {
const nativeBalance = CurrencyAmount.fromRawAmount(pair.liquidityToken, balances[index])
const farmingInfo = farming[index]
Expand All @@ -381,6 +392,7 @@ export const getStablePairDetails = async (
const farmingDeposited0 = CurrencyAmount.fromRawAmount(token1.wrapped, farmingToken0Amount.toString())
const farmingDeposited1 = CurrencyAmount.fromRawAmount(token1.wrapped, farmingToken1Amount.toString())

const STABLE_UNIVERSAL_FARMS = farmConfig.filter((farm) => farm.protocol === Protocol.STABLE)
const isStaked = !!STABLE_UNIVERSAL_FARMS.find((farm) => farm.lpAddress === pair.lpAddress)

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ERC20Token } from '@pancakeswap/sdk'
import { useQueries, UseQueryOptions, UseQueryResult } from '@tanstack/react-query'
import { SLOW_INTERVAL } from 'config/constants'
import { useOfficialsAndUserAddedTokensByChainIds } from 'hooks/Tokens'
import { useCallback, useMemo } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useSelector } from 'react-redux'
import { AppState } from 'state'
import { Address } from 'viem'
Expand All @@ -13,18 +13,33 @@ import { useLatestTxReceipt } from './useLatestTxReceipt'
export const useAccountV2LpDetails = (chainIds: number[], account?: Address | null) => {
const tokens = useOfficialsAndUserAddedTokensByChainIds(chainIds)
const userSavedPairs = useSelector<AppState, AppState['user']['pairs']>(({ user: { pairs } }) => pairs)
const lpTokensByChain = useMemo(() => {
const result: Record<number, [ERC20Token, ERC20Token][]> = {}
chainIds.forEach((chainId) => {
const lpTokens = getTrackedV2LpTokens(chainId, tokens[chainId], userSavedPairs)
if (lpTokens && lpTokens.length > 0) {
result[chainId] = lpTokens
}
})
return result
const [lpTokensByChain, setLpTokensByChain] = useState<Record<number, [ERC20Token, ERC20Token][]> | null>(null)

useEffect(() => {
const fetchLpTokens = async () => {
const result: Record<number, [ERC20Token, ERC20Token][]> = {}

await Promise.all(
chainIds.map(async (chainId) => {
const lpTokens = await getTrackedV2LpTokens(chainId, tokens[chainId], userSavedPairs)
if (lpTokens && lpTokens.length > 0) {
result[chainId] = lpTokens
}
}),
)

setLpTokensByChain(result)
}

fetchLpTokens()
}, [chainIds, tokens, userSavedPairs])

const [latestTxReceipt] = useLatestTxReceipt()
const queries = useMemo(() => {
if (!lpTokensByChain) {
return []
}

return Object.entries(lpTokensByChain).map(([chainId, lpTokens]) => {
return {
queryKey: ['accountV2LpDetails', account, chainId, lpTokens.length, latestTxReceipt?.blockHash],
Expand Down
17 changes: 11 additions & 6 deletions apps/web/src/state/farmsV4/state/extendPools/fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getChainNameInKebabCase } from '@pancakeswap/chains'
import { UNIVERSAL_FARMS, UNIVERSAL_FARMS_MAP } from '@pancakeswap/farms'
import { fetchAllUniversalFarms, fetchAllUniversalFarmsMap } from '@pancakeswap/farms'
import set from 'lodash/set'
import { chainIdToExplorerInfoChainName, explorerApiClient } from 'state/info/api/client'
import { PoolInfo, StablePoolInfo, V2PoolInfo } from '../type'
Expand Down Expand Up @@ -33,20 +33,22 @@ export const fetchExplorerPoolsList = async (query: Required<ExtendPoolsQuery>,
}

const { rows, endCursor, startCursor, hasNextPage, hasPrevPage } = resp.data
const pools = await parseFarmPools(rows)

return {
pools: parseFarmPools(rows),
pools,
endCursor,
startCursor,
hasNextPage,
hasPrevPage,
}
}

const composeFarmConfig = (farm: PoolInfo) => {
const composeFarmConfig = async (farm: PoolInfo) => {
if (farm.protocol !== 'stable' && farm.protocol !== 'v2') return farm

const localFarm = UNIVERSAL_FARMS_MAP[`${farm.chainId}:${farm.lpAddress}`] as V2PoolInfo | StablePoolInfo | undefined
const farmConfig = await fetchAllUniversalFarmsMap()
const localFarm = farmConfig[`${farm.chainId}:${farm.lpAddress}`] as V2PoolInfo | StablePoolInfo | undefined

if (!localFarm) {
return farm
Expand Down Expand Up @@ -78,7 +80,10 @@ export const fetchExplorerPoolInfo = async <TPoolType extends PoolInfo>(
}
// @ts-ignore
resp.data.chainId = chainId
const isFarming = UNIVERSAL_FARMS.some((farm) => farm.lpAddress === poolAddress)
const farmConfig = await fetchAllUniversalFarms()
const isFarming = farmConfig.some((farm) => farm.lpAddress.toLowerCase() === poolAddress.toLowerCase())
const farm = await parseFarmPools([resp.data], { isFarming })
const data = await composeFarmConfig(farm[0])

return composeFarmConfig(parseFarmPools([resp.data], { isFarming })[0]) as TPoolType
return data as TPoolType
}
5 changes: 4 additions & 1 deletion apps/web/src/state/farmsV4/state/extendPools/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,10 @@ export const usePoolInfo = <TPoolType extends PoolInfo>({
}): TPoolType | undefined | null => {
const { data: poolInfo } = useQuery({
queryKey: ['poolInfo', chainId, poolAddress],
queryFn: () => fetchExplorerPoolInfo(poolAddress ?? '', chainId),
queryFn: async () => {
const result = await fetchExplorerPoolInfo(poolAddress ?? '', chainId)
return result
},
enabled: !!poolAddress && !!chainId,
})

Expand Down
6 changes: 4 additions & 2 deletions apps/web/src/state/farmsV4/state/farmPools/fetcher.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { getChainNameInKebabCase } from '@pancakeswap/chains'
import {
FarmV4SupportedChainId,
fetchAllUniversalFarms,
masterChefV3Addresses,
Protocol,
supportedChainIdV4,
UNIVERSAL_FARMS,
} from '@pancakeswap/farms'
import { smartChefABI } from '@pancakeswap/pools'
import { getStableSwapPools } from '@pancakeswap/stable-swap-sdk'
Expand Down Expand Up @@ -64,7 +64,9 @@ export const fetchFarmPools = async (
} catch (error) {
console.error('Failed to fetch remote pools', error)
}
const localPools = UNIVERSAL_FARMS.filter((farm) => {

const fetchFarmConfig = await fetchAllUniversalFarms()
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think we can fetch these in parallel:
remotePools = await fetchExplorerFarmPools(args, signal) and
const fetchFarmConfig = await fetchAllUniversalFarms()

and handle error individually for remotePools?

const localPools = fetchFarmConfig.filter((farm) => {
return (
args.protocols?.includes(farm.protocol) &&
(Array.isArray(args.chainId) ? args.chainId.includes(farm.chainId) : farm.chainId === args.chainId)
Expand Down
Loading
Loading