diff --git a/src/index.ts b/src/index.ts index 794e7af..039d7b2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,6 +46,7 @@ import { sleep, validateDlobQuery, } from './utils/utils'; +import FEATURE_FLAGS from './utils/featureFlags'; import { DLOBProvider, getDLOBProviderFromOrderSubscriber, @@ -174,7 +175,7 @@ const main = async () => { const clearingHousePublicKey = new PublicKey(sdkConfig.DRIFT_PROGRAM_ID); const connection = new Connection(endpoint, { - wsEndpoint: wsEndpoint, + wsEndpoint, commitment: stateCommitment, }); @@ -274,7 +275,8 @@ const main = async () => { `dlob provider initialized in ${Date.now() - initDLOBProviderStart} ms` ); logger.info(`dlob provider size ${dlobProvider.size()}`); - if (useWebsocket) { + + if (useWebsocket && !FEATURE_FLAGS.DISABLE_GPA_REFRESH) { const recursiveFetch = (delay = WS_FALLBACK_FETCH_INTERVAL) => { setTimeout(() => { dlobProvider.fetch().then(() => { @@ -322,218 +324,234 @@ const main = async () => { app.get('/startup', handleStartup); app.get('/', handleHealthCheck(slotSource)); - app.get('/orders/json/raw', async (_req, res, next) => { - try { - // object with userAccount key and orders object serialized - const orders: Array = []; - const oracles: Array = []; - const slot = slotSource.getSlot(); + if (FEATURE_FLAGS.ENABLE_ORDERS_ENDPOINTS) { + app.get('/orders/json/raw', async (_req, res, next) => { + try { + // object with userAccount key and orders object serialized + const orders: Array = []; + const oracles: Array = []; + const slot = slotSource.getSlot(); - for (const market of driftClient.getPerpMarketAccounts()) { - const oracle = driftClient.getOracleDataForPerpMarket( - market.marketIndex - ); - oracles.push({ - marketIndex: market.marketIndex, - ...oracle, - }); - } - - for (const { userAccount, publicKey } of dlobProvider.getUserAccounts()) { - for (const order of userAccount.orders) { - if (isVariant(order.status, 'init')) { - continue; - } - - orders.push({ - user: publicKey.toBase58(), - order: order, + for (const market of driftClient.getPerpMarketAccounts()) { + const oracle = driftClient.getOracleDataForPerpMarket( + market.marketIndex + ); + oracles.push({ + marketIndex: market.marketIndex, + ...oracle, }); } - } - // respond with orders - res.writeHead(200); - res.end( - JSON.stringify({ - slot, - oracles, - orders, - }) - ); - } catch (e) { - next(e); - } - }); - - app.get('/orders/json', async (_req, res, next) => { - try { - // object with userAccount key and orders object serialized - const slot = slotSource.getSlot(); - const orders: Array = []; - const oracles: Array = []; - for (const market of driftClient.getPerpMarketAccounts()) { - const oracle = driftClient.getOracleDataForPerpMarket( - market.marketIndex - ); - const oracleHuman = { - marketIndex: market.marketIndex, - price: oracle.price.toString(), - slot: oracle.slot.toString(), - confidence: oracle.confidence.toString(), - hasSufficientNumberOfDataPoints: - oracle.hasSufficientNumberOfDataPoints, - }; - if (oracle.twap) { - oracleHuman['twap'] = oracle.twap.toString(); - } - if (oracle.twapConfidence) { - oracleHuman['twapConfidence'] = oracle.twapConfidence.toString(); - } - oracles.push(oracleHuman); - } - for (const { userAccount, publicKey } of dlobProvider.getUserAccounts()) { - for (const order of userAccount.orders) { - if (isVariant(order.status, 'init')) { - continue; - } - - const orderHuman = { - status: getVariant(order.status), - orderType: getVariant(order.orderType), - marketType: getVariant(order.marketType), - slot: order.slot.toString(), - orderId: order.orderId, - userOrderId: order.userOrderId, - marketIndex: order.marketIndex, - price: order.price.toString(), - baseAssetAmount: order.baseAssetAmount.toString(), - baseAssetAmountFilled: order.baseAssetAmountFilled.toString(), - quoteAssetAmountFilled: order.quoteAssetAmountFilled.toString(), - direction: getVariant(order.direction), - reduceOnly: order.reduceOnly, - triggerPrice: order.triggerPrice.toString(), - triggerCondition: getVariant(order.triggerCondition), - existingPositionDirection: getVariant( - order.existingPositionDirection - ), - postOnly: order.postOnly, - immediateOrCancel: order.immediateOrCancel, - oraclePriceOffset: order.oraclePriceOffset, - auctionDuration: order.auctionDuration, - auctionStartPrice: order.auctionStartPrice.toString(), - auctionEndPrice: order.auctionEndPrice.toString(), - maxTs: order.maxTs.toString(), - }; - if (order.quoteAssetAmount) { - orderHuman['quoteAssetAmount'] = order.quoteAssetAmount.toString(); - } - - orders.push({ - user: publicKey.toBase58(), - order: orderHuman, - }); - } - } - - // respond with orders - res.writeHead(200); - res.end( - JSON.stringify({ - slot, - oracles, - orders, - }) - ); - } catch (err) { - next(err); - } - }); - - app.get('/orders/idl', async (_req, res, next) => { - try { - const dlobOrders: DLOBOrders = []; - - for (const { userAccount, publicKey } of dlobProvider.getUserAccounts()) { - for (const order of userAccount.orders) { - if (isVariant(order.status, 'init')) { - continue; - } - - dlobOrders.push({ - user: publicKey, - order, - } as DLOBOrder); - } - } - - res.writeHead(200); - res.end(dlobCoder.encode(dlobOrders)); - } catch (err) { - next(err); - } - }); - - app.get('/orders/idlWithSlot', async (req, res, next) => { - try { - const { marketName, marketIndex, marketType } = req.query; - const { normedMarketType, normedMarketIndex, error } = validateDlobQuery( - driftClient, - driftEnv, - marketType as string, - marketIndex as string, - marketName as string - ); - const useFilter = - marketName !== undefined || - marketIndex !== undefined || - marketType !== undefined; - - if (useFilter) { - if ( - error || - normedMarketType === undefined || - normedMarketIndex === undefined - ) { - res.status(400).send(error); - return; - } - } - - const dlobOrders: DLOBOrders = []; - - for (const { userAccount, publicKey } of dlobProvider.getUserAccounts()) { - for (const order of userAccount.orders) { - if (isVariant(order.status, 'init')) { - continue; - } - - if (useFilter) { - if ( - getVariant(order.marketType) !== getVariant(normedMarketType) || - order.marketIndex !== normedMarketIndex - ) { + for (const { + userAccount, + publicKey, + } of dlobProvider.getUserAccounts()) { + for (const order of userAccount.orders) { + if (isVariant(order.status, 'init')) { continue; } + + orders.push({ + user: publicKey.toBase58(), + order: order, + }); } - - dlobOrders.push({ - user: publicKey, - order, - } as DLOBOrder); } - } - res.end( - JSON.stringify({ - slot: slotSource.getSlot(), - data: dlobCoder.encode(dlobOrders).toString('base64'), - }) - ); - } catch (err) { - next(err); - } - }); + // respond with orders + res.writeHead(200); + res.end( + JSON.stringify({ + slot, + oracles, + orders, + }) + ); + } catch (e) { + next(e); + } + }); + + app.get('/orders/json', async (_req, res, next) => { + try { + // object with userAccount key and orders object serialized + const slot = slotSource.getSlot(); + const orders: Array = []; + const oracles: Array = []; + for (const market of driftClient.getPerpMarketAccounts()) { + const oracle = driftClient.getOracleDataForPerpMarket( + market.marketIndex + ); + const oracleHuman = { + marketIndex: market.marketIndex, + price: oracle.price.toString(), + slot: oracle.slot.toString(), + confidence: oracle.confidence.toString(), + hasSufficientNumberOfDataPoints: + oracle.hasSufficientNumberOfDataPoints, + }; + if (oracle.twap) { + oracleHuman['twap'] = oracle.twap.toString(); + } + if (oracle.twapConfidence) { + oracleHuman['twapConfidence'] = oracle.twapConfidence.toString(); + } + oracles.push(oracleHuman); + } + for (const { + userAccount, + publicKey, + } of dlobProvider.getUserAccounts()) { + for (const order of userAccount.orders) { + if (isVariant(order.status, 'init')) { + continue; + } + + const orderHuman = { + status: getVariant(order.status), + orderType: getVariant(order.orderType), + marketType: getVariant(order.marketType), + slot: order.slot.toString(), + orderId: order.orderId, + userOrderId: order.userOrderId, + marketIndex: order.marketIndex, + price: order.price.toString(), + baseAssetAmount: order.baseAssetAmount.toString(), + baseAssetAmountFilled: order.baseAssetAmountFilled.toString(), + quoteAssetAmountFilled: order.quoteAssetAmountFilled.toString(), + direction: getVariant(order.direction), + reduceOnly: order.reduceOnly, + triggerPrice: order.triggerPrice.toString(), + triggerCondition: getVariant(order.triggerCondition), + existingPositionDirection: getVariant( + order.existingPositionDirection + ), + postOnly: order.postOnly, + immediateOrCancel: order.immediateOrCancel, + oraclePriceOffset: order.oraclePriceOffset, + auctionDuration: order.auctionDuration, + auctionStartPrice: order.auctionStartPrice.toString(), + auctionEndPrice: order.auctionEndPrice.toString(), + maxTs: order.maxTs.toString(), + }; + if (order.quoteAssetAmount) { + orderHuman['quoteAssetAmount'] = + order.quoteAssetAmount.toString(); + } + + orders.push({ + user: publicKey.toBase58(), + order: orderHuman, + }); + } + } + + // respond with orders + res.writeHead(200); + res.end( + JSON.stringify({ + slot, + oracles, + orders, + }) + ); + } catch (err) { + next(err); + } + }); + + app.get('/orders/idl', async (_req, res, next) => { + try { + const dlobOrders: DLOBOrders = []; + + for (const { + userAccount, + publicKey, + } of dlobProvider.getUserAccounts()) { + for (const order of userAccount.orders) { + if (isVariant(order.status, 'init')) { + continue; + } + + dlobOrders.push({ + user: publicKey, + order, + } as DLOBOrder); + } + } + + res.writeHead(200); + res.end(dlobCoder.encode(dlobOrders)); + } catch (err) { + next(err); + } + }); + + app.get('/orders/idlWithSlot', async (req, res, next) => { + try { + const { marketName, marketIndex, marketType } = req.query; + const { normedMarketType, normedMarketIndex, error } = + validateDlobQuery( + driftClient, + driftEnv, + marketType as string, + marketIndex as string, + marketName as string + ); + const useFilter = + marketName !== undefined || + marketIndex !== undefined || + marketType !== undefined; + + if (useFilter) { + if ( + error || + normedMarketType === undefined || + normedMarketIndex === undefined + ) { + res.status(400).send(error); + return; + } + } + + const dlobOrders: DLOBOrders = []; + + for (const { + userAccount, + publicKey, + } of dlobProvider.getUserAccounts()) { + for (const order of userAccount.orders) { + if (isVariant(order.status, 'init')) { + continue; + } + + if (useFilter) { + if ( + getVariant(order.marketType) !== getVariant(normedMarketType) || + order.marketIndex !== normedMarketIndex + ) { + continue; + } + } + + dlobOrders.push({ + user: publicKey, + order, + } as DLOBOrder); + } + } + + res.end( + JSON.stringify({ + slot: slotSource.getSlot(), + data: dlobCoder.encode(dlobOrders).toString('base64'), + }) + ); + } catch (err) { + next(err); + } + }); + } app.get('/topMakers', async (req, res, next) => { try { diff --git a/src/utils/featureFlags.ts b/src/utils/featureFlags.ts index d5bfb19..2694bd7 100644 --- a/src/utils/featureFlags.ts +++ b/src/utils/featureFlags.ts @@ -1,7 +1,17 @@ -// 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 + // TODO : Remove this once we're confident that NEW_ORACLE_DATA_IN_L2 works .. delete corresponding code + OLD_ORACLE_PRICE_IN_L2: true, NEW_ORACLE_DATA_IN_L2: true, + + // enables old orders endpoint, disabled by default since the response is too big now + ENABLE_ORDERS_ENDPOINTS: process.env.ENABLE_ORDERS_ENDPOINTS + ? process.env.ENABLE_ORDERS_ENDPOINTS.toLowerCase() + : false, + + // disables periodically refreshing userAccounts via gPA + DISABLE_GPA_REFRESH: process.env.DISABLE_GPA_REFRESH + ? process.env.DISABLE_GPA_REFRESH.toLowerCase() + : false, }; export default FEATURE_FLAGS;