Skip to content

Commit

Permalink
feat: Update farm liquidity api (#10011)
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 adds an `EXPLORER_URL` and `EXPLORER_API_KEY` for fetching
liquidity data from an explorer and updates chain names accordingly.

### Detailed summary
- Added `EXPLORER_URL` and `EXPLORER_API_KEY` for fetching liquidity
data
- Updated chain names for mainnet chains in kebab case
- Modified functions to use the new explorer for data fetching

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

<!-- end pr-codex -->
  • Loading branch information
0xjojoex authored Jun 13, 2024
1 parent a5cc3ac commit 9a16780
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 91 deletions.
5 changes: 5 additions & 0 deletions .changeset/quick-turkeys-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@pancakeswap/chains': patch
---

add helper for chainname in mainnet
2 changes: 2 additions & 0 deletions apis/farms/.dev.vars
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ LINEA_NODE=https://rpc.linea.build
BASE_NODE=https://mainnet.base.org
OPBNB_NODE=https://opbnb-mainnet-rpc.bnbchain.org
OPBNB_TESTNET_NODE=https://opbnb-testnet-rpc.bnbchain.org
EXPLORER_URL=http://localhost:4123
EXPLORER_API_KEY=API_KEY
4 changes: 2 additions & 2 deletions apis/farms/src/bindings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ declare global {
const BASE_NODE: string
const OPBNB_NODE: string
const OPBNB_TESTNET_NODE: string
const NODE_REAL_SUBGRAPH_API_KEY: string
const THE_GRAPH_API_KEY: string
const EXPLORER_URL: string
const EXPLORER_API_KEY: string
}
4 changes: 2 additions & 2 deletions apis/farms/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ const requireCheck = [
BASE_NODE,
OPBNB_NODE,
OPBNB_TESTNET_NODE,
NODE_REAL_SUBGRAPH_API_KEY,
THE_GRAPH_API_KEY,
EXPLORER_URL,
EXPLORER_API_KEY,
]

const base = {
Expand Down
138 changes: 54 additions & 84 deletions apis/farms/src/v3.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
/* eslint-disable no-param-reassign, no-await-in-loop */
import { ChainId, getV3Subgraphs } from '@pancakeswap/chains'
import { FarmV3SupportedChainId, masterChefV3Addresses } from '@pancakeswap/farms'
import { ChainId, getMainnetChainNameInKebabCase } from '@pancakeswap/chains'
import { masterChefV3Addresses } from '@pancakeswap/farms'
import { ERC20Token } from '@pancakeswap/sdk'
import { CurrencyAmount } from '@pancakeswap/swap-sdk-core'
import { PositionMath } from '@pancakeswap/v3-sdk'
import { GraphQLClient, gql } from 'graphql-request'
import { Request } from 'itty-router'
import { error, json } from 'itty-router-extras'
import { Address } from 'viem'
Expand All @@ -17,8 +16,6 @@ export const V3_SUBGRAPH_CLIENTS_CHAIN_IDS = [
ChainId.ETHEREUM,
ChainId.GOERLI,
ChainId.BSC,
ChainId.BSC_TESTNET,
ChainId.ZKSYNC_TESTNET,
ChainId.POLYGON_ZKEVM,
ChainId.ZKSYNC,
ChainId.ARBITRUM_ONE,
Expand All @@ -29,16 +26,6 @@ export const V3_SUBGRAPH_CLIENTS_CHAIN_IDS = [

type SupportChainId = (typeof V3_SUBGRAPH_CLIENTS_CHAIN_IDS)[number]

const V3_SUBGRAPHS = getV3Subgraphs({
noderealApiKey: NODE_REAL_SUBGRAPH_API_KEY,
theGraphApiKey: THE_GRAPH_API_KEY,
})

export const V3_SUBGRAPH_CLIENTS = V3_SUBGRAPH_CLIENTS_CHAIN_IDS.reduce((acc, chainId) => {
acc[chainId] = new GraphQLClient(V3_SUBGRAPHS[chainId], { fetch })
return acc
}, {} as Record<Exclude<FarmV3SupportedChainId, ChainId.POLYGON_ZKEVM_TESTNET | ChainId.OPBNB_TESTNET>, GraphQLClient>)

const zChainId = z.enum(V3_SUBGRAPH_CLIENTS_CHAIN_IDS.map((chainId) => String(chainId)) as [string, ...string[]])

const zAddress = z.string().regex(/^0x[a-fA-F0-9]{40}$/)
Expand Down Expand Up @@ -250,7 +237,7 @@ const handler_ = async (req: Request, event: FetchEvent) => {

const resultTimeout = await Promise.race([
timeout(20),
fetchLiquidityFromSubgraph(chainId, address, masterChefV3Address, tick, sqrtPriceX96),
fetchLiquidityFromExplorer(chainId, address, masterChefV3Address, tick, sqrtPriceX96),
])

if (!resultTimeout) {
Expand Down Expand Up @@ -319,8 +306,8 @@ const handler_ = async (req: Request, event: FetchEvent) => {
}
}

async function fetchLiquidityFromSubgraph(
chainId: keyof typeof V3_SUBGRAPH_CLIENTS,
async function fetchLiquidityFromExplorer(
chainId: (typeof V3_SUBGRAPH_CLIENTS_CHAIN_IDS)[number],
address: string,
masterChefV3Address: string,
tick: number,
Expand All @@ -329,71 +316,54 @@ async function fetchLiquidityFromSubgraph(
const updatedAt = new Date().toISOString()
let allActivePositions: any[] = []

const poolTokens = await V3_SUBGRAPH_CLIENTS[chainId].request(
gql`
query pool($poolAddress: String!) {
pool(id: $poolAddress) {
token0 {
id
decimals
}
token1 {
id
decimals
}
}
}
`,
{
poolAddress: address,
const chainName = getMainnetChainNameInKebabCase(chainId)
const pool: {
token0: {
id: Address
decimals: number
}
token1: {
id: Address
decimals: number
}
} = await fetch(`${EXPLORER_URL}/cached/pools/v3/${chainName}/${address}`, {
headers: {
'x-api-key': EXPLORER_API_KEY,
'Content-Type': 'application/json',
},
)

// eslint-disable-next-line no-inner-declarations
async function fetchPositionByMasterChefId(posId_ = '0') {
const resp = await V3_SUBGRAPH_CLIENTS[chainId].request(
gql`
query tvl($poolAddress: String!, $owner: String!, $posId: String!, $currentTick: String!) {
positions(
where: { pool: $poolAddress, liquidity_gt: "0", owner: $owner, id_gt: $posId }
first: 1000
orderBy: id
tickLower_: { tickIdx_lte: currentTick }
tickUpper_: { tickIdx_gt: currentTick }
) {
liquidity
id
tickUpper {
tickIdx
}
tickLower {
tickIdx
}
}
}
`,
{
poolAddress: address,
owner: masterChefV3Address.toLowerCase(),
currentTick: tick.toString(),
posId: posId_,
},
)
}).then((res) => {
return res.json()
})

return resp.positions
if (!pool) {
throw new Error('Pool not found')
}

let posId = '0'
let hasNextPage = true
let cursor: string | undefined

// eslint-disable-next-line no-constant-condition
while (true) {
while (hasNextPage) {
// eslint-disable-next-line no-await-in-loop
const pos = await fetchPositionByMasterChefId(posId)
allActivePositions = [...allActivePositions, ...pos]
if (pos.length < 1000) {
break
}
posId = pos[pos.length - 1].id
const positions: any = await getPositionByMasterChefId(cursor)
allActivePositions = [...allActivePositions, ...positions.rows]
// eslint-disable-next-line prefer-destructuring
hasNextPage = positions.hasNextPage
// eslint-disable-next-line prefer-destructuring
cursor = positions.endCursor
}

async function getPositionByMasterChefId(after?: string) {
return fetch(
`${EXPLORER_URL}/cached/pools/positions/v3/${chainName}/${address}?owner=${masterChefV3Address.toLowerCase()}${
after ? `&after=${after}` : ''
}`,
{
headers: {
'x-api-key': EXPLORER_API_KEY,
'Content-Type': 'application/json',
},
},
).then((res) => res.json())
}

console.info('fetching farms active liquidity', {
Expand All @@ -416,20 +386,20 @@ async function fetchLiquidityFromSubgraph(

for (const position of allActivePositions.filter(
// double check that the position is within the current tick range
(p) => +p.tickLower.tickIdx <= currentTick && +p.tickUpper.tickIdx > currentTick,
(p) => +p.lowerTickIdx <= currentTick && +p.upperTickIdx > currentTick,
)) {
const token0 = PositionMath.getToken0Amount(
currentTick,
+position.tickLower.tickIdx,
+position.tickUpper.tickIdx,
+position.lowerTickIdx,
+position.upperTickIdx,
sqrtRatio,
BigInt(position.liquidity),
)

const token1 = PositionMath.getToken1Amount(
currentTick,
+position.tickLower.tickIdx,
+position.tickUpper.tickIdx,
+position.lowerTickIdx,
+position.upperTickIdx,
sqrtRatio,
BigInt(position.liquidity),
)
Expand All @@ -438,11 +408,11 @@ async function fetchLiquidityFromSubgraph(
}

const curr0 = CurrencyAmount.fromRawAmount(
new ERC20Token(+chainId, poolTokens.pool.token0.id, +poolTokens.pool.token0.decimals, '0'),
new ERC20Token(+chainId, pool.token0.id, +pool.token0.decimals, '0'),
totalToken0.toString(),
).toExact()
const curr1 = CurrencyAmount.fromRawAmount(
new ERC20Token(+chainId, poolTokens.pool.token1.id, +poolTokens.pool.token1.decimals, '1'),
new ERC20Token(+chainId, pool.token1.id, +pool.token1.decimals, '1'),
totalToken1.toString(),
).toExact()

Expand Down
4 changes: 2 additions & 2 deletions apis/farms/wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ crons = ["0 0 * * *", "*/1 * * * *"]
# - ARBITRUM_ONE_NODE
# - LINEA_NODE
# - BASE_NODE
# - NODE_REAL_SUBGRAPH_API_KEY
# - THE_GRAPH_API_KEY
# - EXPLORER_API_KEY
# - EXPLORER_API_KEY
# Run `echo <VALUE> | wrangler secret put <NAME>` for each of these
22 changes: 22 additions & 0 deletions packages/chains/src/chainNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,28 @@ export const chainNamesInKebabCase = {
[ChainId.BASE_SEPOLIA]: 'base-sepolia',
} as const

export const mainnetChainNamesInKebabCase = {
[ChainId.ETHEREUM]: 'ethereum',
[ChainId.GOERLI]: 'ethereum',
[ChainId.BSC]: 'bsc',
[ChainId.BSC_TESTNET]: 'bsc',
[ChainId.ARBITRUM_ONE]: 'arbitrum',
[ChainId.ARBITRUM_GOERLI]: 'arbitrum',
[ChainId.POLYGON_ZKEVM]: 'polygon-zkevm',
[ChainId.POLYGON_ZKEVM_TESTNET]: 'polygon-zkevm',
[ChainId.ZKSYNC]: 'zksync',
[ChainId.ZKSYNC_TESTNET]: 'zksync',
[ChainId.LINEA]: 'linea',
[ChainId.LINEA_TESTNET]: 'linea',
[ChainId.OPBNB]: 'opbnb',
[ChainId.OPBNB_TESTNET]: 'opbnb',
[ChainId.BASE]: 'base',
[ChainId.BASE_TESTNET]: 'base',
[ChainId.SEPOLIA]: 'ethereum',
[ChainId.ARBITRUM_SEPOLIA]: 'arbitrum',
[ChainId.BASE_SEPOLIA]: 'base',
} as const

export const chainNameToChainId = Object.entries(chainNames).reduce((acc, [chainId, chainName]) => {
return {
[chainName]: chainId as unknown as ChainId,
Expand Down
2 changes: 2 additions & 0 deletions packages/chains/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ test('exports', () => {
"testnetChainIds",
"chainNames",
"chainNamesInKebabCase",
"mainnetChainNamesInKebabCase",
"chainNameToChainId",
"defiLlamaChainNames",
"getChainName",
"getChainNameInKebabCase",
"getMainnetChainNameInKebabCase",
"getLlamaChainName",
"getChainIdByChainName",
"isTestnetChainId",
Expand Down
12 changes: 11 additions & 1 deletion packages/chains/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { ChainId, testnetChainIds } from './chainId'
import { chainNameToChainId, chainNames, chainNamesInKebabCase, defiLlamaChainNames } from './chainNames'
import {
chainNameToChainId,
chainNames,
chainNamesInKebabCase,
defiLlamaChainNames,
mainnetChainNamesInKebabCase,
} from './chainNames'

export function getChainName(chainId: ChainId) {
return chainNames[chainId]
Expand All @@ -9,6 +15,10 @@ export function getChainNameInKebabCase(chainId: ChainId) {
return chainNamesInKebabCase[chainId]
}

export function getMainnetChainNameInKebabCase(chainId: keyof typeof mainnetChainNamesInKebabCase) {
return mainnetChainNamesInKebabCase[chainId]
}

export function getLlamaChainName(chainId: ChainId) {
return defiLlamaChainNames[chainId]
}
Expand Down

0 comments on commit 9a16780

Please sign in to comment.