Skip to content

Commit

Permalink
fix: Merkl tag on farms (#11103)
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 enhancing the `Merkl` integration by
updating components, adjusting filtering logic, and modifying the data
structure in the `merklPools` configuration. It also introduces a new
`MerklTag` component to the UI.

### Detailed summary
- Updated `MerklSection` to include `poolAddress` and `chainId`.
- Added a check for `lmPool` against `zeroAddress` in
`useLmPoolLiquidity`.
- Changed token filtering from `name` to `symbol` in various places.
- Modified `merklPools` JSON configuration to include new pools and
chain IDs.
- Introduced `MerklTag` in `PoolV2Page` for better UI representation.
- Enhanced the logic in `useMerkl` for fetching and filtering Merkl
data.

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

<!-- end pr-codex -->
  • Loading branch information
memoyil authored Jan 3, 2025
1 parent 04a64cb commit 2f0b8b5
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 61 deletions.
1 change: 0 additions & 1 deletion apps/web/src/components/Merkl/MerklSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ export function MerklSection({
}: {
poolAddress?: `0x${string}`
chainId?: ChainId
tokenId?: bigint
notEnoughLiquidity: boolean
outRange: boolean
disabled: boolean
Expand Down
32 changes: 16 additions & 16 deletions apps/web/src/config/constants/merklPools.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
[
{
"chainId": 56,
"address": "0xd5a79aB649E0a5F20e995026d034a0bF28B8aACa",
"link": "https://merkl.angle.money/bnb%20smart%20chain/pool/2/0xd5a79aB649E0a5F20e995026d034a0bF28B8aACa"
},
{
"chainId": 56,
"address": "0xeD000AB362Ef11E962658Fc04c1A7D667a647213",
"link": "https://merkl.angle.money/bnb%20smart%20chain/pool/1/0xeD000AB362Ef11E962658Fc04c1A7D667a647213"
},
{
"chainId": 42161,
"address": "0xE4BfcC208f3447cc5D2f5CCB40C52778d4bE2004",
"link": "https://merkl.angle.money/arbitrum/pool/2/0xE4BfcC208f3447cc5D2f5CCB40C52778d4bE2004"
"chainId": 1,
"address": "0x6db0f81Db2C3B2A85a802d511577d8522D0D8C14",
"link": "https://merkl.angle.money/ethereum/pool/2/0x6db0f81Db2C3B2A85a802d511577d8522D0D8C14"
},
{
"chainId": 1,
Expand All @@ -25,8 +15,18 @@
"link": "https://merkl.angle.money/bnb%20smart%20chain/pool/2/0x9d84f1d12FdC6c977BF451e70689F45107b79b77"
},
{
"chainId": 1,
"address": "0x6db0f81Db2C3B2A85a802d511577d8522D0D8C14",
"link": "https://merkl.angle.money/ethereum/pool/2/0x6db0f81Db2C3B2A85a802d511577d8522D0D8C14"
"chainId": 56,
"address": "0xeD000AB362Ef11E962658Fc04c1A7D667a647213",
"link": "https://merkl.angle.money/bnb%20smart%20chain/pool/1/0xeD000AB362Ef11E962658Fc04c1A7D667a647213"
},
{
"chainId": 56,
"address": "0xd5a79aB649E0a5F20e995026d034a0bF28B8aACa",
"link": "https://merkl.angle.money/bnb%20smart%20chain/pool/2/0xd5a79aB649E0a5F20e995026d034a0bF28B8aACa"
},
{
"chainId": 42161,
"address": "0xE4BfcC208f3447cc5D2f5CCB40C52778d4bE2004",
"link": "https://merkl.angle.money/arbitrum/pool/2/0xE4BfcC208f3447cc5D2f5CCB40C52778d4bE2004"
}
]
84 changes: 44 additions & 40 deletions apps/web/src/hooks/useMerkl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useWalletClient } from 'wagmi'
import { useMasterchefV3 } from 'hooks/useContract'
import { isAddressEqual } from 'utils'
import { useCurrentBlockTimestamp as useBlockTimestamp } from 'state/block/hooks'
import { supportedChainIdV4 } from '@pancakeswap/farms'

export const MERKL_API_V4 = 'https://api.merkl.xyz/v4'

Expand All @@ -41,12 +42,12 @@ export function useMerklInfo(poolAddress?: string): {
const lists = useAllLists()

const { data, isPending, refetch } = useQuery({
queryKey: [`fetchMerkl-${chainId}`],
queryKey: [`fetchMerklPools`],
queryFn: async () => {
if (!chainId) return undefined

const responsev4 = await fetch(
`${MERKL_API_V4}/opportunities?chainId=${chainId}&test=false&items=1000&action=POOL,HOLD`,
`${MERKL_API_V4}/opportunities?${supportedChainIdV4.join(
',',
)}&test=false&items=1000&action=POOL,HOLD&status=LIVE`,
)

if (!responsev4.ok) {
Expand All @@ -57,7 +58,7 @@ export function useMerklInfo(poolAddress?: string): {

const opportunities = merklDataV4?.filter(
(opportunity) =>
opportunity?.tokens?.[0]?.symbol?.toLowerCase().startsWith('Cake-LP') ||
opportunity?.tokens?.[0]?.symbol?.toLowerCase().startsWith('cake-lp') ||
opportunity?.protocol?.id?.toLowerCase().startsWith('pancakeswap'),
)

Expand All @@ -76,7 +77,7 @@ export function useMerklInfo(poolAddress?: string): {

return { pools }
},
enabled: Boolean(chainId && poolAddress),
enabled: Boolean(poolAddress),
staleTime: FAST_INTERVAL,
retryDelay: (attemptIndex) => Math.min(2000 * 2 ** attemptIndex, 30000),
})
Expand All @@ -96,15 +97,17 @@ export function useMerklInfo(poolAddress?: string): {

if (!merklDataV4) return undefined

return merklDataV4?.[0] || {}
return merklDataV4
},
enabled: Boolean(data && chainId && account && poolAddress),
staleTime: FAST_INTERVAL,
retryDelay: (attemptIndex) => Math.min(2000 * 2 ** attemptIndex, 30000),
})

return useMemo(() => {
if (!data || !currentTimestamp)
const pool = data?.pools?.filter((opportunity) => isAddressEqual(opportunity.identifier, poolAddress))?.[0]

if (!pool || !currentTimestamp)
return {
rewardsPerToken: [],
rewardTokenAddresses: [],
Expand All @@ -114,14 +117,10 @@ export function useMerklInfo(poolAddress?: string): {
isPending,
}

const { pools } = data

const hasLive = pools.some((pool) => {
const hasMeanAPR = pool.status === 'LIVE' && pool.apr > 0

if (!hasMeanAPR) return false

const hasLiveDistribution = Boolean(
const hasLive =
pool.status === 'LIVE' &&
pool.apr > 0 &&
Boolean(
pool.campaigns?.some((campaign) => {
const { startTimestamp, endTimestamp, whitelist, blacklist } = campaign
const startTimestampNumber = Number(startTimestamp)
Expand All @@ -144,14 +143,19 @@ export function useMerklInfo(poolAddress?: string): {
}),
)

return hasLiveDistribution
})
const rewardAddresses = (
pool.rewardsRecord?.breakdowns?.flatMap((breakdown) => breakdown.token.address) || []
).filter((address, index, allAddresses) => allAddresses.indexOf(address) === index)

const rewardsPerTokenObject = userData?.rewards?.filter((reward) => {
const { amount, claimed } = reward || {}
const unclaimed = BigInt(amount || 0) - BigInt(claimed || 0)
return unclaimed > 0
})
const chainUserData = userData?.filter((chainUserReward) => chainUserReward?.chain?.id === pool.chainId)?.[0]

const rewardsPerTokenObject = chainUserData?.rewards
?.filter((reward) => rewardAddresses.some((rewardAddress) => isAddressEqual(reward.token.address, rewardAddress)))
.filter((reward) => {
const { amount, claimed } = reward || {}
const unclaimed = BigInt(amount || 0) - BigInt(claimed || 0)
return unclaimed > 0
})

const transactionData = rewardsPerTokenObject?.reduce((acc, reward) => {
// eslint-disable-next-line no-param-reassign
Expand Down Expand Up @@ -183,31 +187,31 @@ export function useMerklInfo(poolAddress?: string): {

const { rewardsPerToken = [], rewardTokenAddresses = [], ...rest } = rewardResult

const rewardCurrencies = (rewardTokenAddresses as string[])
.reduce<TokenInfo[]>((result, address) => {
Object.values(lists).find((list) => {
const token: TokenInfo | undefined = list?.current?.tokens.find((t) => isAddressEqual(t.address, address))
const rewardCurrencies = rewardsPerToken.length
? rewardsPerToken
: (rewardTokenAddresses as string[])
.reduce<TokenInfo[]>((result, address) => {
Object.values(lists).find((list) => {
const token: TokenInfo | undefined = list?.current?.tokens.find((t) => isAddressEqual(t.address, address))

if (token) return result.push(token)
if (token) return result.push(token)

return false
})
return false
})

return result
}, [])
.map((info) => {
const t = new Token(chainId as number, info.address, info.decimals, info.symbol)
return result
}, [])
.map((info) => {
const t = new Token(chainId as number, info.address, info.decimals, info.symbol)

return CurrencyAmount.fromRawAmount(t, '0')
})
return CurrencyAmount.fromRawAmount(t, '0')
})

const merklApr = data?.pools?.find((pool) => isAddressEqual(pool.identifier, poolAddress))?.apr as
| number
| undefined
const merklApr = pool?.apr as number | undefined

return {
...rest,
rewardsPerToken: rewardsPerToken.length ? rewardsPerToken : rewardCurrencies,
rewardsPerToken: rewardCurrencies,
refreshData: refetch,
merklApr,
}
Expand Down
6 changes: 4 additions & 2 deletions apps/web/src/pages/v2/pair/[[...currency]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ import { useRouter } from 'next/router'
import { useAccount } from 'wagmi'
import { useAccountPositionDetailByPool } from 'state/farmsV4/state/accountPositions/hooks'
import { usePoolInfo } from 'state/farmsV4/state/extendPools/hooks'
import { useMemo } from 'react'
import React, { useMemo } from 'react'
import { formatFiatNumber } from '@pancakeswap/utils/formatFiatNumber'
import { useTotalPriceUSD } from 'hooks/useTotalPriceUSD'
import { useLPApr } from 'state/swap/useLPApr'
import { formatAmount } from 'utils/formatInfoNumbers'
import { MerklSection } from 'components/Merkl/MerklSection'
import { MerklTag } from 'components/Merkl/MerklTag'

export const BodyWrapper = styled(Card)`
border-radius: 24px;
Expand Down Expand Up @@ -147,6 +148,7 @@ export default function PoolV2Page() {
<Heading as="h2" ml="8px">
{pair?.token0?.symbol}-{pair?.token1?.symbol} LP
</Heading>
<MerklTag poolAddress={pair?.liquidityToken?.address} />
</Flex>
}
backTo="/liquidity/positions"
Expand Down Expand Up @@ -273,7 +275,7 @@ export default function PoolV2Page() {
{t('LP reward APR')}: {formatAmount(poolData.lpApr)}%
</Text>
)}
<Text color="textSubtle" ml="4px">
<Text color="textSubtle">
{t('Your share in pool')}: {poolTokenPercentage ? `${poolTokenPercentage.toFixed(8)}%` : '-'}
</Text>
</Flex>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/state/farmsV4/state/poolApr/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export const getAllNetworkMerklApr = async (signal?: AbortSignal) => {
const result = await resp.json()
const pancakeResult = result?.filter(
(opportunity) =>
opportunity?.tokens?.[0].name?.toLowerCase().startsWith('pancake') ||
opportunity?.tokens?.[0]?.symbol?.toLowerCase().startsWith('cake-lp') ||
opportunity?.protocol?.id?.toLowerCase().startsWith('pancakeswap'),
)
const aprs = await Promise.all(supportedChainIdV4.map((chainId) => getMerklApr(pancakeResult, chainId)))
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/views/Farms/hooks/useLmPoolLiquidity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const fetchLmPoolLiquidity = async (lpAddress: Address, chainId: number): Promis
abi: pancakeV3PoolABI,
functionName: 'lmPool',
})

if (isAddressEqual(lmPool, zeroAddress)) return 0n

const lmPoolLiquidity = await client.readContract({
address: lmPool,
abi: lmPoolAbi,
Expand Down
8 changes: 7 additions & 1 deletion scripts/updateMerkl/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ const fetchAllMerklConfig = async (): Promise<any[]> => {

const parseMerklConfig = (merklConfigResponse: any[]): MerklConfigPool[] => {
return merklConfigResponse
.sort((a, b) => {
if (a.chainId === b.chainId) {
return a.id - b.id
}
return a.chainId - b.chainId
})
.filter(
(opportunity) =>
(opportunity?.tokens?.[0].name?.toLowerCase().startsWith('pancake') ||
(opportunity?.tokens?.[0]?.symbol?.toLowerCase().startsWith('cake-lp') ||
opportunity?.protocol?.id?.toLowerCase().startsWith('pancakeswap')) &&
opportunity?.apr > 0,
)
Expand Down

0 comments on commit 2f0b8b5

Please sign in to comment.