Skip to content

Commit

Permalink
fix: Filter out whitelisted merkl awards (#10680)
Browse files Browse the repository at this point in the history
<!--
Before opening a pull request, please read the [contributing
guidelines](https://github.com/pancakeswap/pancake-frontend/blob/develop/CONTRIBUTING.md)
first
-->

<!-- start pr-codex -->

---

## PR-Codex overview
This PR primarily focuses on refining the handling of `undefined` values
instead of `null` across various components, improving type safety and
consistency in the codebase.

### Detailed summary
- Updated props to accept `undefined` instead of `null` in multiple
components.
- Enhanced `usePoolInfo` to accept `poolAddress` as `0x${string} |
undefined`.
- Modified `getMerklApr` to accept results as a parameter.
- Adjusted `MerklTag` and `MerklRewardsTag` to accept optional
`poolAddress`.
- Improved error handling in Merkl APR fetching.
- Refined logic in `LiquidityCardRow` to conditionally compute
`poolAddress`.

> ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your
question}`

<!-- end pr-codex -->
  • Loading branch information
memoyil authored Oct 23, 2024
1 parent 36cbc2b commit 0e5d9af
Show file tree
Hide file tree
Showing 19 changed files with 113 additions and 85 deletions.
13 changes: 10 additions & 3 deletions apps/web/src/components/Merkl/MerklSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import { CurrencyLogo } from '@pancakeswap/widgets-internal'
import { LightGreyCard } from 'components/Card'

import { Currency, CurrencyAmount } from '@pancakeswap/swap-sdk-core'
import { useMemo } from 'react'
import { getMerklLink } from 'utils/getMerklLink'
import { ChainId } from '@pancakeswap/chains'
import useMerkl from '../../hooks/useMerkl'

function TextWarning({ tokenAmount }: { tokenAmount: CurrencyAmount<Currency> }) {
Expand Down Expand Up @@ -51,19 +54,23 @@ const LearnMoreLink = () => {

export function MerklSection({
poolAddress,
chainId,
notEnoughLiquidity,
outRange,
disabled,
}: {
poolAddress?: `0x${string}`
chainId?: ChainId
notEnoughLiquidity: boolean
outRange: boolean
disabled: boolean
poolAddress: string | null
notEnoughLiquidity: boolean
}) {
const { t } = useTranslation()

const { claimTokenReward, isClaiming, rewardsPerToken, hasMerkl } = useMerkl(poolAddress)

const merklLink = useMemo(() => getMerklLink({ chainId, lpAddress: poolAddress }), [chainId, poolAddress])

if (!rewardsPerToken.length || (!hasMerkl && rewardsPerToken.every((r) => r.equalTo('0')))) return null

return (
Expand Down Expand Up @@ -118,7 +125,7 @@ export function MerklSection({
external
color="currentColor"
style={{ display: 'inline-flex' }}
href="https://merkl.angle.money/?search=PancakeSwap&status=live%2Csoon"
href={merklLink ?? 'https://merkl.angle.money/?search=PancakeSwap&status=live%2Csoon'}
>
{t('here')}
</Link>
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/Merkl/MerklTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useTranslation } from '@pancakeswap/localization'
import { Tag } from '@pancakeswap/uikit'
import { useMerklInfo } from 'hooks/useMerkl'

export function MerklTag({ poolAddress }: { poolAddress: string | null }) {
export function MerklTag({ poolAddress }: { poolAddress?: string }) {
const { t } = useTranslation()
const { hasMerkl } = useMerklInfo(poolAddress)

Expand All @@ -15,7 +15,7 @@ export function MerklTag({ poolAddress }: { poolAddress: string | null }) {
)
}

export function MerklRewardsTag({ poolAddress }: { poolAddress: string | null }) {
export function MerklRewardsTag({ poolAddress }: { poolAddress?: string }) {
const { t } = useTranslation()
const { hasMerkl } = useMerklInfo(poolAddress)

Expand Down
66 changes: 32 additions & 34 deletions apps/web/src/hooks/useMerkl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ import { useAllLists } from 'state/lists/hooks'
import { getContract } from 'utils/contractHelpers'
import { Address } from 'viem'
import { useWalletClient } from 'wagmi'
import { useMasterchefV3 } from 'hooks/useContract'

export const MERKL_API_V2 = 'https://api.angle.money/v2/merkl'

export function useMerklInfo(poolAddress: string | null): {
export function useMerklInfo(poolAddress?: string): {
rewardsPerToken: CurrencyAmount<Currency>[]
isPending: boolean
transactionData: {
Expand All @@ -34,31 +35,9 @@ export function useMerklInfo(poolAddress: string | null): {
merklApr?: number
} {
const { account, chainId } = useAccountActiveChain()
const masterChefV3Address = useMasterchefV3()?.address as Address
const lists = useAllLists()

const { data: merklApr } = useQuery({
queryKey: ['merklAprData', chainId],
queryFn: async () => {
const resp = await fetch(`https://api.angle.money/v2/merkl?chainIds[]=${chainId}&AMMs[]=pancakeswapv3`)
if (resp.ok) {
const result = await resp.json()
return result
}
throw resp
},
enabled: Boolean(chainId && poolAddress),
staleTime: FAST_INTERVAL,
retryDelay: (attemptIndex) => Math.min(2000 * 2 ** attemptIndex, 30000),
select: useCallback(
(data) => {
return data?.[chainId ?? 0]?.pools?.[poolAddress ?? '']?.aprs?.['Average APR (rewards / pool TVL)'] as
| number
| undefined
},
[chainId, poolAddress],
),
})

