Auction Params Endpoint (#442)

* move auction endpoint changes to fresh branch

* final cleanup and add testing for util functions

* fix tests

* fix quote amount conversion
This commit is contained in:
lowkeynicc
2025-07-14 19:13:43 -04:00
committed by GitHub
parent dd524de371
commit 7483704d09
7 changed files with 2553 additions and 35 deletions

View File

@@ -18,6 +18,10 @@ import {
isVariant,
OrderSubscriber,
DelistedMarketSetting,
BigNum,
PRICE_PRECISION_EXP,
MarketTypeStr,
AssetType,
} from '@drift-labs/sdk';
import { RedisClient, RedisClientPrefix } from '@drift/common/clients';
@@ -41,11 +45,19 @@ import {
getAccountFromId,
getRawAccountFromId,
selectMostRecentBySlot,
createMarketBasedAuctionParams,
parseBoolean,
parseNumber,
mapToMarketOrderParams,
formatAuctionParamsForResponse,
fetchL2FromRedis,
} from './utils/utils';
import FEATURE_FLAGS from './utils/featureFlags';
import { getDLOBProviderFromOrderSubscriber } from './dlobProvider';
import { setGlobalDispatcher, Agent } from 'undici';
import { HermesClient } from '@pythnetwork/hermes-client';
import { COMMON_UI_UTILS } from '@drift/common';
import { AuctionParamArgs } from './utils/types';
setGlobalDispatcher(
new Agent({
@@ -546,11 +558,12 @@ const main = async (): Promise<void> => {
const adjustedDepth = depth ?? '100';
let l2Formatted: any;
const redisL2 = await fetchFromRedis(
`last_update_orderbook_${
isSpot ? 'spot' : 'perp'
}_${normedMarketIndex}${includeIndicativeStr ? '_indicative' : ''}`,
selectMostRecentBySlot
const redisL2 = await fetchL2FromRedis(
fetchFromRedis,
selectMostRecentBySlot,
normedMarketType,
normedMarketIndex,
includeIndicativeStr
);
const depthToUse = Math.min(parseInt(adjustedDepth as string) ?? 1, 100);
let cacheMiss = true;
@@ -660,13 +673,12 @@ const main = async (): Promise<void> => {
const adjustedDepth = normedParam['depth'] ?? '100';
let l2Formatted: any;
const redisL2 = await fetchFromRedis(
`last_update_orderbook_${
isSpot ? 'spot' : 'perp'
}_${normedMarketIndex}${
normedIncludeIndicative ? '_indicative' : ''
}`,
selectMostRecentBySlot
const redisL2 = await fetchL2FromRedis(
fetchFromRedis,
selectMostRecentBySlot,
normedMarketType,
normedMarketIndex,
normedIncludeIndicative
);
const depth = Math.min(parseInt(adjustedDepth as string) ?? 1, 100);
let cacheMiss = true;
@@ -903,6 +915,139 @@ const main = async (): Promise<void> => {
}
});
app.get('/auctionParams', async (req, res, next) => {
try {
const {
marketIndex,
marketType,
direction,
amount,
assetType,
reduceOnly,
allowInfSlippage,
slippageTolerance,
isOracleOrder,
auctionDuration,
auctionStartPriceOffset,
auctionEndPriceOffset,
auctionStartPriceOffsetFrom,
auctionEndPriceOffsetFrom,
additionalEndPriceBuffer,
userOrderId,
} = req.query;
// Validate required parameters
if (!marketIndex || !marketType || !direction || !amount || !assetType) {
res
.status(400)
.send(
'Bad Request: marketIndex, marketType, direction, amount, and assetType are required'
);
return;
}
// Parse and validate values
const parsedMarketIndex = parseInt(marketIndex as string);
if (isNaN(parsedMarketIndex)) {
res.status(400).send('Bad Request: marketIndex must be a valid number');
return;
}
if (direction !== 'long' && direction !== 'short') {
res
.status(400)
.send('Bad Request: direction must be either "long" or "short"');
return;
}
if (assetType !== 'base' && assetType !== 'quote') {
res
.status(400)
.send('Bad Request: assetType must be either "base" or "quote"');
return;
}
// Build auction params object
const auctionParamsInput: AuctionParamArgs = {
marketIndex: parsedMarketIndex,
marketType: marketType as MarketTypeStr,
direction: direction as 'long' | 'short',
amount: amount as string,
assetType: assetType as AssetType,
};
// Add optional parameters if provided
const optionalParams = {
reduceOnly: parseBoolean(reduceOnly as string),
allowInfSlippage: parseBoolean(allowInfSlippage as string),
slippageTolerance:
slippageTolerance === 'dynamic'
? undefined
: parseNumber(slippageTolerance as string), // Convert "dynamic" to undefined for dynamic calculation
isOracleOrder: parseBoolean(isOracleOrder as string),
auctionDuration: parseNumber(auctionDuration as string),
auctionStartPriceOffset:
auctionStartPriceOffset === 'marketBased'
? 'marketBased'
: parseNumber(auctionStartPriceOffset as string),
auctionEndPriceOffset: parseNumber(auctionEndPriceOffset as string),
auctionStartPriceOffsetFrom:
auctionStartPriceOffsetFrom === 'marketBased'
? 'marketBased'
: (auctionStartPriceOffsetFrom as any),
auctionEndPriceOffsetFrom: auctionEndPriceOffsetFrom as any,
additionalEndPriceBuffer: additionalEndPriceBuffer as string,
userOrderId: parseNumber(userOrderId as string),
};
// Only add non-undefined values
Object.entries(optionalParams).forEach(([key, value]) => {
if (value !== undefined) {
auctionParamsInput[key] = value;
}
});
const inputParams = createMarketBasedAuctionParams(auctionParamsInput);
const result = await mapToMarketOrderParams(
inputParams,
driftClient,
fetchFromRedis,
selectMostRecentBySlot
);
if (!result.success) {
res.status(400).json({
error: result.error,
});
return;
}
const auctionParams = COMMON_UI_UTILS.deriveMarketOrderParams(
result.data.marketOrderParams
);
const response = {
data: {
params: formatAuctionParamsForResponse(auctionParams),
entryPrice: result.data.estimatedPrices.entryPrice.toString(),
bestPrice: result.data.estimatedPrices.bestPrice.toString(),
worstPrice: result.data.estimatedPrices.worstPrice.toString(),
priceImpact: BigNum.from(
result.data.estimatedPrices.priceImpact,
PRICE_PRECISION_EXP
).toNum(),
slippageTolerance: result.data.marketOrderParams.slippageTolerance,
baseFilled: result.data.estimatedPrices.baseFilled,
},
};
res.status(200).json(response);
} catch (err) {
next(err);
}
});
server.listen(serverPort, () => {
logger.info(`DLOB server listening on port http://localhost:${serverPort}`);
});
@@ -920,4 +1065,4 @@ async function recursiveTryCatch(f: () => Promise<void>) {
recursiveTryCatch(() => main());
export { commitHash, driftClient, driftEnv, endpoint, sdkConfig, wsEndpoint };
export { commitHash, driftClient, driftEnv, endpoint, sdkConfig, wsEndpoint };