diff --git a/CHANGELOG.md b/CHANGELOG.md index 018d9e7ef..1b60a3a6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixes +- ts-sd: fix oracle is valid ([#806](https://github.com/drift-labs/protocol-v2/pull/806)) + ### Breaking diff --git a/sdk/src/jupiter/jupiterClient.ts b/sdk/src/jupiter/jupiterClient.ts index 91d49f187..474bf339a 100644 --- a/sdk/src/jupiter/jupiterClient.ts +++ b/sdk/src/jupiter/jupiterClient.ts @@ -215,7 +215,7 @@ export interface QuoteResponse { * @type {string} * @memberof QuoteResponse */ - error?: string + error?: string; } export class JupiterClient { diff --git a/sdk/src/math/oracles.ts b/sdk/src/math/oracles.ts index d5bcedccb..0f0715fb2 100644 --- a/sdk/src/math/oracles.ts +++ b/sdk/src/math/oracles.ts @@ -47,8 +47,8 @@ export function isOracleValid( .div(oraclePriceData.price) .gt(oracleGuardRails.validity.confidenceIntervalMaxSize); - const oracleIsStale = oraclePriceData.slot - .sub(new BN(slot)) + const oracleIsStale = new BN(slot) + .sub(oraclePriceData.slot) .gt(oracleGuardRails.validity.slotsBeforeStaleForAmm); return !( diff --git a/sdk/tests/amm/test.ts b/sdk/tests/amm/test.ts index 484d075bc..9a54ec143 100644 --- a/sdk/tests/amm/test.ts +++ b/sdk/tests/amm/test.ts @@ -28,6 +28,8 @@ import { squareRootBN, calculateReferencePriceOffset, calculateInventoryLiquidityRatio, + isOracleValid, + OracleGuardRails, } from '../../src'; import { mockPerpMarkets } from '../dlob/helpers'; @@ -875,11 +877,12 @@ describe('AMM Tests', () => { const mockMarket1 = myMockPerpMarkets[0]; const mockAmm = mockMarket1.amm; const now = new BN(new Date().getTime() / 1000); //todo + const slot = 999999999; const oraclePriceData = { price: new BN(13.553 * PRICE_PRECISION.toNumber()), - slot: new BN(68 + 1), - confidence: new BN(1), + slot: new BN(slot), + confidence: new BN(1000), hasSufficientNumberOfDataPoints: true, }; mockAmm.oracleStd = new BN(0.18 * PRICE_PRECISION.toNumber()); @@ -901,6 +904,127 @@ describe('AMM Tests', () => { const liveOracleStd = calculateLiveOracleStd(mockAmm, oraclePriceData, now); console.log('liveOracleStd:', liveOracleStd.toNumber()); assert(liveOracleStd.eq(new BN(192962))); + + const oracleGuardRails: OracleGuardRails = { + priceDivergence: { + markOraclePercentDivergence: PERCENTAGE_PRECISION.divn(10), + oracleTwap5MinPercentDivergence: PERCENTAGE_PRECISION.divn(10), + }, + validity: { + slotsBeforeStaleForAmm: new BN(10), + slotsBeforeStaleForMargin: new BN(60), + confidenceIntervalMaxSize: new BN(20000), + tooVolatileRatio: new BN(5), + }, + }; + + // good oracle + assert(isOracleValid(mockAmm, oraclePriceData, oracleGuardRails, slot + 5)); + + // conf too high + assert( + !isOracleValid( + mockAmm, + { + price: new BN(13.553 * PRICE_PRECISION.toNumber()), + slot: new BN(slot), + confidence: new BN(13.553 * PRICE_PRECISION.toNumber() * 0.021), + hasSufficientNumberOfDataPoints: true, + }, + oracleGuardRails, + slot + ) + ); + + // not hasSufficientNumberOfDataPoints + assert( + !isOracleValid( + mockAmm, + { + price: new BN(13.553 * PRICE_PRECISION.toNumber()), + slot: new BN(slot), + confidence: new BN(1), + hasSufficientNumberOfDataPoints: false, + }, + oracleGuardRails, + slot + ) + ); + + // negative oracle price + assert( + !isOracleValid( + mockAmm, + { + price: new BN(-1 * PRICE_PRECISION.toNumber()), + slot: new BN(slot), + confidence: new BN(1), + hasSufficientNumberOfDataPoints: true, + }, + oracleGuardRails, + slot + ) + ); + + // too delayed for amm + assert( + !isOracleValid( + mockAmm, + { + price: new BN(13.553 * PRICE_PRECISION.toNumber()), + slot: new BN(slot), + confidence: new BN(1), + hasSufficientNumberOfDataPoints: true, + }, + oracleGuardRails, + slot + 100 + ) + ); + + // im passing stale slot (should not call oracle invalid) + assert( + isOracleValid( + mockAmm, + { + price: new BN(13.553 * PRICE_PRECISION.toNumber()), + slot: new BN(slot + 100), + confidence: new BN(1), + hasSufficientNumberOfDataPoints: true, + }, + oracleGuardRails, + slot + ) + ); + + // too volatile (more than 5x higher) + assert( + !isOracleValid( + mockAmm, + { + price: new BN(113.553 * PRICE_PRECISION.toNumber()), + slot: new BN(slot + 5), + confidence: new BN(1), + hasSufficientNumberOfDataPoints: true, + }, + oracleGuardRails, + slot + ) + ); + + // too volatile (more than 1/5 lower) + assert( + !isOracleValid( + mockAmm, + { + price: new BN(0.553 * PRICE_PRECISION.toNumber()), + slot: new BN(slot + 5), + confidence: new BN(1), + hasSufficientNumberOfDataPoints: true, + }, + oracleGuardRails, + slot + ) + ); }); it('predicted funding rate mock1', async () => {