const { data, isPending, refetch } = useQuery({
queryKey: [`fetchMerkl-${chainId}-${account || 'no-account'}`],
queryFn: async () => {
Expand All @@ -74,9 +53,7 @@ export function useMerklInfo(poolAddress: string | null): {

if (!chainId || !merklDataV2[chainId]) return null

const { pools, transactionData } = merklDataV2[chainId]

return { pools, transactionData }
return merklDataV2[chainId]
},
enabled: Boolean(chainId && poolAddress),
staleTime: FAST_INTERVAL,
Expand All @@ -96,11 +73,28 @@ export function useMerklInfo(poolAddress: string | null): {

const { pools, transactionData } = data

const hasLive = first(
Object.keys(pools)
.filter((poolId) => poolId === poolAddress && pools[poolId].meanAPR !== 0)
.map((poolId) => pools[poolId]),
)
const hasLive = Object.keys(pools)
.filter((poolId) => poolId === poolAddress)
.some((poolId) => {
const pool = pools[poolId]
const hasMeanAPR = pool.meanAPR > 0

if (!hasMeanAPR) return false

const hasLiveDistribution = pool.distributionData.some((distribution) => {
const { isLive, whitelist } = distribution
if (!isLive) return false
const whitelistValid =
!whitelist ||
whitelist.length === 0 ||
whitelist.includes(account) ||
whitelist.includes(masterChefV3Address)

return whitelistValid
})

return hasLiveDistribution
})

const merklPoolData = first(
Object.keys(pools)
Expand Down Expand Up @@ -148,16 +142,20 @@ export function useMerklInfo(poolAddress: string | null): {
return CurrencyAmount.fromRawAmount(t, '0')
})

const merklApr = data?.[chainId ?? 0]?.pools?.[poolAddress ?? '']?.aprs?.['Average APR (rewards / pool TVL)'] as
| number
| undefined

return {
...rest,
rewardsPerToken: rewardsPerToken.length ? rewardsPerToken : rewardCurrencies,
refreshData: refetch,
merklApr,
}
}, [chainId, data, lists, refetch, merklApr, isPending, poolAddress])
}, [chainId, data, lists, refetch, isPending, poolAddress, account, masterChefV3Address])
}

