diff --git a/lib/fetchStats.ts b/lib/fetchStats.ts index aa94f90..ed9d1ea 100644 --- a/lib/fetchStats.ts +++ b/lib/fetchStats.ts @@ -1,43 +1,107 @@ import { Connection, PublicKey } from "@solana/web3.js" -import { IDS, MangoClient } from "@blockworks-foundation/mango-client" +import { + Config, + getMultipleAccounts, + GroupConfig, + I80F48, + IDS, + MangoClient, + PerpMarket, + PerpMarketLayout, +} from "@blockworks-foundation/mango-client" import PerpMarketStats from "../models/perp_market_stats" +import SpotMarketStats from "../models/spot_market_stats" -async function fetchAndPersistStats() { - // const clusterUrls = IDS.cluster_urls[cluster] - // if (!clusterUrls) return - // const client = new MangoClient() - // const connection = new Connection(IDS.cluster_urls[cluster], "singleGossip") - // const stats: any[][] = await Promise.all( - // MANGO_GROUPS.map(async (mangoGroupName) => { - // const assets = IDS[cluster].mango_groups?.[mangoGroupName]?.symbols - // const mangoGroupId = IDS[cluster].mango_groups?.[mangoGroupName]?.mango_group_pk - // const mangoGroupPk = new PublicKey(mangoGroupId) - // const mangoGroup = await client.getMangoGroup(connection, mangoGroupPk) - // const mangoGroupStats = Object.keys(assets).map((symbol, index) => { - // const totalDeposits = mangoGroup.getUiTotalDeposit(index) - // const totalBorrows = mangoGroup.getUiTotalBorrow(index) - // return { - // time: new Date(), - // symbol, - // totalDeposits, - // totalBorrows, - // depositInterest: mangoGroup.getDepositRate(index), - // borrowInterest: mangoGroup.getBorrowRate(index), - // utilization: totalDeposits > 0.0 ? totalBorrows / totalDeposits : 0.0, - // mangoGroup: mangoGroupName, - // } - // }) - // return mangoGroupStats - // }) - // ) - // const tableName = cluster === "devnet" ? DevnetStats : MainnetStats - // try { - // console.log("stats", stats.flat()) - // await tableName.bulkCreate(stats.flat()) - // console.log("stats inserted") - // } catch (err) { - // console.log("failed to insert stats", err) - // } +const CLUSTER_URLS = [ + { + name: "mainnet-beta", + url: "https://mango.rpcpool.com/", + websocket: "https://mango.rpcpool.com/", + }, + { + name: "devnet", + url: "https://mango.devnet.rpcpool.com", + websocket: "https://api.devnet.solana.com", + }, +] + +export type CLUSTER_TYPE = "devnet" | "mainnet-beta" + +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 fetchAndPersistStats(cluster: CLUSTER_TYPE) { + const clusterUrl = CLUSTER_URLS.find((c) => c.name === cluster) + if (!clusterUrl) return + const connection = new Connection(clusterUrl.url, "singleGossip") + const config = new Config(IDS) + + const groupConfig = config.getGroup(cluster, "mango_test_v3.8") + console.log("1") + + const client = new MangoClient(connection, groupConfig.mangoProgramId) + console.log("2") + + const mangoGroup = await client.getMangoGroup(groupConfig.publicKey) + console.log("3") + + await mangoGroup.loadRootBanks(connection) + console.log("4") + + const spotMarketStats = groupConfig.spotMarkets.map((spotMarket, index) => { + const totalDeposits = mangoGroup.getUiTotalDeposit(index) + const totalBorrows = mangoGroup.getUiTotalBorrow(index) + return { + time: new Date(), + name: groupConfig.spotMarkets[index].name, + publicKey: spotMarket.publicKey.toString(), + mangoGroup: groupConfig.name, + totalDeposits: totalDeposits.toNumber(), + totalBorrows: totalBorrows.toNumber(), + depositRate: mangoGroup.getDepositRate(index).toNumber(), + borrowRate: mangoGroup.getBorrowRate(index).toNumber(), + utilization: totalDeposits.gt(I80F48.fromNumber(0)) ? totalBorrows.div(totalDeposits).toNumber() : 0, + baseOraclePrice: 0, + } + }) + try { + await SpotMarketStats.bulkCreate(spotMarketStats) + console.log("spot stats inserted") + } catch (err) { + console.log("failed to insert spot stats", `${err}`) + } + + const perpMarkets = await loadPerpMarkets(connection, groupConfig) + console.log("6") + + const perpMarketStats = perpMarkets.map((perpMarket, index) => { + return { + time: new Date(), + name: groupConfig.perpMarkets[index].name, + publicKey: perpMarket.publicKey.toString(), + mangoGroup: groupConfig.name, + longFunding: perpMarket.longFunding.toNumber(), + shortFunding: perpMarket.shortFunding.toNumber(), + openInterest: perpMarket.openInterest.toNumber(), + baseOraclePrice: 0, + } + }) + + try { + await PerpMarketStats.bulkCreate(perpMarketStats) + console.log("perp stats inserted") + } catch (err) { + console.log("failed to insert spot stats", `${err}`) + } } export default fetchAndPersistStats diff --git a/models/spot_market_stats.ts b/models/spot_market_stats.ts index 10e3687..6f4f7b5 100644 --- a/models/spot_market_stats.ts +++ b/models/spot_market_stats.ts @@ -2,9 +2,9 @@ import { Sequelize, DataTypes } from "sequelize" import db from "./index" const PerpMarketStats = db.sequelize.define( - "perp_market_stats", + "spot_market_stats", { - symbol: { type: DataTypes.STRING, allowNull: false }, + name: { type: DataTypes.STRING, allowNull: false }, totalDeposits: { type: DataTypes.DECIMAL, get() { @@ -33,6 +33,13 @@ const PerpMarketStats = db.sequelize.define( return value === null ? null : parseFloat(value) }, }, + baseOraclePrice: { + type: DataTypes.DECIMAL, + get() { + const value = this.getDataValue("baseOraclePrice") + return value === null ? null : parseFloat(value) + }, + }, mangoGroup: DataTypes.STRING, publicKey: DataTypes.STRING, utilization: DataTypes.DECIMAL, diff --git a/package.json b/package.json index 30e9376..010ae2f 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "start": "node dist/src/index.js", "lint": "eslint src/**/*.ts", "format": "eslint src/**/*.ts --fix", - "db-setup": "sequelize db:drop && sequelize db:create tsdb && sequelize db:migrate", + "db-setup": "sequelize db:drop && sequelize db:create mango_stats_v3_dev && sequelize db:migrate", "db-migrate": "sequelize db:migrate" }, "devDependencies": { @@ -25,7 +25,7 @@ "typescript": "^4.1.5" }, "dependencies": { - "@blockworks-foundation/mango-client": "^3.0.1", + "@blockworks-foundation/mango-client": "https://github.com/blockworks-foundation/mango-client-v3", "@solana/web3.js": "^1.18.0", "cors": "^2.8.5", "dotenv": "^8.2.0", diff --git a/src/schedule.ts b/src/schedule.ts index 55f4563..54f81d3 100644 --- a/src/schedule.ts +++ b/src/schedule.ts @@ -1,3 +1,5 @@ -import fetchAndPersistStats from '../lib/fetchStats'; +import fetchAndPersistStats, { CLUSTER_TYPE } from "../lib/fetchStats" -fetchAndPersistStats(); +const cluster = (process.env.CLUSTER || "devnet") as CLUSTER_TYPE + +fetchAndPersistStats(cluster) diff --git a/yarn.lock b/yarn.lock index 67117d6..1b663b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39,12 +39,11 @@ dependencies: regenerator-runtime "^0.13.4" -"@blockworks-foundation/mango-client@^3.0.1": +"@blockworks-foundation/mango-client@https://github.com/blockworks-foundation/mango-client-v3": version "3.0.1" - resolved "https://registry.yarnpkg.com/@blockworks-foundation/mango-client/-/mango-client-3.0.1.tgz#10e54c6fe2ef196a4092af82e49c7cd8b25cccf6" - integrity sha512-iiMD6HoR7cOkrHBvorBP1DCNarusG/QO8JWYEkTUqfF4de7Gw/rWg1oyaQ+9gnDoMyGe61W9kTsTp4COEEKOFw== + resolved "https://github.com/blockworks-foundation/mango-client-v3#06d0d92b5d15d319ef7db5320b8427851c7c4a03" dependencies: - "@project-serum/serum" "^0.13.38" + "@project-serum/serum" "^0.13.45" "@project-serum/sol-wallet-adapter" "^0.2.0" "@solana/spl-token" "^0.1.6" "@solana/web3.js" "^1.18.0" @@ -120,7 +119,7 @@ bn.js "^5.1.2" buffer-layout "^1.2.0" -"@project-serum/serum@^0.13.38": +"@project-serum/serum@^0.13.45": version "0.13.54" resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.54.tgz#f7a05ade7a044b87f693928c3a0cd01e82fe0484" integrity sha512-kYNAFcLbqhYocQ+cl3s+QZSroVOIrPiKo81fvdyrE/r2U2qshmULzMVDNpXqdrd3qdJUIZGroXMi1DHTaG2qjA== @@ -585,9 +584,9 @@ borsh@^0.4.0: bs58 "^4.0.0" text-encoding-utf-8 "^1.0.2" -"borsh@git+https://github.com/defactojob/borsh-js.git#field-mapper": +"borsh@https://github.com/defactojob/borsh-js#field-mapper": version "0.3.1" - resolved "git+https://github.com/defactojob/borsh-js.git#33a0d24af281112c0a48efb3fa503f3212443de9" + resolved "https://github.com/defactojob/borsh-js#33a0d24af281112c0a48efb3fa503f3212443de9" dependencies: "@types/bn.js" "^4.11.5" bn.js "^5.0.0"