129 lines
4.2 KiB
TypeScript
129 lines
4.2 KiB
TypeScript
import fs from 'fs'
|
|
import { request, gql } from 'graphql-request'
|
|
import BigNumber from 'bignumber.js'
|
|
import { ChainId } from '@pancakeswap-libs/sdk'
|
|
import chunk from 'lodash/chunk'
|
|
import { sub, getUnixTime } from 'date-fns'
|
|
import farmsConfig from '../src/config/constants/farms'
|
|
|
|
const BLOCK_SUBGRAPH_ENDPOINT = 'https://api.thegraph.com/subgraphs/name/pancakeswap/blocks'
|
|
const STREAMING_FAST_ENDPOINT = 'https://bsc.streamingfast.io/subgraphs/name/pancakeswap/exchange-v2'
|
|
|
|
interface BlockResponse {
|
|
blocks: {
|
|
number: string
|
|
}[]
|
|
}
|
|
|
|
interface SingleFarmResponse {
|
|
id: string
|
|
reserveUSD: string
|
|
volumeUSD: string
|
|
}
|
|
|
|
interface FarmsResponse {
|
|
farmsAtLatestBlock: SingleFarmResponse[]
|
|
farmsOneWeekAgo: SingleFarmResponse[]
|
|
}
|
|
|
|
interface AprMap {
|
|
[key: string]: BigNumber
|
|
}
|
|
|
|
const getWeekAgoTimestamp = () => {
|
|
const weekAgo = sub(new Date(), { weeks: 1 })
|
|
return getUnixTime(weekAgo)
|
|
}
|
|
|
|
const LP_HOLDERS_FEE = 0.0017
|
|
const WEEKS_IN_A_YEAR = 52.1429
|
|
|
|
const getBlockAtTimestamp = async (timestamp: number) => {
|
|
try {
|
|
const { blocks } = await request<BlockResponse>(
|
|
BLOCK_SUBGRAPH_ENDPOINT,
|
|
`query getBlock($timestampGreater: Int!, $timestampLess: Int!) {
|
|
blocks(first: 1, where: { timestamp_gt: $timestampGreater, timestamp_lt: $timestampLess }) {
|
|
number
|
|
}
|
|
}`,
|
|
{ timestampGreater: timestamp, timestampLess: timestamp + 600 },
|
|
)
|
|
return parseInt(blocks[0].number, 10)
|
|
} catch (error) {
|
|
throw new Error(`Failed to fetch block number for ${timestamp}\n${error}`)
|
|
}
|
|
}
|
|
|
|
const getAprsForFarmGroup = async (addresses: string[], blockWeekAgo: number): Promise<AprMap> => {
|
|
try {
|
|
const { farmsAtLatestBlock, farmsOneWeekAgo } = await request<FarmsResponse>(
|
|
STREAMING_FAST_ENDPOINT,
|
|
gql`
|
|
query farmsBulk($addresses: [String]!, $blockWeekAgo: Int!) {
|
|
farmsAtLatestBlock: pairs(first: 30, where: { id_in: $addresses }) {
|
|
id
|
|
volumeUSD
|
|
reserveUSD
|
|
}
|
|
farmsOneWeekAgo: pairs(first: 30, where: { id_in: $addresses }, block: { number: $blockWeekAgo }) {
|
|
id
|
|
volumeUSD
|
|
reserveUSD
|
|
}
|
|
}
|
|
`,
|
|
{ addresses, blockWeekAgo },
|
|
)
|
|
const aprs: AprMap = farmsAtLatestBlock.reduce((aprMap, farm) => {
|
|
const farmWeekAgo = farmsOneWeekAgo.find((oldFarm) => oldFarm.id === farm.id)
|
|
// In case farm is too new to estimate LP APR (i.e. not returned in farmsOneWeekAgo query) - return 0
|
|
let lpApr = new BigNumber(0)
|
|
if (farmWeekAgo) {
|
|
const volume7d = new BigNumber(farm.volumeUSD).minus(new BigNumber(farmWeekAgo.volumeUSD))
|
|
const lpFees7d = volume7d.times(LP_HOLDERS_FEE)
|
|
const lpFeesInAYear = lpFees7d.times(WEEKS_IN_A_YEAR)
|
|
// Some untracked pairs like KUN-QSD will report 0 volume
|
|
if (lpFeesInAYear.gt(0)) {
|
|
const liquidity = new BigNumber(farm.reserveUSD)
|
|
lpApr = lpFeesInAYear.times(100).dividedBy(liquidity)
|
|
}
|
|
}
|
|
return {
|
|
...aprMap,
|
|
[farm.id]: lpApr.decimalPlaces(2).toNumber(),
|
|
}
|
|
}, {})
|
|
return aprs
|
|
} catch (error) {
|
|
throw new Error(`Failed to fetch LP APR data: ${error}`)
|
|
}
|
|
}
|
|
|
|
const fetchAndUpdateLPsAPR = async () => {
|
|
// pids before 250 are inactive farms from v1 and failed v2 migration
|
|
const lowerCaseAddresses = farmsConfig
|
|
.filter((farm) => farm.pid > 250)
|
|
.map((farm) => farm.lpAddresses[ChainId.MAINNET].toLowerCase())
|
|
console.info(`Fetching farm data for ${lowerCaseAddresses.length} addresses`)
|
|
// Split it into chunks of 30 addresses to avoid gateway timeout
|
|
const addressesInGroups = chunk(lowerCaseAddresses, 30)
|
|
const weekAgoTimestamp = getWeekAgoTimestamp()
|
|
const blockWeekAgo = await getBlockAtTimestamp(weekAgoTimestamp)
|
|
|
|
let allAprs: AprMap = {}
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
for (const groupOfAddresses of addressesInGroups) {
|
|
// eslint-disable-next-line no-await-in-loop
|
|
const aprs = await getAprsForFarmGroup(groupOfAddresses, blockWeekAgo)
|
|
allAprs = { ...allAprs, ...aprs }
|
|
}
|
|
|
|
fs.writeFile(`src/config/constants/lpAprs.json`, JSON.stringify(allAprs, null, 2), (err) => {
|
|
if (err) throw err
|
|
console.info(` ✅ - lpAprs.json has been updated!`)
|
|
})
|
|
}
|
|
|
|
fetchAndUpdateLPsAPR()
|