diff --git a/src/index.ts b/src/index.ts index af414e9..ebc3f33 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,7 +27,8 @@ import { SpotMarketConfig, PhoenixSubscriber, SerumSubscriber, -} from '@drift-labs/sdk'; + DLOBNode, +} from "@drift-labs/sdk"; import { Mutex } from 'async-mutex'; @@ -768,17 +769,107 @@ const main = async () => { }; }; - app.get('/l2', handleResponseTime, async (req, res, next) => { - try { - const { - marketName, - marketIndex, - marketType, - depth, - includeVamm, - includePhoenix, - includeSerum, - } = req.query; + app.get("/topMakers", handleResponseTime, async (req, res, next) => { + try { + const { + marketName, + marketIndex, + marketType, + side, // bid or ask + limit, // number of unique makers to return, if undefined will return all + } = req.query; + + const { normedMarketType, normedMarketIndex, error } = validateDlobQuery( + marketType as string, + marketIndex as string, + marketName as string + ); + if (error) { + res.status(400).send(error); + return; + } + + if (side !== "bid" && side !== "ask") { + res.status(400).send("Bad Request: side must be either bid or ask"); + return; + } + const normedSide = (side as string).toLowerCase(); + const oracle = driftClient.getOracleDataForPerpMarket(normedMarketIndex); + + let normedLimit = undefined; + if (limit) { + if (isNaN(parseInt(limit as string))) { + res + .status(400) + .send("Bad Request: limit must be a number if supplied"); + return; + } + normedLimit = parseInt(limit as string); + } + + const topMakers: Set = new Set(); + let foundMakers = 0; + const findMakers = (sideGenerator: Generator) => { + for (const side of sideGenerator) { + if (limit && foundMakers >= normedLimit) { + break; + } + if (side.userAccount) { + const maker = side.userAccount.toBase58(); + if (topMakers.has(maker)) { + continue; + } else { + topMakers.add(side.userAccount.toBase58()); + foundMakers++; + } + } else { + continue; + } + } + }; + + if (normedSide === "bid") { + findMakers( + dlobSubscriber + .getDLOB() + .getRestingLimitBids( + normedMarketIndex, + slotSubscriber.getSlot(), + normedMarketType, + oracle + ) + ); + } else { + findMakers( + dlobSubscriber + .getDLOB() + .getRestingLimitAsks( + normedMarketIndex, + slotSubscriber.getSlot(), + normedMarketType, + oracle + ) + ); + } + + res.writeHead(200); + res.end(JSON.stringify([...topMakers])); + } catch (err) { + next(err); + } + }); + + app.get("/l2", handleResponseTime, async (req, res, next) => { + try { + const { + marketName, + marketIndex, + marketType, + depth, + includeVamm, + includePhoenix, + includeSerum, + } = req.query; const { normedMarketType, normedMarketIndex, error } = validateDlobQuery( marketType as string, @@ -790,7 +881,7 @@ const main = async () => { return; } - const isSpot = marketType === 'spot'; + const isSpot = getVariant(normedMarketType); const l2 = await dlobSubscriber.getL2({ marketIndex: normedMarketIndex,