export default function useMerkl(poolAddress: string | null) {
export default function useMerkl(poolAddress?: string) {
const { account, chainId } = useAccountActiveChain()

const { data: signer } = useWalletClient()
Expand Down
3 changes: 2 additions & 1 deletion apps/web/src/pages/liquidity/[tokenId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export default function PoolPage() {
return undefined
}, [liquidity, pool, tickLower, tickUpper])

const poolAddress = useMemo(() => pool && Pool.getAddress(pool.token0, pool.token1, pool.fee), [pool])
const poolAddress = useMemo(() => (pool ? Pool.getAddress(pool.token0, pool.token1, pool.fee) : undefined), [pool])

const poolInfo = usePoolInfo({ poolAddress, chainId })

Expand Down Expand Up @@ -805,6 +805,7 @@ export default function PoolPage() {
: false,
)}
poolAddress={poolAddress}
chainId={pool?.chainId}
/>
</Flex>
{positionDetails && currency0 && currency1 && (
Expand Down
1 change: 1 addition & 0 deletions apps/web/src/pages/liquidity/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ export default function PoolListPage() {
<LiquidityCardRow
feeAmount={feeAmount}
link={positionSummaryLink}
outOfRange={outOfRange}
currency0={currencyQuote}
currency1={currencyBase}
tokenId={p.tokenId}
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/pages/stable/[address].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default function StablePoolPage() {
[lpTokens, poolAddress],
)

const poolInfo = usePoolInfo({ poolAddress: selectedLp ? selectedLp?.stableSwapAddress : null, chainId })
const poolInfo = usePoolInfo({ poolAddress: selectedLp ? selectedLp?.stableSwapAddress : undefined, chainId })

const stableSwapInfoContract = useInfoStableSwapContract(selectedLp?.infoStableSwapAddress)

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/pages/v2/pair/[[...currency]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export default function PoolV2Page() {

const [, pair] = useV2Pair(baseCurrency ?? undefined, currencyB ?? undefined)

const poolInfo = usePoolInfo({ poolAddress: pair ? pair.liquidityToken.address : null, chainId })
const poolInfo = usePoolInfo({ poolAddress: pair ? pair.liquidityToken.address : undefined, chainId })

const { data: positionDetails } = useAccountPositionDetailByPool<Protocol.V2>(
poolInfo?.chainId ?? chainId,
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/state/farmsV4/state/extendPools/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const getPoolAddressByToken = memoize(
const token0 = new Token(chainId, token0Address, 18, '')
const token1 = new Token(chainId, token1Address, 18, '')
if (!token0 || !token1) {
return null
return undefined
}
return computePoolAddress({
deployerAddress,
Expand All @@ -95,7 +95,7 @@ export const usePoolInfo = <TPoolType extends PoolInfo>({
poolAddress,
chainId,
}: {
poolAddress: string | null
poolAddress: `0x${string}` | undefined
chainId: number
}): TPoolType | undefined | null => {
const { data: poolInfo } = useQuery({
Expand Down
44 changes: 19 additions & 25 deletions apps/web/src/state/farmsV4/state/poolApr/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,38 +196,32 @@ export const getV2PoolCakeApr = async (
}
}

export const getMerklApr = async (chainId: number, signal?: AbortSignal) => {
export const getMerklApr = async (result: any, chainId: number) => {
try {
// @todo @ChefJerry merkl api cannot accept multiple chainIds, we need to batch fetch
const resp = await fetch(`https://api.angle.money/v2/merkl?chainIds=${chainId}&AMMs=pancakeswapv3`, { signal })
if (resp.ok) {
const result = await resp.json()
if (!result[chainId] || !result[chainId].pools) return {}
return Object.keys(result[chainId].pools).reduce((acc, poolId) => {
const key = `${chainId}:${safeGetAddress(poolId)}`
if (!result[chainId].pools[poolId].aprs || !Object.keys(result[chainId].pools[poolId].aprs).length) return acc

const apr = result[chainId].pools[poolId].aprs?.['Average APR (rewards / pool TVL)'] ?? '0'
// eslint-disable-next-line no-param-reassign
acc[key] = apr / 100
return acc
}, {} as MerklApr)
}
throw resp
if (!result[chainId] || !result[chainId].pools) return {}
return Object.keys(result[chainId].pools).reduce((acc, poolId) => {
const key = `${chainId}:${safeGetAddress(poolId)}`
if (!result[chainId].pools[poolId].aprs || !Object.keys(result[chainId].pools[poolId].aprs).length) return acc

const apr = result[chainId].pools[poolId].aprs?.['Average APR (rewards / pool TVL)'] ?? '0'
// eslint-disable-next-line no-param-reassign
acc[key] = apr / 100
return acc
}, {} as MerklApr)
} catch (error) {
if (error instanceof Error) {
if (error.name === 'AbortError') {
throw error
}
}
console.error('Failed to fetch merkl apr', error)
console.error('Failed to process merkl apr', error)
return {}
}
}

export const getAllNetworkMerklApr = async (signal?: AbortSignal) => {
const aprs = await Promise.all(supportedChainIdV4.map((chainId) => getMerklApr(chainId, signal)))
return aprs.reduce((acc, apr) => Object.assign(acc, apr), {})
const resp = await fetch(`https://api.angle.money/v2/merkl?AMMs=pancakeswapv3`, { signal })
if (resp.ok) {
const result = await resp.json()
const aprs = await Promise.all(supportedChainIdV4.map((chainId) => getMerklApr(result, chainId)))
return aprs.reduce((acc, apr) => Object.assign(acc, apr), {})
}
throw resp
}

const getV3PoolsCakeAprByChainId = async (pools: V3PoolInfo[], chainId: number, cakePrice: BigNumber) => {
Expand Down
13 changes: 9 additions & 4 deletions apps/web/src/state/farmsV4/state/poolApr/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ export const usePoolApr = (
const cakePrice = useCakePrice()
const getMerklApr = useCallback(() => {
if (Object.values(merklAprs).length === 0) {
return getAllNetworkMerklApr().then((aprs) => {
updateMerklApr(aprs)
return aprs[key!] ?? '0'
})
return getAllNetworkMerklApr()
.then((aprs) => {
updateMerklApr(aprs)
return aprs[key!] ?? '0'
})
.catch((error) => {
console.error('Error fetching Merkl APR:', error)
return '0'
})
}
return merklAprs[key!] ?? '0'
}, [key, merklAprs, updateMerklApr])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ interface LiquidityCardRowProps {
currency1?: Currency
pairText: string | React.ReactElement
feeAmount?: number
hasMerkl?: boolean
outOfRange?: boolean
tokenId?: bigint
tags: React.ReactElement
subtitle: string
Expand All @@ -41,12 +41,14 @@ export const LiquidityCardRow = ({
subtitle,
tokenId,
onSwitch,
hasMerkl,
outOfRange,
}: LiquidityCardRowProps) => {
const poolAddress = useMemo(
() =>
currency0 && currency1 && feeAmount ? Pool.getAddress(currency0.wrapped, currency1.wrapped, feeAmount) : null,
[currency0, currency1, feeAmount],
currency0 && currency1 && feeAmount && !outOfRange
? Pool.getAddress(currency0.wrapped, currency1.wrapped, feeAmount)
: undefined,
[currency0, currency1, feeAmount, outOfRange],
)

const content = (
Expand All @@ -65,7 +67,7 @@ export const LiquidityCardRow = ({
{new Percent(feeAmount, 1_000_000).toSignificant()}%
</Tag>
)}
{!hasMerkl && <MerklRewardsTag poolAddress={poolAddress} />}
<MerklRewardsTag poolAddress={poolAddress} />
<TagCell>{tags}</TagCell>
</Flex>
<Flex>
Expand Down
4 changes: 3 additions & 1 deletion apps/web/src/views/AddLiquidityV3/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export function AddLiquidityV3Modal({

const poolAddress = useMemo(
() =>
currency0 && currency1 && feeAmount ? Pool.getAddress(currency0.wrapped, currency1.wrapped, feeAmount) : null,
currency0 && currency1 && feeAmount
? Pool.getAddress(currency0.wrapped, currency1.wrapped, feeAmount)
: undefined,
[currency0, currency1, feeAmount],
)

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/views/AddLiquidityV3/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ export function AddLiquidityV3Layout({
() =>
baseCurrency?.wrapped && quoteCurrency?.wrapped && feeAmount
? Pool.getAddress(baseCurrency.wrapped, quoteCurrency.wrapped, feeAmount)
: null,
: undefined,
[baseCurrency?.wrapped, feeAmount, quoteCurrency?.wrapped],
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const FarmV3Card: React.FC<React.PropsWithChildren<FarmCardProps>> = ({ f
const { status: boostStatus } = useBoostStatus(farm.pid)
const merklUserLink = useMerklUserLink()
const merklLink = getMerklLink({ chainId, lpAddress })
const { merklApr } = useMerklInfo(merklLink ? lpAddress : null)
const { merklApr } = useMerklInfo(merklLink ? lpAddress : undefined)
const infoUrl = useMemo(() => {
return chainId ? `/info/v3${multiChainPaths[chainId]}/pairs/${lpAddress}?chain=${CHAIN_QUERY_NAME[chainId]}` : ''
}, [chainId, lpAddress])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ export const ActionPanelV3: FC<ActionPanelV3Props> = ({
)

const addLiquidityModal = useModalV2()
const { merklApr } = useMerklInfo(merklLink ? details.lpAddress : null)
const { merklApr } = useMerklInfo(merklLink ? details.lpAddress : undefined)

return (
<>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/views/Farms/components/FarmTable/Row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const Row: React.FunctionComponent<React.PropsWithChildren<RowPropsWithLoading>>
const columnNames = useMemo(() => tableSchema.map((column) => column.name), [tableSchema])
const merklUserLink = useMerklUserLink()

const { merklApr } = useMerklInfo(farm?.merklLink ? props.details.lpAddress : null)
const { merklApr } = useMerklInfo(farm?.merklLink ? props.details.lpAddress : undefined)

return (
<>
Expand Down
Loading

0 comments on commit 0e5d9af

Please sign in to comment.