mango-stats-v3/src/collectStats.ts

133 lines
4.8 KiB
TypeScript

import { Connection } from "@solana/web3.js"
import {
Config,
Cluster,
getMultipleAccounts,
GroupConfig,
I80F48,
IDS,
MangoClient,
PerpMarket,
PerpMarketLayout,
getMarketIndexBySymbol,
QUOTE_INDEX,
} from "@blockworks-foundation/mango-client"
import PerpMarketStats from "../models/perp_market_stats"
import SpotMarketStats from "../models/spot_market_stats"
import notify from "./notify"
const CLUSTER_URLS = [
{
name: "mainnet",
url: process.env.ENDPOINT || "https://mango.rpcpool.com/",
websocket: "https://mango.rpcpool.com/",
},
{
name: "devnet",
url: "https://mango.devnet.rpcpool.com",
websocket: "https://api.devnet.solana.com",
},
]
const cluster = (process.env.CLUSTER || "devnet") as Cluster
const groupName = (process.env.GROUP || "devnet.1") as string
const SECONDS = 1000
const PERP_INTERVAL = 30 * SECONDS
const SPOT_INTERVAL = 30 * SECONDS
const clusterUrl = CLUSTER_URLS.find((c) => c.name === cluster)
const connection = new Connection(clusterUrl.url, "singleGossip")
const config = new Config(IDS)
const groupConfig = config.getGroup(cluster, groupName)
const client = new MangoClient(connection, groupConfig.mangoProgramId)
const loadPerpMarkets = async (connection, groupConfig: GroupConfig) => {
const perpMarketPks = groupConfig.perpMarkets.map((p) => p.publicKey)
const allMarketAccountInfos = await getMultipleAccounts(connection, perpMarketPks)
return groupConfig.perpMarkets.map((config, i) => {
const decoded = PerpMarketLayout.decode(allMarketAccountInfos[i].accountInfo.data)
return new PerpMarket(config.publicKey, config.baseDecimals, config.quoteDecimals, decoded)
})
}
async function fetchSpotStats() {
try {
const mangoGroup = await client.getMangoGroup(groupConfig.publicKey)
const mangoCache = await mangoGroup.loadCache(connection)
// TODO: reduce calls in loadRootBanks
await mangoGroup.loadRootBanks(connection)
const spotMarketStats = groupConfig.tokens.map((token) => {
let tokenIndex = getMarketIndexBySymbol(groupConfig, token.symbol)
tokenIndex = tokenIndex === -1 ? QUOTE_INDEX : tokenIndex
const totalDeposits = mangoGroup.getUiTotalDeposit(tokenIndex)
const totalBorrows = mangoGroup.getUiTotalBorrow(tokenIndex)
return {
time: new Date(),
name: token.symbol,
mangoGroup: groupConfig.name,
totalDeposits: totalDeposits.toNumber(),
totalBorrows: totalBorrows.toNumber(),
depositRate: mangoGroup.getDepositRate(tokenIndex).toNumber(),
borrowRate: mangoGroup.getBorrowRate(tokenIndex).toNumber(),
depositIndex: mangoGroup.rootBankAccounts[tokenIndex].depositIndex.toNumber(),
borrowIndex: mangoGroup.rootBankAccounts[tokenIndex].borrowIndex.toNumber(),
utilization: totalDeposits.gt(I80F48.fromNumber(0)) ? totalBorrows.div(totalDeposits).toNumber() : 0,
baseOraclePrice: mangoGroup.getPrice(tokenIndex, mangoCache).toNumber(),
}
})
await SpotMarketStats.bulkCreate(spotMarketStats)
console.log("spot stats inserted")
} catch (err) {
notify(`mango-stats-v3 perp stats ERROR: ${err}`)
console.log("failed to insert spot stats", `${err}`)
} finally {
setTimeout(fetchSpotStats, SPOT_INTERVAL)
}
}
async function fetchPerpStats() {
try {
const mangoGroup = await client.getMangoGroup(groupConfig.publicKey)
const mangoCache = await mangoGroup.loadCache(connection)
const perpMarkets = await loadPerpMarkets(connection, groupConfig)
const perpMarketStats = perpMarkets.map((perpMarket, index) => {
return {
time: new Date(),
name: groupConfig.perpMarkets[index].name,
mangoGroup: groupConfig.name,
longFunding: perpMarket.longFunding.toNumber(),
shortFunding: perpMarket.shortFunding.toNumber(),
openInterest: perpMarket.openInterest.toNumber(),
baseOraclePrice: mangoGroup.getPrice(groupConfig.perpMarkets[index].marketIndex, mangoCache).toNumber(),
feesAccrued: perpMarket.feesAccrued.toNumber(),
mngoLeft: perpMarket.liquidityMiningInfo.mngoLeft.toNumber(),
mngoPerPeriod: perpMarket.liquidityMiningInfo.mngoPerPeriod.toNumber(),
rate: perpMarket.liquidityMiningInfo.rate.toNumber(),
maxDepthBps: perpMarket.liquidityMiningInfo.maxDepthBps.toNumber(),
periodStart: new Date(perpMarket.liquidityMiningInfo.periodStart.toNumber() * 1000).toISOString(),
}
})
await PerpMarketStats.bulkCreate(perpMarketStats)
console.log("perp stats inserted")
} catch (err) {
console.log("failed to insert perp stats", `${err}`)
notify(`mango-stats-v3 spot stats ERROR: ${err}`)
} finally {
setTimeout(fetchPerpStats, PERP_INTERVAL)
}
}
async function main() {
notify("mango-stats-v3 restarting")
fetchPerpStats()
fetchSpotStats()
}
main()