From 67e5f1c7e798183c58621be9a4c81c1a7eea17f7 Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Wed, 20 Aug 2025 16:19:42 -0700 Subject: [PATCH 1/2] ref price offset amm math fix --- sdk/src/math/amm.ts | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index f86ea9444f..a7b7b16385 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -939,7 +939,8 @@ export function calculateSpreadReserves( amm: AMM, mmOraclePriceData: MMOraclePriceData, now?: BN, - isPrediction = false + isPrediction = false, + latestSlot?: BN ) { function calculateSpreadReserve( spread: number, @@ -1041,28 +1042,28 @@ export function calculateSpreadReserves( amm.curveUpdateIntensity > 100; if (doReferencePricOffsetSmooth) { - if (mmOraclePriceData.slot !== amm.lastUpdateSlot) { - const slotsPassed = - mmOraclePriceData.slot.toNumber() - amm.lastUpdateSlot.toNumber(); - const fullOffsetDelta = referencePriceOffset - amm.referencePriceOffset; - const raw = Math.trunc( - Math.min(Math.abs(fullOffsetDelta), slotsPassed * 1000) / 10 - ); - const maxAllowed = - Math.abs(amm.referencePriceOffset) || Math.abs(referencePriceOffset); + const slotsPassed = + latestSlot != null + ? BN.max(latestSlot.sub(amm.lastUpdateSlot), ZERO).toNumber() + : 0; + const fullOffsetDelta = referencePriceOffset - amm.referencePriceOffset; + const raw = Math.trunc( + Math.min(Math.abs(fullOffsetDelta), slotsPassed * 1000) / 10 + ); + const maxAllowed = + Math.abs(amm.referencePriceOffset) || Math.abs(referencePriceOffset); - const magnitude = Math.min(Math.max(raw, 10), maxAllowed); - const referencePriceDelta = Math.sign(fullOffsetDelta) * magnitude; + const magnitude = Math.min(Math.max(raw, 10), maxAllowed); + const referencePriceDelta = Math.sign(fullOffsetDelta) * magnitude; - referencePriceOffset = amm.referencePriceOffset + referencePriceDelta; + referencePriceOffset = amm.referencePriceOffset + referencePriceDelta; - if (referencePriceDelta < 0) { - longSpread += Math.abs(referencePriceDelta); - shortSpread += Math.abs(referencePriceOffset); - } else { - shortSpread += Math.abs(referencePriceDelta); - longSpread += Math.abs(referencePriceOffset); - } + if (referencePriceDelta < 0) { + longSpread += Math.abs(referencePriceDelta); + shortSpread += Math.abs(referencePriceOffset); + } else { + shortSpread += Math.abs(referencePriceDelta); + longSpread += Math.abs(referencePriceOffset); } } From 1d12b679cd0321675f31e415a8d4937900e856db Mon Sep 17 00:00:00 2001 From: Nour Alharithi Date: Wed, 20 Aug 2025 16:26:30 -0700 Subject: [PATCH 2/2] add latest slot optional var to callers of update amm spread --- sdk/src/dlob/orderBookLevels.ts | 5 ++++- sdk/src/math/amm.ts | 12 ++++++++---- sdk/src/math/market.ts | 14 ++++++++++---- sdk/src/math/position.ts | 7 +++++-- sdk/src/math/trade.ts | 29 ++++++++++++++++++++++------- 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/sdk/src/dlob/orderBookLevels.ts b/sdk/src/dlob/orderBookLevels.ts index 2e45976858..67528a51bb 100644 --- a/sdk/src/dlob/orderBookLevels.ts +++ b/sdk/src/dlob/orderBookLevels.ts @@ -182,12 +182,14 @@ export function getVammL2Generator({ numOrders, now = new BN(Math.floor(Date.now() / 1000)), topOfBookQuoteAmounts = [], + latestSlot, }: { marketAccount: PerpMarketAccount; mmOraclePriceData: MMOraclePriceData; numOrders: number; now?: BN; topOfBookQuoteAmounts?: BN[]; + latestSlot?: BN; }): L2OrderBookGenerator { const updatedAmm = calculateUpdatedAMM(marketAccount.amm, mmOraclePriceData); const paused = isOperationPaused( @@ -211,7 +213,8 @@ export function getVammL2Generator({ updatedAmm, mmOraclePriceData, now, - isVariant(marketAccount.contractType, 'prediction') + isVariant(marketAccount.contractType, 'prediction'), + latestSlot ); const numBaseOrders = Math.max(1, numOrders - topOfBookQuoteAmounts.length); diff --git a/sdk/src/math/amm.ts b/sdk/src/math/amm.ts index a7b7b16385..7b41b4a783 100644 --- a/sdk/src/math/amm.ts +++ b/sdk/src/math/amm.ts @@ -197,14 +197,16 @@ export function calculateUpdatedAMMSpreadReserves( amm: AMM, direction: PositionDirection, mmOraclePriceData: MMOraclePriceData, - isPrediction = false + isPrediction = false, + latestSlot?: BN ): { baseAssetReserve: BN; quoteAssetReserve: BN; sqrtK: BN; newPeg: BN } { const newAmm = calculateUpdatedAMM(amm, mmOraclePriceData); const [shortReserves, longReserves] = calculateSpreadReserves( newAmm, mmOraclePriceData, undefined, - isPrediction + isPrediction, + latestSlot ); const dirReserves = isVariant(direction, 'long') @@ -225,7 +227,8 @@ export function calculateBidAskPrice( amm: AMM, mmOraclePriceData: MMOraclePriceData, withUpdate = true, - isPrediction = false + isPrediction = false, + latestSlot?: BN ): [BN, BN] { let newAmm: AMM; if (withUpdate) { @@ -238,7 +241,8 @@ export function calculateBidAskPrice( newAmm, mmOraclePriceData, undefined, - isPrediction + isPrediction, + latestSlot ); const askPrice = calculatePrice( diff --git a/sdk/src/math/market.ts b/sdk/src/math/market.ts index 4ef1bb6752..6dd3697de6 100644 --- a/sdk/src/math/market.ts +++ b/sdk/src/math/market.ts @@ -61,13 +61,16 @@ export function calculateReservePrice( */ export function calculateBidPrice( market: PerpMarketAccount, - mmOraclePriceData: MMOraclePriceData + mmOraclePriceData: MMOraclePriceData, + latestSlot?: BN ): BN { const { baseAssetReserve, quoteAssetReserve, newPeg } = calculateUpdatedAMMSpreadReserves( market.amm, PositionDirection.SHORT, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); return calculatePrice(baseAssetReserve, quoteAssetReserve, newPeg); @@ -81,13 +84,16 @@ export function calculateBidPrice( */ export function calculateAskPrice( market: PerpMarketAccount, - mmOraclePriceData: MMOraclePriceData + mmOraclePriceData: MMOraclePriceData, + latestSlot?: BN ): BN { const { baseAssetReserve, quoteAssetReserve, newPeg } = calculateUpdatedAMMSpreadReserves( market.amm, PositionDirection.LONG, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); return calculatePrice(baseAssetReserve, quoteAssetReserve, newPeg); diff --git a/sdk/src/math/position.ts b/sdk/src/math/position.ts index b3f8a30fac..3db5007a20 100644 --- a/sdk/src/math/position.ts +++ b/sdk/src/math/position.ts @@ -37,7 +37,8 @@ export function calculateBaseAssetValue( userPosition: PerpPosition, mmOraclePriceData: MMOraclePriceData, useSpread = true, - skipUpdate = false + skipUpdate = false, + latestSlot?: BN ): BN { if (userPosition.baseAssetAmount.eq(ZERO)) { return ZERO; @@ -52,7 +53,9 @@ export function calculateBaseAssetValue( calculateUpdatedAMMSpreadReserves( market.amm, directionToClose, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); prepegAmm = { baseAssetReserve, diff --git a/sdk/src/math/trade.ts b/sdk/src/math/trade.ts index bd83d9e317..2b0dec0f58 100644 --- a/sdk/src/math/trade.ts +++ b/sdk/src/math/trade.ts @@ -78,7 +78,8 @@ export function calculateTradeSlippage( market: PerpMarketAccount, inputAssetType: AssetType = 'quote', mmOraclePriceData: MMOraclePriceData, - useSpread = true + useSpread = true, + latestSlot?: BN ): [BN, BN, BN, BN] { let oldPrice: BN; @@ -115,7 +116,9 @@ export function calculateTradeSlippage( calculateUpdatedAMMSpreadReserves( market.amm, direction, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); amm = { baseAssetReserve, @@ -170,7 +173,8 @@ export function calculateTradeAcquiredAmounts( market: PerpMarketAccount, inputAssetType: AssetType = 'quote', mmOraclePriceData: MMOraclePriceData, - useSpread = true + useSpread = true, + latestSlot?: BN ): [BN, BN, BN] { if (amount.eq(ZERO)) { return [ZERO, ZERO, ZERO]; @@ -184,7 +188,9 @@ export function calculateTradeAcquiredAmounts( calculateUpdatedAMMSpreadReserves( market.amm, direction, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); amm = { baseAssetReserve, @@ -236,7 +242,8 @@ export function calculateTargetPriceTrade( pct: BN = MAXPCT, outputAssetType: AssetType = 'quote', mmOraclePriceData?: MMOraclePriceData, - useSpread = true + useSpread = true, + latestSlot?: BN ): [PositionDirection, BN, BN, BN] { assert(market.amm.baseAssetReserve.gt(ZERO)); assert(targetPrice.gt(ZERO)); @@ -272,7 +279,9 @@ export function calculateTargetPriceTrade( calculateUpdatedAMMSpreadReserves( market.amm, direction, - mmOraclePriceData + mmOraclePriceData, + undefined, + latestSlot ); baseAssetReserveBefore = baseAssetReserve; quoteAssetReserveBefore = quoteAssetReserve; @@ -430,7 +439,13 @@ export function calculateEstimatedPerpEntryPrice( const swapDirection = getSwapDirection(assetType, direction); const { baseAssetReserve, quoteAssetReserve, sqrtK, newPeg } = - calculateUpdatedAMMSpreadReserves(market.amm, direction, mmOraclePriceData); + calculateUpdatedAMMSpreadReserves( + market.amm, + direction, + mmOraclePriceData, + undefined, + new BN(slot) + ); const amm = { baseAssetReserve, quoteAssetReserve,