From d8489b7590e11fea4ab4d5bcfd56cd47811f37f1 Mon Sep 17 00:00:00 2001 From: Luke Steyn Date: Sun, 26 Nov 2023 23:36:45 +0100 Subject: [PATCH] Added oracle data to l2 responses --- src/dlob-subscriber/DLOBSubscriberIO.ts | 7 +-- src/index.ts | 61 ++++++++++++----------- src/utils/featureFlags.ts | 7 +++ src/utils/utils.ts | 64 +++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 31 deletions(-) create mode 100644 src/utils/featureFlags.ts diff --git a/src/dlob-subscriber/DLOBSubscriberIO.ts b/src/dlob-subscriber/DLOBSubscriberIO.ts index 9d2e200..039c738 100644 --- a/src/dlob-subscriber/DLOBSubscriberIO.ts +++ b/src/dlob-subscriber/DLOBSubscriberIO.ts @@ -11,9 +11,9 @@ import { groupL2, isVariant, } from '@drift-labs/sdk'; -import { getOracleForMarket, l2WithBNToStrings } from '../utils/utils'; -import { RedisClient } from '../utils/redisClient'; import { driftEnv } from '../publishers/dlobPublisher'; +import { RedisClient } from '../utils/redisClient'; +import { addOracletoResponse, l2WithBNToStrings } from '../utils/utils'; type wsMarketL2Args = { marketIndex: number; @@ -106,7 +106,8 @@ export class DLOBSubscriberIO extends DLOBSubscriber { l2Formatted['marketName'] = marketName?.toUpperCase(); l2Formatted['marketType'] = marketType?.toLowerCase(); l2Formatted['marketIndex'] = l2Args.marketIndex; - l2Formatted['oracle'] = getOracleForMarket( + addOracletoResponse( + l2Formatted, this.driftClient, l2Args.marketType, l2Args.marketIndex diff --git a/src/index.ts b/src/index.ts index 7925208..35275e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,48 +1,48 @@ import { program } from 'commander'; +import compression from 'compression'; +import cors from 'cors'; import express from 'express'; import rateLimit from 'express-rate-limit'; -import compression from 'compression'; import morgan from 'morgan'; -import cors from 'cors'; -import { Connection, Commitment, PublicKey, Keypair } from '@solana/web3.js'; +import { Commitment, Connection, Keypair, PublicKey } from '@solana/web3.js'; import { - getVariant, - DriftClient, - initialize, - DriftEnv, - UserMap, + BN, + BulkAccountLoader, + DLOBNode, DLOBOrder, DLOBOrders, DLOBOrdersCoder, - DLOBNode, - isVariant, - BN, - groupL2, - Wallet, - UserStatsMap, DLOBSubscriber, - BulkAccountLoader, + DriftClient, + DriftEnv, + UserMap, + UserStatsMap, + Wallet, + getVariant, + groupL2, + initialize, + isVariant, } from '@drift-labs/sdk'; import { logger, setLogLevel } from './utils/logger'; +import { Mutex } from 'async-mutex'; import * as http from 'http'; +import { handleHealthCheck } from './core/metrics'; +import { handleResponseTime } from './core/middleware'; import { - l2WithBNToStrings, - sleep, - getOracleForMarket, - normalizeBatchQueryParams, SubscriberLookup, + addOracletoResponse, errorHandler, getPhoenixSubscriber, getSerumSubscriber, + l2WithBNToStrings, + normalizeBatchQueryParams, + sleep, validateDlobQuery, } from './utils/utils'; -import { handleResponseTime } from './core/middleware'; -import { handleHealthCheck } from './core/metrics'; -import { Mutex } from 'async-mutex'; require('dotenv').config(); const driftEnv = (process.env.ENV || 'devnet') as DriftEnv; @@ -639,7 +639,8 @@ const main = async () => { groupL2(l2, groupingBN, finalDepth) ); if (`${includeOracle}`.toLowerCase() === 'true') { - l2Formatted['oracle'] = getOracleForMarket( + addOracletoResponse( + l2Formatted, driftClient, normedMarketType, normedMarketIndex @@ -652,7 +653,8 @@ const main = async () => { // make the BNs into strings const l2Formatted = l2WithBNToStrings(l2); if (`${includeOracle}`.toLowerCase() === 'true') { - l2Formatted['oracle'] = getOracleForMarket( + addOracletoResponse( + l2Formatted, driftClient, normedMarketType, normedMarketIndex @@ -760,7 +762,8 @@ const main = async () => { groupL2(l2, groupingBN, finalDepth) ); if (`${normedParam['includeOracle']}`.toLowerCase() === 'true') { - l2Formatted['oracle'] = getOracleForMarket( + addOracletoResponse( + l2Formatted, driftClient, normedMarketType, normedMarketIndex @@ -771,7 +774,8 @@ const main = async () => { // make the BNs into strings const l2Formatted = l2WithBNToStrings(l2); if (`${normedParam['includeOracle']}`.toLowerCase() === 'true') { - l2Formatted['oracle'] = getOracleForMarket( + addOracletoResponse( + l2Formatted, driftClient, normedMarketType, normedMarketIndex @@ -821,7 +825,8 @@ const main = async () => { } if (`${includeOracle}`.toLowerCase() === 'true') { - l3['oracle'] = getOracleForMarket( + addOracletoResponse( + l3, driftClient, normedMarketType, normedMarketIndex @@ -852,4 +857,4 @@ async function recursiveTryCatch(f: () => void) { recursiveTryCatch(() => main()); -export { sdkConfig, endpoint, wsEndpoint, driftEnv, commitHash, driftClient }; +export { commitHash, driftClient, driftEnv, endpoint, sdkConfig, wsEndpoint }; diff --git a/src/utils/featureFlags.ts b/src/utils/featureFlags.ts new file mode 100644 index 0000000..d5bfb19 --- /dev/null +++ b/src/utils/featureFlags.ts @@ -0,0 +1,7 @@ +// TODO : Is it worth adding proper infrastructure for feature flags? .. Would allow more powerful things like toggling them at runtime rather than being hardcoded +export const FEATURE_FLAGS = { + OLD_ORACLE_PRICE_IN_L2: true, // TODO : Remove this once we're confident that NEW_ORACLE_DATA_IN_L2 works .. delete corresponding code + NEW_ORACLE_DATA_IN_L2: true, +}; + +export default FEATURE_FLAGS; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 848de31..4105eba 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -2,7 +2,9 @@ import { DriftClient, DriftEnv, L2OrderBook, + L3OrderBook, MarketType, + OraclePriceData, PerpMarkets, PhoenixSubscriber, PublicKey, @@ -13,6 +15,7 @@ import { } from '@drift-labs/sdk'; import { logger } from './logger'; import { NextFunction, Request, Response } from 'express'; +import FEATURE_FLAGS from './featureFlags'; export const l2WithBNToStrings = (l2: L2OrderBook): any => { for (const key of Object.keys(l2)) { @@ -48,6 +51,67 @@ export const getOracleForMarket = ( } }; +type SerializableOraclePriceData = { + price: string; + slot: string; + confidence: string; + hasSufficientNumberOfDataPoints: boolean; + twap?: string; + twapConfidence?: string; +}; + +const getSerializableOraclePriceData = ( + oraclePriceData: OraclePriceData +): SerializableOraclePriceData => { + return { + price: oraclePriceData.price?.toString?.(), + slot: oraclePriceData.slot?.toString?.(), + confidence: oraclePriceData.confidence?.toString?.(), + hasSufficientNumberOfDataPoints: + oraclePriceData.hasSufficientNumberOfDataPoints, + twap: oraclePriceData.twap?.toString?.(), + twapConfidence: oraclePriceData.twapConfidence?.toString?.(), + }; +}; + +export const getOracleDataForMarket = ( + driftClient: DriftClient, + marketType: MarketType, + marketIndex: number +): SerializableOraclePriceData => { + if (isVariant(marketType, 'spot')) { + return getSerializableOraclePriceData( + driftClient.getOracleDataForSpotMarket(marketIndex) + ); + } else if (isVariant(marketType, 'perp')) { + return getSerializableOraclePriceData( + driftClient.getOracleDataForPerpMarket(marketIndex) + ); + } +}; + +export const addOracletoResponse = ( + response: L2OrderBook | L3OrderBook, + driftClient: DriftClient, + marketType: MarketType, + marketIndex: number +): void => { + if (FEATURE_FLAGS.OLD_ORACLE_PRICE_IN_L2) { + response['oracle'] = getOracleForMarket( + driftClient, + marketType, + marketIndex + ); + } + if (FEATURE_FLAGS.NEW_ORACLE_DATA_IN_L2) { + response['oracleData'] = getOracleDataForMarket( + driftClient, + marketType, + marketIndex + ); + } +}; + /** * Takes in a req.query like: `{ * marketName: 'SOL-PERP,BTC-PERP,ETH-PERP',