mango-alerts-v3/src/index.ts

223 lines
5.6 KiB
TypeScript

import Koa from "koa"
import Router from "koa-router"
import mongo from "koa-mongo"
import bodyParser from "koa-bodyparser"
import cors from "@koa/cors"
import * as cron from "node-cron"
import { MongoClient, ObjectId } from "mongodb"
import {
Cluster,
Config,
GroupConfig,
MangoClient,
MangoAccount,
IDS,
} from "@blockworks-foundation/mango-client"
import { Commitment, Connection, PublicKey } from "@solana/web3.js"
import { UserError } from "./errors"
// import { sendLogsToDiscord } from "./logger"
import {
// initiateTelegramBot,
// generateTelegramCode,
validateMangoAccount,
// validatePhoneNumber,
validateEmail,
reduceMangoGroups,
sendAlert,
} from "./utils"
import config from "./environment"
const MESSAGE = "Your health ratio is at or below @ratio@% \n"
const app = new Koa()
const router = new Router()
// const rpcUrl = "https://mango.rpcpool.com/946ef7337da3f5b8d3e4a34e7f88"
const clientConfig = new Config(IDS)
const cluster = (process.env.CLUSTER || "mainnet") as Cluster
const groupName = process.env.GROUP || "mainnet.1"
const groupIds = clientConfig.getGroup(cluster, groupName)
if (!groupIds) {
throw new Error(`Group ${groupName} not found`)
}
const mangoProgramId = groupIds.mangoProgramId
// const mangoGroupKey = groupIds.publicKey
const connection = new Connection(
process.env.ENDPOINT_URL || clientConfig.cluster_urls[cluster],
"processed" as Commitment
)
const client = new MangoClient(connection, mangoProgramId)
let db: any
app.use(cors())
app.use(bodyParser())
app.use(
mongo(
{
uri: config.dbConnectionString,
},
{ useUnifiedTopology: true }
)
)
// initiateTelegramBot()
router.post("/alerts", async (ctx, next) => {
try {
const alert: any = ctx.request.body
// await validateMangoAccount(client, alert)
if (alert.alertProvider == "mail") {
validateEmail(alert.email)
ctx.body = { status: "success" }
} else {
throw new UserError("Invalid alert provider")
}
alert.open = true
alert.timestamp = Date.now()
ctx.db.collection("alerts").insertOne(alert)
} catch (e: any) {
let errorMessage = "Something went wrong"
if (e.name == "UserError") {
errorMessage = e.message
} else {
// sendLogsToDiscord(null, e)
}
ctx.throw(400, errorMessage)
}
await next()
})
router.post("/delete-alert", async (ctx, next) => {
try {
const id: any = new ObjectId(ctx.request.body.id)
if (id) {
ctx.body = { status: "success" }
}
ctx.db.collection("alerts").deleteOne({ _id: id })
} catch (e: any) {
let errorMessage = "Something went wrong"
if (e.name == "UserError") {
errorMessage = e.message
}
ctx.throw(400, errorMessage)
}
await next()
})
router.get("/alerts/:mangoAccountPk", async (ctx, next) => {
try {
const { mangoAccountPk } = ctx.params
if (!mangoAccountPk) {
throw new UserError("Missing margin account")
}
const alerts = await ctx.db
.collection("alerts")
.find(
{ mangoAccountPk },
{
projection: {
_id: 1,
health: 1,
alertProvider: 1,
open: 1,
timestamp: 1,
triggeredTimestamp: 1,
},
}
)
.toArray()
ctx.body = { alerts }
} catch (e: any) {
let errorMessage = "Something went wrong"
if (e.name == "UserError") {
errorMessage = e.message
} else {
// sendLogsToDiscord(null, e)
}
ctx.throw(400, errorMessage)
}
})
app.use(router.allowedMethods())
app.use(router.routes())
app.listen(config.port, () => {
const readyMessage = `> Server ready on http://localhost:${config.port}`
console.log(readyMessage)
// sendLogsToDiscord(readyMessage, null)
})
const handleAlert = async (alert: any, db: any) => {
try {
const mangoAccountPk = new PublicKey(alert.mangoAccountPk)
const mangoGroupPk = new PublicKey(alert.mangoGroupPk)
const mangoGroup = await client.getMangoGroup(mangoGroupPk)
const mangoCache = await mangoGroup.loadCache(connection)
const mangoAccount = await client.getMangoAccount(
mangoAccountPk,
mangoGroup.dexProgramId
)
const health = await mangoAccount.getHealthRatio(
mangoGroup,
mangoCache,
"Maint"
)
if (health.toNumber() <= parseFloat(alert.health)) {
let message = MESSAGE.replace("@ratio@", alert.health)
message += mangoAccount.name || alert.mangoAccountPk
message += "\nVisit https://trade.mango.markets/"
const alertSent = await sendAlert(alert, message)
if (alertSent) {
db.collection("alerts").updateOne(
{ _id: new ObjectId(alert._id) },
{ $set: { open: false, triggeredTimestamp: Date.now() } }
)
}
}
} catch (e) {
console.log(e)
// sendLogsToDiscord(null, e)
}
}
const runCron = async () => {
const uri = config.dbConnectionString
const mongoClient = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
const mongoConnection = await mongoClient.connect()
if (!db) db = mongoConnection.db("mango")
cron.schedule("* * * * *", async () => {
try {
const alerts: any[] = await db
.collection("alerts")
.find({ open: true })
.toArray()
console.log(alerts)
// const uniqueMangoGroupPks: string[] = [
// ...new Set(alerts.map((alert) => alert.mangoGroupPk)),
// ]
// const mangoGroups: any = await reduceMangoGroups(
// client,
// uniqueMangoGroupPks
// )
alerts.forEach(async (alert) => {
handleAlert(alert, db)
})
} catch (e) {
// sendLogsToDiscord(null, e)
}
})
}
runCron()