@drift-labs/sdk-browser
Version:
SDK for Drift Protocol
255 lines (254 loc) • 11.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.calculateOrderBaseAssetAmount = exports.isSignedMsgOrder = exports.isTakingOrder = exports.isRestingLimitOrder = exports.isTriggered = exports.mustBeTriggered = exports.isLimitOrder = exports.isMarketOrder = exports.isOrderExpired = exports.calculateBaseAssetAmountToFillUpToLimitPrice = exports.calculateBaseAssetAmountForAmmToFulfill = exports.isFillableByVAMM = exports.hasAuctionPrice = exports.hasLimitPrice = exports.applyProtectedMakerParams = exports.getLimitPrice = exports.standardizePrice = exports.standardizeBaseAssetAmount = exports.isOrderReduceOnly = exports.isOrderRiskIncreasingInSameDirection = exports.isOrderRiskIncreasing = void 0;
const types_1 = require("../types");
const numericConstants_1 = require("../constants/numericConstants");
const anchor_1 = require("@coral-xyz/anchor");
const auction_1 = require("./auction");
const amm_1 = require("./amm");
function isOrderRiskIncreasing(user, order) {
if (!(0, types_1.isVariant)(order.status, 'open')) {
return false;
}
const position = user.getPerpPosition(order.marketIndex) ||
user.getEmptyPosition(order.marketIndex);
// if no position exists, it's risk increasing
if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
return true;
}
// if position is long and order is long
if (position.baseAssetAmount.gt(numericConstants_1.ZERO) && (0, types_1.isVariant)(order.direction, 'long')) {
return true;
}
// if position is short and order is short
if (position.baseAssetAmount.lt(numericConstants_1.ZERO) &&
(0, types_1.isVariant)(order.direction, 'short')) {
return true;
}
const baseAssetAmountToFill = order.baseAssetAmount.sub(order.baseAssetAmountFilled);
// if order will flip position
if (baseAssetAmountToFill.gt(position.baseAssetAmount.abs().mul(numericConstants_1.TWO))) {
return true;
}
return false;
}
exports.isOrderRiskIncreasing = isOrderRiskIncreasing;
function isOrderRiskIncreasingInSameDirection(user, order) {
if (!(0, types_1.isVariant)(order.status, 'open')) {
return false;
}
const position = user.getPerpPosition(order.marketIndex) ||
user.getEmptyPosition(order.marketIndex);
// if no position exists, it's risk increasing
if (position.baseAssetAmount.eq(numericConstants_1.ZERO)) {
return true;
}
// if position is long and order is long
if (position.baseAssetAmount.gt(numericConstants_1.ZERO) && (0, types_1.isVariant)(order.direction, 'long')) {
return true;
}
// if position is short and order is short
if (position.baseAssetAmount.lt(numericConstants_1.ZERO) &&
(0, types_1.isVariant)(order.direction, 'short')) {
return true;
}
return false;
}
exports.isOrderRiskIncreasingInSameDirection = isOrderRiskIncreasingInSameDirection;
function isOrderReduceOnly(user, order) {
if (!(0, types_1.isVariant)(order.status, 'open')) {
return false;
}
const position = user.getPerpPosition(order.marketIndex) ||
user.getEmptyPosition(order.marketIndex);
// if position is long and order is long
if (position.baseAssetAmount.gte(numericConstants_1.ZERO) &&
(0, types_1.isVariant)(order.direction, 'long')) {
return false;
}
// if position is short and order is short
if (position.baseAssetAmount.lte(numericConstants_1.ZERO) &&
(0, types_1.isVariant)(order.direction, 'short')) {
return false;
}
return true;
}
exports.isOrderReduceOnly = isOrderReduceOnly;
function standardizeBaseAssetAmount(baseAssetAmount, stepSize) {
const remainder = baseAssetAmount.mod(stepSize);
return baseAssetAmount.sub(remainder);
}
exports.standardizeBaseAssetAmount = standardizeBaseAssetAmount;
function standardizePrice(price, tickSize, direction) {
if (price.eq(numericConstants_1.ZERO)) {
console.log('price is zero');
return price;
}
const remainder = price.mod(tickSize);
if (remainder.eq(numericConstants_1.ZERO)) {
return price;
}
if ((0, types_1.isVariant)(direction, 'long')) {
return price.sub(remainder);
}
else {
return price.add(tickSize).sub(remainder);
}
}
exports.standardizePrice = standardizePrice;
function getLimitPrice(order, oraclePriceData, slot, fallbackPrice, protectedMakerParams) {
let limitPrice;
if (hasAuctionPrice(order, slot)) {
limitPrice = (0, auction_1.getAuctionPrice)(order, slot, oraclePriceData.price);
}
else if (order.oraclePriceOffset !== 0) {
limitPrice = anchor_1.BN.max(oraclePriceData.price.add(new anchor_1.BN(order.oraclePriceOffset)), numericConstants_1.ONE);
}
else if (order.price.eq(numericConstants_1.ZERO)) {
limitPrice = fallbackPrice;
}
else {
limitPrice = order.price;
}
if (protectedMakerParams) {
limitPrice = applyProtectedMakerParams(limitPrice, order.direction, protectedMakerParams);
}
return limitPrice;
}
exports.getLimitPrice = getLimitPrice;
function applyProtectedMakerParams(limitPrice, direction, protectedMakerParams) {
const minOffset = protectedMakerParams.tickSize.muln(8);
let limitPriceBpsDivisor;
if (protectedMakerParams.limitPriceDivisor > 0) {
limitPriceBpsDivisor = 10000 / protectedMakerParams.limitPriceDivisor;
}
else {
limitPriceBpsDivisor = 1000;
}
const limitPriceOffset = anchor_1.BN.min(anchor_1.BN.max(anchor_1.BN.max(limitPrice.divn(limitPriceBpsDivisor), minOffset), protectedMakerParams.dynamicOffset), limitPrice.divn(20));
if ((0, types_1.isVariant)(direction, 'long')) {
return anchor_1.BN.max(limitPrice.sub(limitPriceOffset), protectedMakerParams.tickSize);
}
else {
return limitPrice.add(limitPriceOffset);
}
}
exports.applyProtectedMakerParams = applyProtectedMakerParams;
function hasLimitPrice(order, slot) {
return (order.price.gt(numericConstants_1.ZERO) ||
order.oraclePriceOffset != 0 ||
!(0, auction_1.isAuctionComplete)(order, slot));
}
exports.hasLimitPrice = hasLimitPrice;
function hasAuctionPrice(order, slot) {
return (!(0, auction_1.isAuctionComplete)(order, slot) &&
(!order.auctionStartPrice.eq(numericConstants_1.ZERO) || !order.auctionEndPrice.eq(numericConstants_1.ZERO)));
}
exports.hasAuctionPrice = hasAuctionPrice;
function isFillableByVAMM(order, market, mmOraclePriceData, slot, ts, minAuctionDuration) {
return (((0, auction_1.isFallbackAvailableLiquiditySource)(order, minAuctionDuration, slot) &&
calculateBaseAssetAmountForAmmToFulfill(order, market, mmOraclePriceData, slot).gt(numericConstants_1.ZERO)) ||
isOrderExpired(order, ts));
}
exports.isFillableByVAMM = isFillableByVAMM;
function calculateBaseAssetAmountForAmmToFulfill(order, market, mmOraclePriceData, slot) {
if (mustBeTriggered(order) && !isTriggered(order)) {
return numericConstants_1.ZERO;
}
const limitPrice = getLimitPrice(order, mmOraclePriceData, slot);
let baseAssetAmount;
const updatedAMM = (0, amm_1.calculateUpdatedAMM)(market.amm, mmOraclePriceData);
if (limitPrice !== undefined) {
baseAssetAmount = calculateBaseAssetAmountToFillUpToLimitPrice(order, updatedAMM, limitPrice, mmOraclePriceData);
}
else {
baseAssetAmount = order.baseAssetAmount.sub(order.baseAssetAmountFilled);
}
const maxBaseAssetAmount = (0, amm_1.calculateMaxBaseAssetAmountFillable)(updatedAMM, order.direction);
return anchor_1.BN.min(maxBaseAssetAmount, baseAssetAmount);
}
exports.calculateBaseAssetAmountForAmmToFulfill = calculateBaseAssetAmountForAmmToFulfill;
function calculateBaseAssetAmountToFillUpToLimitPrice(order, amm, limitPrice, mmOraclePriceData) {
const adjustedLimitPrice = (0, types_1.isVariant)(order.direction, 'long')
? limitPrice.sub(amm.orderTickSize)
: limitPrice.add(amm.orderTickSize);
const [maxAmountToTrade, direction] = (0, amm_1.calculateMaxBaseAssetAmountToTrade)(amm, adjustedLimitPrice, order.direction, mmOraclePriceData);
const baseAssetAmount = standardizeBaseAssetAmount(maxAmountToTrade, amm.orderStepSize);
// Check that directions are the same
const sameDirection = isSameDirection(direction, order.direction);
if (!sameDirection) {
return numericConstants_1.ZERO;
}
const baseAssetAmountUnfilled = order.baseAssetAmount.sub(order.baseAssetAmountFilled);
return baseAssetAmount.gt(baseAssetAmountUnfilled)
? baseAssetAmountUnfilled
: baseAssetAmount;
}
exports.calculateBaseAssetAmountToFillUpToLimitPrice = calculateBaseAssetAmountToFillUpToLimitPrice;
function isSameDirection(firstDirection, secondDirection) {
return (((0, types_1.isVariant)(firstDirection, 'long') && (0, types_1.isVariant)(secondDirection, 'long')) ||
((0, types_1.isVariant)(firstDirection, 'short') && (0, types_1.isVariant)(secondDirection, 'short')));
}
function isOrderExpired(order, ts, enforceBuffer = false, bufferSeconds = 15) {
if (mustBeTriggered(order) ||
!(0, types_1.isVariant)(order.status, 'open') ||
order.maxTs.eq(numericConstants_1.ZERO)) {
return false;
}
let maxTs;
if (enforceBuffer && isLimitOrder(order)) {
maxTs = order.maxTs.addn(bufferSeconds);
}
else {
maxTs = order.maxTs;
}
return new anchor_1.BN(ts).gt(maxTs);
}
exports.isOrderExpired = isOrderExpired;
function isMarketOrder(order) {
return (0, types_1.isOneOfVariant)(order.orderType, ['market', 'triggerMarket', 'oracle']);
}
exports.isMarketOrder = isMarketOrder;
function isLimitOrder(order) {
return (0, types_1.isOneOfVariant)(order.orderType, ['limit', 'triggerLimit']);
}
exports.isLimitOrder = isLimitOrder;
function mustBeTriggered(order) {
return (0, types_1.isOneOfVariant)(order.orderType, ['triggerMarket', 'triggerLimit']);
}
exports.mustBeTriggered = mustBeTriggered;
function isTriggered(order) {
return (0, types_1.isOneOfVariant)(order.triggerCondition, [
'triggeredAbove',
'triggeredBelow',
]);
}
exports.isTriggered = isTriggered;
function isRestingLimitOrder(order, slot) {
if (!isLimitOrder(order)) {
return false;
}
return order.postOnly || (0, auction_1.isAuctionComplete)(order, slot);
}
exports.isRestingLimitOrder = isRestingLimitOrder;
function isTakingOrder(order, slot) {
return isMarketOrder(order) || !isRestingLimitOrder(order, slot);
}
exports.isTakingOrder = isTakingOrder;
const FLAG_IS_SIGNED_MSG = 0x01;
function isSignedMsgOrder(order) {
return (order.bitFlags & FLAG_IS_SIGNED_MSG) !== 0;
}
exports.isSignedMsgOrder = isSignedMsgOrder;
function calculateOrderBaseAssetAmount(order, existingBaseAssetAmount) {
if (!order.reduceOnly) {
return order.baseAssetAmount;
}
if ((0, types_1.isVariant)(order.direction, 'long')) {
return anchor_1.BN.min(anchor_1.BN.min(existingBaseAssetAmount, numericConstants_1.ZERO).abs(), order.baseAssetAmount);
}
else {
return anchor_1.BN.min(anchor_1.BN.max(existingBaseAssetAmount, numericConstants_1.ZERO), order.baseAssetAmount);
}
}
exports.calculateOrderBaseAssetAmount = calculateOrderBaseAssetAmount;