diff --git a/sdk/src/dlob/DLOB.ts b/sdk/src/dlob/DLOB.ts index 92fb572c2..5a3367912 100644 --- a/sdk/src/dlob/DLOB.ts +++ b/sdk/src/dlob/DLOB.ts @@ -32,7 +32,7 @@ import { StateAccount, } from '../types'; import { isUserProtectedMaker } from '../math/userStatus'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { ProtectMakerParamsMap } from './types'; import { SlotSubscriber } from '../slot/SlotSubscriber'; import { UserMap } from '../userMap/userMap'; @@ -446,16 +446,20 @@ export class DLOB { return undefined; } - public findNodesToFill( + public findNodesToFill( marketIndex: number, fallbackBid: BN | undefined, fallbackAsk: BN | undefined, slot: number, ts: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, stateAccount: StateAccount, - marketAccount: PerpMarketAccount | SpotMarketAccount + marketAccount: T extends { spot: unknown } + ? SpotMarketAccount + : PerpMarketAccount ): NodeToFill[] { if (fillPaused(stateAccount, marketAccount)) { return []; @@ -572,11 +576,13 @@ export class DLOB { return Array.from(mergedNodesToFill.values()); } - public findRestingLimitOrderNodesToFill( + public findRestingLimitOrderNodesToFill( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, isAmmPaused: boolean, minAuctionDuration: number, makerRebateNumerator: number, @@ -656,11 +662,13 @@ export class DLOB { return nodesToFill; } - public findTakingNodesToFill( + public findTakingNodesToFill( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, isAmmPaused: boolean, minAuctionDuration: number, fallbackAsk: BN | undefined, @@ -782,17 +790,21 @@ export class DLOB { return nodesToFill; } - public findTakingNodesCrossingMakerNodes( + public findTakingNodesCrossingMakerNodes( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, takerNodeGenerator: Generator, makerNodeGeneratorFn: ( marketIndex: number, slot: number, marketType: MarketType, - oraclePriceData: OraclePriceData + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData ) => Generator, doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean ): NodeToFill[] { @@ -878,10 +890,12 @@ export class DLOB { return nodesToFill; } - public findNodesCrossingFallbackLiquidity( - marketType: MarketType, + public findNodesCrossingFallbackLiquidity( + marketType: T, slot: number, - oraclePriceData: OraclePriceData, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, nodeGenerator: Generator, doesCross: (nodePrice: BN | undefined) => boolean, minAuctionDuration: number @@ -997,11 +1011,13 @@ export class DLOB { return nodesToFill; } - *getTakingBids( + *getTakingBids( marketIndex: number, - marketType: MarketType, + marketType: T, slot: number, - oraclePriceData: OraclePriceData, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { const marketTypeStr = getVariant(marketType) as MarketTypeStr; @@ -1032,11 +1048,13 @@ export class DLOB { ); } - *getTakingAsks( + *getTakingAsks( marketIndex: number, - marketType: MarketType, + marketType: T, slot: number, - oraclePriceData: OraclePriceData, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { const marketTypeStr = getVariant(marketType) as MarketTypeStr; @@ -1138,11 +1156,13 @@ export class DLOB { } } - *getRestingLimitAsks( + *getRestingLimitAsks( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { if (isVariant(marketType, 'spot') && !oraclePriceData) { @@ -1180,11 +1200,13 @@ export class DLOB { ); } - *getRestingLimitBids( + *getRestingLimitBids( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { if (isVariant(marketType, 'spot') && !oraclePriceData) { @@ -1231,12 +1253,14 @@ export class DLOB { * @param oraclePriceData * @param filterFcn */ - *getAsks( + *getAsks( marketIndex: number, _fallbackAsk: BN | undefined, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { if (isVariant(marketType, 'spot') && !oraclePriceData) { @@ -1276,12 +1300,14 @@ export class DLOB { * @param oraclePriceData * @param filterFcn */ - *getBids( + *getBids( marketIndex: number, _fallbackBid: BN | undefined, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData, + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData, filterFcn?: DLOBFilterFcn ): Generator { if (isVariant(marketType, 'spot') && !oraclePriceData) { @@ -1313,11 +1339,13 @@ export class DLOB { ); } - findCrossingRestingLimitOrders( + findCrossingRestingLimitOrders( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData ): NodeToFill[] { const nodesToFill = new Array(); @@ -1439,11 +1467,13 @@ export class DLOB { } } - public getBestAsk( + public getBestAsk( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData ): BN | undefined { const bestAsk = this.getRestingLimitAsks( marketIndex, @@ -1458,11 +1488,13 @@ export class DLOB { return undefined; } - public getBestBid( + public getBestBid( marketIndex: number, slot: number, - marketType: MarketType, - oraclePriceData: OraclePriceData + marketType: T, + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData ): BN | undefined { const bestBid = this.getRestingLimitBids( marketIndex, @@ -1636,7 +1668,7 @@ export class DLOB { if (isVariant(marketType, 'perp')) { const slot = slotSubscriber.getSlot(); const oraclePriceData = - driftClient.getOracleDataForPerpMarket(marketIndex); + driftClient.getMMOracleDataForPerpMarket(marketIndex); const bestAsk = this.getBestAsk( marketIndex, @@ -1681,18 +1713,18 @@ export class DLOB { } else if (isVariant(marketType, 'spot')) { const slot = slotSubscriber.getSlot(); const oraclePriceData = - driftClient.getOracleDataForPerpMarket(marketIndex); + driftClient.getOracleDataForSpotMarket(marketIndex); const bestAsk = this.getBestAsk( marketIndex, slot, - marketType, + MarketType.SPOT, oraclePriceData ); const bestBid = this.getBestBid( marketIndex, slot, - marketType, + MarketType.SPOT, oraclePriceData ); const mid = bestAsk.add(bestBid).div(new BN(2)); @@ -1783,7 +1815,7 @@ export class DLOB { * @param depth how many levels of the order book to return * @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber} */ - public getL2({ + public getL2({ marketIndex, marketType, slot, @@ -1792,9 +1824,11 @@ export class DLOB { fallbackL2Generators = [], }: { marketIndex: number; - marketType: MarketType; + marketType: T; slot: number; - oraclePriceData: OraclePriceData; + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData; depth: number; fallbackL2Generators?: L2OrderBookGenerator[]; }): L2OrderBook { @@ -1853,16 +1887,18 @@ export class DLOB { * @param slot * @param oraclePriceData */ - public getL3({ + public getL3({ marketIndex, marketType, slot, oraclePriceData, }: { marketIndex: number; - marketType: MarketType; + marketType: T; slot: number; - oraclePriceData: OraclePriceData; + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData; }): L3OrderBook { const bids: L3Level[] = []; const asks: L3Level[] = []; @@ -1945,7 +1981,7 @@ export class DLOB { * @param param.oraclePriceData the oracle price data * @returns the estimated quote amount filled: QUOTE_PRECISION */ - public estimateFillWithExactBaseAmount({ + public estimateFillWithExactBaseAmount({ marketIndex, marketType, baseAmount, @@ -1954,11 +1990,13 @@ export class DLOB { oraclePriceData, }: { marketIndex: number; - marketType: MarketType; + marketType: T; baseAmount: BN; orderDirection: PositionDirection; slot: number; - oraclePriceData: OraclePriceData; + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData; }): BN { if (isVariant(orderDirection, 'long')) { return this.estimateFillExactBaseAmountInForSide( @@ -1977,7 +2015,7 @@ export class DLOB { } } - public getBestMakers({ + public getBestMakers({ marketIndex, marketType, direction, @@ -1986,10 +2024,12 @@ export class DLOB { numMakers, }: { marketIndex: number; - marketType: MarketType; + marketType: T; direction: PositionDirection; slot: number; - oraclePriceData: OraclePriceData; + oraclePriceData: T extends { spot: unknown } + ? OraclePriceData + : MMOraclePriceData; numMakers: number; }): PublicKey[] { const makers = new Map(); diff --git a/sdk/src/dlob/DLOBSubscriber.ts b/sdk/src/dlob/DLOBSubscriber.ts index 431302c01..03e5cf70b 100644 --- a/sdk/src/dlob/DLOBSubscriber.ts +++ b/sdk/src/dlob/DLOBSubscriber.ts @@ -139,7 +139,7 @@ export class DLOBSubscriber { fallbackL2Generators = [ getVammL2Generator({ marketAccount: this.driftClient.getPerpMarketAccount(marketIndex), - oraclePriceData: + mmOraclePriceData: this.driftClient.getMMOracleDataForPerpMarket(marketIndex), numOrders: numVammOrders ?? depth, topOfBookQuoteAmounts: diff --git a/sdk/src/dlob/orderBookLevels.ts b/sdk/src/dlob/orderBookLevels.ts index 41022f499..2e4597685 100644 --- a/sdk/src/dlob/orderBookLevels.ts +++ b/sdk/src/dlob/orderBookLevels.ts @@ -22,7 +22,7 @@ import { PositionDirection, SwapDirection, } from '../types'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { PublicKey } from '@solana/web3.js'; import { standardizeBaseAssetAmount, standardizePrice } from '../math/orders'; @@ -178,18 +178,18 @@ export function createL2Levels( export function getVammL2Generator({ marketAccount, - oraclePriceData, + mmOraclePriceData, numOrders, now = new BN(Math.floor(Date.now() / 1000)), topOfBookQuoteAmounts = [], }: { marketAccount: PerpMarketAccount; - oraclePriceData: OraclePriceData; + mmOraclePriceData: MMOraclePriceData; numOrders: number; now?: BN; topOfBookQuoteAmounts?: BN[]; }): L2OrderBookGenerator { - const updatedAmm = calculateUpdatedAMM(marketAccount.amm, oraclePriceData); + const updatedAmm = calculateUpdatedAMM(marketAccount.amm, mmOraclePriceData); const paused = isOperationPaused( marketAccount.pausedOperations, PerpOperation.AMM_FILL @@ -209,7 +209,7 @@ export function getVammL2Generator({ const [bidReserves, askReserves] = calculateSpreadReserves( updatedAmm, - oraclePriceData, + mmOraclePriceData, now, isVariant(marketAccount.contractType, 'prediction') ); @@ -218,7 +218,7 @@ export function getVammL2Generator({ const commonOpts = { numOrders, numBaseOrders, - oraclePriceData, + mmOraclePriceData, orderTickSize: marketAccount.amm.orderTickSize, orderStepSize: marketAccount.amm.orderStepSize, pegMultiplier: updatedAmm.pegMultiplier, @@ -253,7 +253,7 @@ export function getVammL2Generator({ const raw = commonOpts.topOfBookQuoteAmounts[count] .mul(AMM_TO_QUOTE_PRECISION_RATIO) .mul(PRICE_PRECISION) - .div(commonOpts.oraclePriceData.price); + .div(commonOpts.mmOraclePriceData.price); baseSwap = standardizeBaseAssetAmount(raw, commonOpts.orderStepSize); const remaining = openLiquidity.abs().sub(topSize); if (remaining.lt(baseSwap)) baseSwap = remaining; diff --git a/sdk/src/driftClient.ts b/sdk/src/driftClient.ts index 305ffa015..370c3f00e 100644 --- a/sdk/src/driftClient.ts +++ b/sdk/src/driftClient.ts @@ -133,7 +133,7 @@ import { import { findDirectionToClose, positionIsAvailable } from './math/position'; import { getSignedTokenAmount, getTokenAmount } from './math/spotBalance'; import { decodeName, DEFAULT_USER_NAME, encodeName } from './userName'; -import { OraclePriceData } from './oracles/types'; +import { MMOraclePriceData, OraclePriceData } from './oracles/types'; import { DriftClientConfig } from './driftClientConfig'; import { PollingDriftClientAccountSubscriber } from './accounts/pollingDriftClientAccountSubscriber'; import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber'; @@ -8514,20 +8514,16 @@ export class DriftClient { } public getOracleDataForPerpMarket(marketIndex: number): OraclePriceData { - const perpMarket = this.getPerpMarketAccount(marketIndex); - const isMMOracleActive = !perpMarket.amm.mmOracleSlot.eq(ZERO); - return { - ...this.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket( - marketIndex - ).data, - isMMOracleActive, - }; + return this.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket( + marketIndex + ).data; } - public getMMOracleDataForPerpMarket(marketIndex: number): OraclePriceData { + public getMMOracleDataForPerpMarket(marketIndex: number): MMOraclePriceData { const perpMarket = this.getPerpMarketAccount(marketIndex); const oracleData = this.getOracleDataForPerpMarket(marketIndex); const stateAccountAndSlot = this.accountSubscriber.getStateAccountAndSlot(); + const isMMOracleActive = !perpMarket.amm.mmOracleSlot.eq(ZERO); const pctDiff = perpMarket.amm.mmOraclePrice .sub(oracleData.price) .abs() @@ -8544,20 +8540,18 @@ export class DriftClient { perpMarket.amm.mmOracleSlot < oracleData.slot || pctDiff.gt(PERCENTAGE_PRECISION.divn(100)) // 1% threshold ) { - return { ...oracleData, fetchedWithMMOracle: true }; + return { ...oracleData, isMMOracleActive }; } else { - const conf = getOracleConfidenceFromMMOracleData({ - mmOraclePrice: perpMarket.amm.mmOraclePrice, - mmOracleSlot: perpMarket.amm.mmOracleSlot, - oraclePriceData: oracleData, - }); + const conf = getOracleConfidenceFromMMOracleData( + perpMarket.amm.mmOraclePrice, + oracleData + ); return { price: perpMarket.amm.mmOraclePrice, slot: perpMarket.amm.mmOracleSlot, confidence: conf, hasSufficientNumberOfDataPoints: true, - fetchedWithMMOracle: true, - isMMOracleActive: oracleData.isMMOracleActive, + isMMOracleActive, }; } } diff --git a/sdk/src/idl/drift.json b/sdk/src/idl/drift.json index 2b6f9bbce..4330b01f7 100644 --- a/sdk/src/idl/drift.json +++ b/sdk/src/idl/drift.json @@ -16077,4 +16077,4 @@ "metadata": { "address": "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" } -} +} \ No newline at end of file diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index cf46acfd7..d90c4f097 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -26,7 +26,7 @@ import { assert } from '../assert/assert'; import { squareRootBN, sigNum, clampBN } from './utils'; import { standardizeBaseAssetAmount } from './orders'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { calculateRepegCost, calculateAdjustKCost, @@ -52,14 +52,14 @@ export function calculatePegFromTargetPrice( export function calculateOptimalPegAndBudget( amm: AMM, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): [BN, BN, BN, boolean] { const reservePriceBefore = calculatePrice( amm.baseAssetReserve, amm.quoteAssetReserve, amm.pegMultiplier ); - const targetPrice = oraclePriceData.price; + const targetPrice = mmOraclePriceData.price; const newPeg = calculatePegFromTargetPrice( targetPrice, amm.baseAssetReserve, @@ -113,13 +113,13 @@ export function calculateOptimalPegAndBudget( export function calculateNewAmm( amm: AMM, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): [BN, BN, BN, BN] { let pKNumer = new BN(1); let pKDenom = new BN(1); const [targetPrice, _newPeg, budget, _checkLowerBound] = - calculateOptimalPegAndBudget(amm, oraclePriceData); + calculateOptimalPegAndBudget(amm, mmOraclePriceData); let prePegCost = calculateRepegCost(amm, _newPeg); let newPeg = _newPeg; @@ -155,15 +155,15 @@ export function calculateNewAmm( export function calculateUpdatedAMM( amm: AMM, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): AMM { - if (amm.curveUpdateIntensity == 0 || oraclePriceData === undefined) { + if (amm.curveUpdateIntensity == 0 || mmOraclePriceData === undefined) { return amm; } const newAmm = Object.assign({}, amm); const [prepegCost, pKNumer, pKDenom, newPeg] = calculateNewAmm( amm, - oraclePriceData + mmOraclePriceData ); newAmm.baseAssetReserve = newAmm.baseAssetReserve.mul(pKNumer).div(pKDenom); @@ -196,21 +196,13 @@ export function calculateUpdatedAMM( export function calculateUpdatedAMMSpreadReserves( amm: AMM, direction: PositionDirection, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, isPrediction = false ): { baseAssetReserve: BN; quoteAssetReserve: BN; sqrtK: BN; newPeg: BN } { - if ( - !oraclePriceData?.fetchedWithMMOracle && - oraclePriceData?.isMMOracleActive - ) { - console.log( - 'Use driftClient method getMMOracleDataForPerpMarket for accurate updated AMM in calculateUpdatedAMMSpreadReserves' - ); - } - const newAmm = calculateUpdatedAMM(amm, oraclePriceData); + const newAmm = calculateUpdatedAMM(amm, mmOraclePriceData); const [shortReserves, longReserves] = calculateSpreadReserves( newAmm, - oraclePriceData, + mmOraclePriceData, undefined, isPrediction ); @@ -231,28 +223,20 @@ export function calculateUpdatedAMMSpreadReserves( export function calculateAMMBidAskPrice( amm: AMM, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, withUpdate = true, isPrediction = false ): [BN, BN] { - if ( - !oraclePriceData?.fetchedWithMMOracle && - oraclePriceData?.isMMOracleActive - ) { - console.log( - 'Use driftClient method getMMOracleDataForPerpMarket for accurate MM pricing in calculateAMMBidAskPrice' - ); - } let newAmm: AMM; if (withUpdate) { - newAmm = calculateUpdatedAMM(amm, oraclePriceData); + newAmm = calculateUpdatedAMM(amm, mmOraclePriceData); } else { newAmm = amm; } const [bidReserves, askReserves] = calculateSpreadReserves( newAmm, - oraclePriceData, + mmOraclePriceData, undefined, isPrediction ); @@ -277,20 +261,20 @@ export function calculateAMMBidAskPrice( */ export function calculateBidAskPrice( amm: AMM, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, withUpdate = true, isPrediction = false ): [BN, BN] { let newAmm: AMM; if (withUpdate) { - newAmm = calculateUpdatedAMM(amm, oraclePriceData); + newAmm = calculateUpdatedAMM(amm, mmOraclePriceData); } else { newAmm = amm; } const [bidReserves, askReserves] = calculateSpreadReserves( newAmm, - oraclePriceData, + mmOraclePriceData, undefined, isPrediction ); @@ -991,7 +975,7 @@ export function getQuoteAssetReservePredictionMarketBounds( export function calculateSpreadReserves( amm: AMM, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, now?: BN, isPrediction = false ) { @@ -1085,7 +1069,7 @@ export function calculateSpreadReserves( let [longSpread, shortSpread] = calculateSpread( amm, - oraclePriceData, + mmOraclePriceData, now, reservePrice ); @@ -1095,9 +1079,9 @@ export function calculateSpreadReserves( amm.curveUpdateIntensity > 100; if (doReferencePricOffsetSmooth) { - if (oraclePriceData.slot !== amm.lastUpdateSlot) { + if (mmOraclePriceData.slot !== amm.lastUpdateSlot) { const slotsPassed = - oraclePriceData.slot.toNumber() - amm.lastUpdateSlot.toNumber(); + mmOraclePriceData.slot.toNumber() - amm.lastUpdateSlot.toNumber(); const fullOffsetDelta = referencePriceOffset - amm.referencePriceOffset; const raw = Math.trunc( Math.min(Math.abs(fullOffsetDelta), slotsPassed * 1000) / 10 @@ -1212,7 +1196,7 @@ export function calculateMaxBaseAssetAmountToTrade( amm: AMM, limit_price: BN, direction: PositionDirection, - oraclePriceData?: OraclePriceData, + mmOraclePriceData?: MMOraclePriceData, now?: BN, isPrediction = false ): [BN, PositionDirection] { @@ -1227,7 +1211,7 @@ export function calculateMaxBaseAssetAmountToTrade( const newBaseAssetReserve = squareRootBN(newBaseAssetReserveSquared); const [shortSpreadReserves, longSpreadReserves] = calculateSpreadReserves( amm, - oraclePriceData, + mmOraclePriceData, now, isPrediction ); diff --git a/sdk/src/math/funding.ts b/sdk/src/math/funding.ts index 6b08c5610..281721404 100644 --- a/sdk/src/math/funding.ts +++ b/sdk/src/math/funding.ts @@ -9,7 +9,7 @@ import { } from '../constants/numericConstants'; import { BigNum } from '../factory/bigNum'; import { PerpMarketAccount, isVariant } from '../types'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { calculateBidAskPrice } from './amm'; import { calculateLiveOracleTwap } from './oracles'; import { clampBN } from './utils'; @@ -20,7 +20,7 @@ import { function calculateLiveMarkTwap( market: PerpMarketAccount, - oraclePriceData?: OraclePriceData, + mmOraclePriceData?: MMOraclePriceData, markPrice?: BN, now?: BN, period = new BN(3600) @@ -37,7 +37,7 @@ function calculateLiveMarkTwap( ); if (!markPrice) { - const [bid, ask] = calculateBidAskPrice(market.amm, oraclePriceData); + const [bid, ask] = calculateBidAskPrice(market.amm, mmOraclePriceData); markPrice = bid.add(ask).div(new BN(2)); } @@ -122,6 +122,7 @@ function shrinkStaleTwaps( */ export function calculateAllEstimatedFundingRate( market: PerpMarketAccount, + mmOraclePriceData?: MMOraclePriceData, oraclePriceData?: OraclePriceData, markPrice?: BN, now?: BN @@ -136,7 +137,7 @@ export function calculateAllEstimatedFundingRate( // calculate real-time mark and oracle twap const liveMarkTwap = calculateLiveMarkTwap( market, - oraclePriceData, + mmOraclePriceData, markPrice, now, market.amm.fundingPeriod @@ -265,6 +266,7 @@ const getFundingRatePct = (rawFundingRate: BN) => { */ export function calculateFormattedLiveFundingRate( market: PerpMarketAccount, + mmOraclePriceData: MMOraclePriceData, oraclePriceData: OraclePriceData, period: 'hour' | 'year' ): { @@ -278,6 +280,7 @@ export function calculateFormattedLiveFundingRate( const [_markTwapLive, _oracleTwapLive, longFundingRate, shortFundingRate] = calculateLongShortFundingRateAndLiveTwaps( market, + mmOraclePriceData, oraclePriceData, undefined, nowBN @@ -349,12 +352,14 @@ function getMaxPriceDivergenceForFundingRate( */ export function calculateLongShortFundingRate( market: PerpMarketAccount, + mmOraclePriceData?: MMOraclePriceData, oraclePriceData?: OraclePriceData, markPrice?: BN, now?: BN ): [BN, BN] { const [_1, _2, _, cappedAltEst, interpEst] = calculateAllEstimatedFundingRate( market, + mmOraclePriceData, oraclePriceData, markPrice, now @@ -380,12 +385,19 @@ export function calculateLongShortFundingRate( */ export function calculateLongShortFundingRateAndLiveTwaps( market: PerpMarketAccount, + mmOraclePriceData?: MMOraclePriceData, oraclePriceData?: OraclePriceData, markPrice?: BN, now?: BN ): [BN, BN, BN, BN] { const [markTwapLive, oracleTwapLive, _2, cappedAltEst, interpEst] = - calculateAllEstimatedFundingRate(market, oraclePriceData, markPrice, now); + calculateAllEstimatedFundingRate( + market, + mmOraclePriceData, + oraclePriceData, + markPrice, + now + ); if ( market.amm.baseAssetAmountLong.gt(market.amm.baseAssetAmountShort.abs()) diff --git a/sdk/src/math/market.ts b/sdk/src/math/market.ts index 78f95048a..4ef1bb675 100644 --- a/sdk/src/math/market.ts +++ b/sdk/src/math/market.ts @@ -20,7 +20,7 @@ import { calculateSizeDiscountAssetWeight, calculateSizePremiumLiabilityWeight, } from './margin'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { BASE_PRECISION, MARGIN_PRECISION, @@ -43,9 +43,9 @@ import { assert } from '../assert/assert'; */ export function calculateReservePrice( market: PerpMarketAccount, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { - const newAmm = calculateUpdatedAMM(market.amm, oraclePriceData); + const newAmm = calculateUpdatedAMM(market.amm, mmOraclePriceData); return calculatePrice( newAmm.baseAssetReserve, newAmm.quoteAssetReserve, @@ -61,13 +61,13 @@ export function calculateReservePrice( */ export function calculateBidPrice( market: PerpMarketAccount, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { const { baseAssetReserve, quoteAssetReserve, newPeg } = calculateUpdatedAMMSpreadReserves( market.amm, PositionDirection.SHORT, - oraclePriceData + mmOraclePriceData ); return calculatePrice(baseAssetReserve, quoteAssetReserve, newPeg); @@ -81,13 +81,13 @@ export function calculateBidPrice( */ export function calculateAskPrice( market: PerpMarketAccount, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { const { baseAssetReserve, quoteAssetReserve, newPeg } = calculateUpdatedAMMSpreadReserves( market.amm, PositionDirection.LONG, - oraclePriceData + mmOraclePriceData ); return calculatePrice(baseAssetReserve, quoteAssetReserve, newPeg); @@ -117,10 +117,10 @@ export function calculateNewMarketAfterTrade( export function calculateOracleReserveSpread( market: PerpMarketAccount, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { - const reservePrice = calculateReservePrice(market, oraclePriceData); - return calculateOracleSpread(reservePrice, oraclePriceData); + const reservePrice = calculateReservePrice(market, mmOraclePriceData); + return calculateOracleSpread(reservePrice, mmOraclePriceData); } export function calculateOracleSpread( @@ -298,7 +298,7 @@ export function calculateNetUserPnlImbalance( export function calculateAvailablePerpLiquidity( market: PerpMarketAccount, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, dlob: DLOB, slot: number ): { bids: BN; asks: BN } { @@ -315,7 +315,7 @@ export function calculateAvailablePerpLiquidity( market.marketIndex, slot, MarketType.PERP, - oraclePriceData + mmOraclePriceData )) { bids = bids.add( bid.order.baseAssetAmount.sub(bid.order.baseAssetAmountFilled) @@ -326,7 +326,7 @@ export function calculateAvailablePerpLiquidity( market.marketIndex, slot, MarketType.PERP, - oraclePriceData + mmOraclePriceData )) { asks = asks.add( ask.order.baseAssetAmount.sub(ask.order.baseAssetAmountFilled) diff --git a/sdk/src/math/orders.ts b/sdk/src/math/orders.ts index 7be65e58c..5789f1c22 100644 --- a/sdk/src/math/orders.ts +++ b/sdk/src/math/orders.ts @@ -10,7 +10,7 @@ import { } from '../types'; import { ZERO, TWO, ONE } from '../constants/numericConstants'; import { BN } from '@coral-xyz/anchor'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { getAuctionPrice, isAuctionComplete, @@ -153,7 +153,7 @@ export function standardizePrice( export function getLimitPrice( order: Order, - oraclePriceData: OraclePriceData, + oraclePriceData: OraclePriceData | MMOraclePriceData, slot: number, fallbackPrice?: BN, protectedMakerParams?: ProtectedMakerParams @@ -232,7 +232,7 @@ export function hasAuctionPrice(order: Order, slot: number): boolean { export function isFillableByVAMM( order: Order, market: PerpMarketAccount, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, slot: number, ts: number, minAuctionDuration: number @@ -242,7 +242,7 @@ export function isFillableByVAMM( calculateBaseAssetAmountForAmmToFulfill( order, market, - oraclePriceData, + mmOraclePriceData, slot ).gte(market.amm.minOrderSize)) || isOrderExpired(order, ts) @@ -252,23 +252,23 @@ export function isFillableByVAMM( export function calculateBaseAssetAmountForAmmToFulfill( order: Order, market: PerpMarketAccount, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, slot: number ): BN { if (mustBeTriggered(order) && !isTriggered(order)) { return ZERO; } - const limitPrice = getLimitPrice(order, oraclePriceData, slot); + const limitPrice = getLimitPrice(order, mmOraclePriceData, slot); let baseAssetAmount; - const updatedAMM = calculateUpdatedAMM(market.amm, oraclePriceData); + const updatedAMM = calculateUpdatedAMM(market.amm, mmOraclePriceData); if (limitPrice !== undefined) { baseAssetAmount = calculateBaseAssetAmountToFillUpToLimitPrice( order, updatedAMM, limitPrice, - oraclePriceData + mmOraclePriceData ); } else { baseAssetAmount = order.baseAssetAmount.sub(order.baseAssetAmountFilled); @@ -286,7 +286,7 @@ export function calculateBaseAssetAmountToFillUpToLimitPrice( order: Order, amm: AMM, limitPrice: BN, - oraclePriceData: OraclePriceData + mmOraclePriceData: MMOraclePriceData ): BN { const adjustedLimitPrice = isVariant(order.direction, 'long') ? limitPrice.sub(amm.orderTickSize) @@ -296,7 +296,7 @@ export function calculateBaseAssetAmountToFillUpToLimitPrice( amm, adjustedLimitPrice, order.direction, - oraclePriceData + mmOraclePriceData ); const baseAssetAmount = standardizeBaseAssetAmount( diff --git a/sdk/src/math/position.ts b/sdk/src/math/position.ts index c64b86d73..b3f8a30fa 100644 --- a/sdk/src/math/position.ts +++ b/sdk/src/math/position.ts @@ -8,7 +8,7 @@ import { ONE, ZERO, } from '../constants/numericConstants'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { PerpMarketAccount, PositionDirection, @@ -35,7 +35,7 @@ import { calculateNetUserPnlImbalance } from './market'; export function calculateBaseAssetValue( market: PerpMarketAccount, userPosition: PerpPosition, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, useSpread = true, skipUpdate = false ): BN { @@ -52,7 +52,7 @@ export function calculateBaseAssetValue( calculateUpdatedAMMSpreadReserves( market.amm, directionToClose, - oraclePriceData + mmOraclePriceData ); prepegAmm = { baseAssetReserve, @@ -61,7 +61,7 @@ export function calculateBaseAssetValue( pegMultiplier: newPeg, }; } else { - prepegAmm = calculateUpdatedAMM(market.amm, oraclePriceData); + prepegAmm = calculateUpdatedAMM(market.amm, mmOraclePriceData); } } else { prepegAmm = market.amm; diff --git a/sdk/src/math/trade.ts b/sdk/src/math/trade.ts index aeb9272ed..bd83d9e31 100644 --- a/sdk/src/math/trade.ts +++ b/sdk/src/math/trade.ts @@ -31,7 +31,7 @@ import { } from './amm'; import { squareRootBN } from './utils'; import { isVariant } from '../types'; -import { OraclePriceData } from '../oracles/types'; +import { MMOraclePriceData, OraclePriceData } from '../oracles/types'; import { DLOB } from '../dlob/DLOB'; import { PublicKey } from '@solana/web3.js'; import { Orderbook } from '@project-serum/serum'; @@ -77,19 +77,19 @@ export function calculateTradeSlippage( amount: BN, market: PerpMarketAccount, inputAssetType: AssetType = 'quote', - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, useSpread = true ): [BN, BN, BN, BN] { let oldPrice: BN; if (useSpread && market.amm.baseSpread > 0) { if (isVariant(direction, 'long')) { - oldPrice = calculateAskPrice(market, oraclePriceData); + oldPrice = calculateAskPrice(market, mmOraclePriceData); } else { - oldPrice = calculateBidPrice(market, oraclePriceData); + oldPrice = calculateBidPrice(market, mmOraclePriceData); } } else { - oldPrice = calculateReservePrice(market, oraclePriceData); + oldPrice = calculateReservePrice(market, mmOraclePriceData); } if (amount.eq(ZERO)) { return [ZERO, ZERO, oldPrice, oldPrice]; @@ -100,7 +100,7 @@ export function calculateTradeSlippage( amount, market, inputAssetType, - oraclePriceData, + mmOraclePriceData, useSpread ); @@ -112,7 +112,11 @@ export function calculateTradeSlippage( let amm: Parameters[0]; if (useSpread && market.amm.baseSpread > 0) { const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData); + calculateUpdatedAMMSpreadReserves( + market.amm, + direction, + mmOraclePriceData + ); amm = { baseAssetReserve, quoteAssetReserve, @@ -165,7 +169,7 @@ export function calculateTradeAcquiredAmounts( amount: BN, market: PerpMarketAccount, inputAssetType: AssetType = 'quote', - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, useSpread = true ): [BN, BN, BN] { if (amount.eq(ZERO)) { @@ -177,7 +181,11 @@ export function calculateTradeAcquiredAmounts( let amm: Parameters[0]; if (useSpread && market.amm.baseSpread > 0) { const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData); + calculateUpdatedAMMSpreadReserves( + market.amm, + direction, + mmOraclePriceData + ); amm = { baseAssetReserve, quoteAssetReserve, @@ -227,16 +235,16 @@ export function calculateTargetPriceTrade( targetPrice: BN, pct: BN = MAXPCT, outputAssetType: AssetType = 'quote', - oraclePriceData?: OraclePriceData, + mmOraclePriceData?: MMOraclePriceData, useSpread = true ): [PositionDirection, BN, BN, BN] { assert(market.amm.baseAssetReserve.gt(ZERO)); assert(targetPrice.gt(ZERO)); assert(pct.lte(MAXPCT) && pct.gt(ZERO)); - const reservePriceBefore = calculateReservePrice(market, oraclePriceData); - const bidPriceBefore = calculateBidPrice(market, oraclePriceData); - const askPriceBefore = calculateAskPrice(market, oraclePriceData); + const reservePriceBefore = calculateReservePrice(market, mmOraclePriceData); + const bidPriceBefore = calculateBidPrice(market, mmOraclePriceData); + const askPriceBefore = calculateAskPrice(market, mmOraclePriceData); let direction; if (targetPrice.gt(reservePriceBefore)) { @@ -261,7 +269,11 @@ export function calculateTargetPriceTrade( if (useSpread && market.amm.baseSpread > 0) { const { baseAssetReserve, quoteAssetReserve, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData); + calculateUpdatedAMMSpreadReserves( + market.amm, + direction, + mmOraclePriceData + ); baseAssetReserveBefore = baseAssetReserve; quoteAssetReserveBefore = quoteAssetReserve; peg = newPeg; @@ -387,7 +399,7 @@ export function calculateEstimatedPerpEntryPrice( amount: BN, direction: PositionDirection, market: PerpMarketAccount, - oraclePriceData: OraclePriceData, + mmOraclePriceData: MMOraclePriceData, dlob: DLOB, slot: number, usersToSkip = new Map() @@ -413,12 +425,12 @@ export function calculateEstimatedPerpEntryPrice( const takerIsLong = isVariant(direction, 'long'); const limitOrders = dlob[ takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids' - ](market.marketIndex, slot, MarketType.PERP, oraclePriceData); + ](market.marketIndex, slot, MarketType.PERP, mmOraclePriceData); const swapDirection = getSwapDirection(assetType, direction); const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, oraclePriceData); + calculateUpdatedAMMSpreadReserves(market.amm, direction, mmOraclePriceData); const amm = { baseAssetReserve, quoteAssetReserve, @@ -464,7 +476,7 @@ export function calculateEstimatedPerpEntryPrice( let limitOrder = limitOrders.next().value; if (limitOrder) { - const limitOrderPrice = limitOrder.getPrice(oraclePriceData, slot); + const limitOrderPrice = limitOrder.getPrice(mmOraclePriceData, slot); bestPrice = takerIsLong ? BN.min(limitOrderPrice, bestPrice) : BN.max(limitOrderPrice, bestPrice); @@ -477,7 +489,7 @@ export function calculateEstimatedPerpEntryPrice( !cumulativeBaseFilled.eq(amount) && (ammLiquidity.gt(ZERO) || limitOrder) ) { - const limitOrderPrice = limitOrder?.getPrice(oraclePriceData, slot); + const limitOrderPrice = limitOrder?.getPrice(mmOraclePriceData, slot); let maxAmmFill: BN; if (limitOrderPrice) { @@ -561,7 +573,7 @@ export function calculateEstimatedPerpEntryPrice( !cumulativeQuoteFilled.eq(amount) && (ammLiquidity.gt(ZERO) || limitOrder) ) { - const limitOrderPrice = limitOrder?.getPrice(oraclePriceData, slot); + const limitOrderPrice = limitOrder?.getPrice(mmOraclePriceData, slot); let maxAmmFill: BN; if (limitOrderPrice) { diff --git a/sdk/src/oracles/types.ts b/sdk/src/oracles/types.ts index 5ffbc2574..4c015be0d 100644 --- a/sdk/src/oracles/types.ts +++ b/sdk/src/oracles/types.ts @@ -2,10 +2,11 @@ import { BN } from '@coral-xyz/anchor'; import { PublicKey } from '@solana/web3.js'; import { OracleSource } from '../types'; -export type MMOraclePriceData = { - mmOraclePrice: BN; - mmOracleSlot: BN; - oraclePriceData: OraclePriceData; +export type MMOraclePriceData = Omit< + OraclePriceData, + 'twap' | 'twapConfidence' | 'maxPrice' +> & { + isMMOracleActive: boolean; }; export type OraclePriceData = { @@ -16,8 +17,6 @@ export type OraclePriceData = { twap?: BN; twapConfidence?: BN; maxPrice?: BN; // pre-launch markets only - fetchedWithMMOracle?: boolean; - isMMOracleActive?: boolean; }; export type OracleInfo = { diff --git a/sdk/src/oracles/utils.ts b/sdk/src/oracles/utils.ts index f26e49848..199549059 100644 --- a/sdk/src/oracles/utils.ts +++ b/sdk/src/oracles/utils.ts @@ -1,11 +1,10 @@ import { BN } from '@coral-xyz/anchor'; -import { MMOraclePriceData } from './types'; +import { OraclePriceData } from './types'; export function getOracleConfidenceFromMMOracleData( - mmOracleData: MMOraclePriceData + mmOraclePrice: BN, + oraclePriceData: OraclePriceData ): BN { - const mmOracleDiffPremium = mmOracleData.mmOraclePrice - .sub(mmOracleData.oraclePriceData.price) - .abs(); - return mmOracleData.oraclePriceData.confidence.add(mmOracleDiffPremium); + const mmOracleDiffPremium = mmOraclePrice.sub(oraclePriceData.price).abs(); + return oraclePriceData.confidence.add(mmOracleDiffPremium); } diff --git a/sdk/src/user.ts b/sdk/src/user.ts index 8224b110a..345dfbad3 100644 --- a/sdk/src/user.ts +++ b/sdk/src/user.ts @@ -90,7 +90,7 @@ import { calculateMarginUSDCRequiredForTrade, calculateWorstCaseBaseAssetAmount, } from './math/margin'; -import { OraclePriceData } from './oracles/types'; +import { MMOraclePriceData, OraclePriceData } from './oracles/types'; import { UserConfig } from './userConfig'; import { PollingUserAccountSubscriber } from './accounts/pollingUserAccountSubscriber'; import { WebSocketUserAccountSubscriber } from './accounts/webSocketUserAccountSubscriber'; @@ -671,7 +671,7 @@ export class User { )[0]; const perpMarket = this.driftClient.getPerpMarketAccount(marketIndex); - const oraclePriceData = this.getOracleDataForPerpMarket(marketIndex); + const oraclePriceData = this.getMMOracleDataForPerpMarket(marketIndex); const worstCaseBaseAssetAmount = perpPosition ? calculateWorstCaseBaseAssetAmount( perpPosition, @@ -843,7 +843,7 @@ export class User { const market = this.driftClient.getPerpMarketAccount( perpPosition.marketIndex ); - const oraclePriceData = this.getOracleDataForPerpMarket( + const oraclePriceData = this.getMMOracleDataForPerpMarket( market.marketIndex ); @@ -1031,7 +1031,7 @@ export class User { } for (const perpPosition of this.getActivePerpPositions()) { - const oraclePriceData = this.getOracleDataForPerpMarket( + const oraclePriceData = this.getMMOracleDataForPerpMarket( perpPosition.marketIndex ); @@ -1447,7 +1447,7 @@ export class User { )[0]; } - let valuationPrice = this.getOracleDataForPerpMarket( + let valuationPrice = this.getMMOracleDataForPerpMarket( market.marketIndex ).price; @@ -1679,7 +1679,7 @@ export class User { const entryPrice = calculateEntryPrice(position); - const oraclePriceData = this.getOracleDataForPerpMarket( + const oraclePriceData = this.getMMOracleDataForPerpMarket( position.marketIndex ); @@ -2660,7 +2660,7 @@ export class User { ? true : targetSide === currentPositionSide; - const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex); + const oracleData = this.getMMOracleDataForPerpMarket(targetMarketIndex); const marketAccount = this.driftClient.getPerpMarketAccount(targetMarketIndex); @@ -3426,7 +3426,7 @@ export class User { this.getEmptyPosition(targetMarketIndex); const perpMarket = this.driftClient.getPerpMarketAccount(targetMarketIndex); - const oracleData = this.getOracleDataForPerpMarket(targetMarketIndex); + const oracleData = this.getMMOracleDataForPerpMarket(targetMarketIndex); let { // eslint-disable-next-line prefer-const @@ -4106,7 +4106,7 @@ export class User { !!marginCategory )[0] || this.getEmptyPosition(marketToIgnore); - const oracleData = this.getOracleDataForPerpMarket(marketToIgnore); + const oracleData = this.getMMOracleDataForPerpMarket(marketToIgnore); let currentPerpPositionValueUSDC = ZERO; if (currentPerpPosition) { @@ -4124,8 +4124,8 @@ export class User { ).sub(currentPerpPositionValueUSDC); } - private getOracleDataForPerpMarket(marketIndex: number): OraclePriceData { - return this.driftClient.getOracleDataForPerpMarket(marketIndex); + private getMMOracleDataForPerpMarket(marketIndex: number): MMOraclePriceData { + return this.driftClient.getMMOracleDataForPerpMarket(marketIndex); } private getOracleDataForSpotMarket(marketIndex: number): OraclePriceData {