@drift-labs/sdk 2.71.0-beta.3 → 2.71.0-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { AMM, OracleGuardRails } from '../types';
1
+ import { AMM, OracleGuardRails, isVariant } from '../types';
2
2
  import { OraclePriceData } from '../oracles/types';
3
3
  import {
4
4
  BID_ASK_SPREAD_PRECISION,
@@ -27,12 +27,33 @@ export function oraclePriceBands(
27
27
  return [oraclePriceData.price.sub(offset), oraclePriceData.price.add(offset)];
28
28
  }
29
29
 
30
+ export function getMaxConfidenceIntervalMultiplier(
31
+ market: PerpMarketAccount
32
+ ): BN {
33
+ let maxConfidenceIntervalMultiplier;
34
+ if (isVariant(market.contractTier, 'a')) {
35
+ maxConfidenceIntervalMultiplier = new BN(1);
36
+ } else if (isVariant(market.contractTier, 'b')) {
37
+ maxConfidenceIntervalMultiplier = new BN(1);
38
+ } else if (isVariant(market.contractTier, 'c')) {
39
+ maxConfidenceIntervalMultiplier = new BN(2);
40
+ } else if (isVariant(market.contractTier, 'speculative')) {
41
+ maxConfidenceIntervalMultiplier = new BN(10);
42
+ } else if (isVariant(market.contractTier, 'isolated')) {
43
+ maxConfidenceIntervalMultiplier = new BN(50);
44
+ }
45
+ return maxConfidenceIntervalMultiplier;
46
+ }
47
+
30
48
  export function isOracleValid(
31
- amm: AMM,
49
+ market: PerpMarketAccount,
32
50
  oraclePriceData: OraclePriceData,
33
51
  oracleGuardRails: OracleGuardRails,
34
52
  slot: number
35
53
  ): boolean {
54
+ // checks if oracle is valid for an AMM only fill
55
+
56
+ const amm = market.amm;
36
57
  const isOraclePriceNonPositive = oraclePriceData.price.lte(ZERO);
37
58
  const isOraclePriceTooVolatile =
38
59
  oraclePriceData.price
@@ -42,10 +63,16 @@ export function isOracleValid(
42
63
  .div(BN.max(ONE, oraclePriceData.price))
43
64
  .gt(oracleGuardRails.validity.tooVolatileRatio);
44
65
 
66
+ const maxConfidenceIntervalMultiplier =
67
+ getMaxConfidenceIntervalMultiplier(market);
45
68
  const isConfidenceTooLarge = BN.max(ONE, oraclePriceData.confidence)
46
69
  .mul(BID_ASK_SPREAD_PRECISION)
47
70
  .div(oraclePriceData.price)
48
- .gt(oracleGuardRails.validity.confidenceIntervalMaxSize);
71
+ .gt(
72
+ oracleGuardRails.validity.confidenceIntervalMaxSize.mul(
73
+ maxConfidenceIntervalMultiplier
74
+ )
75
+ );
49
76
 
50
77
  const oracleIsStale = new BN(slot)
51
78
  .sub(oraclePriceData.slot)
@@ -0,0 +1,77 @@
1
+ import { Connection, PublicKey } from '@solana/web3.js';
2
+ import { PRICE_PRECISION, TEN } from '../constants/numericConstants';
3
+ import { OracleClient, OraclePriceData } from './types';
4
+ import switchboardV2Idl from '../idl/switchboard.json';
5
+ import { BorshAccountsCoder, BN, Idl } from '@coral-xyz/anchor';
6
+
7
+ type SwitchboardDecimal = {
8
+ scale: number;
9
+ mantissa: BN;
10
+ };
11
+
12
+ type AggregatorAccountData = {
13
+ latestConfirmedRound: {
14
+ result: SwitchboardDecimal;
15
+ stdDeviation: SwitchboardDecimal;
16
+ numSuccess: number;
17
+ roundOpenSlot: BN;
18
+ };
19
+ minOracleResults: number;
20
+ };
21
+
22
+ export class SwitchboardClient implements OracleClient {
23
+ connection: Connection;
24
+ coder: BorshAccountsCoder;
25
+
26
+ public constructor(connection: Connection) {
27
+ this.connection = connection;
28
+ this.coder = new BorshAccountsCoder(switchboardV2Idl as Idl);
29
+ }
30
+
31
+ public async getOraclePriceData(
32
+ pricePublicKey: PublicKey
33
+ ): Promise<OraclePriceData> {
34
+ const accountInfo = await this.connection.getAccountInfo(pricePublicKey);
35
+ return this.getOraclePriceDataFromBuffer(accountInfo.data);
36
+ }
37
+
38
+ public getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData {
39
+ const aggregatorAccountData = this.coder.decodeUnchecked(
40
+ 'AggregatorAccountData',
41
+ buffer
42
+ ) as AggregatorAccountData;
43
+
44
+ const price = convertSwitchboardDecimal(
45
+ aggregatorAccountData.latestConfirmedRound.result
46
+ );
47
+
48
+ const confidence = BN.max(
49
+ convertSwitchboardDecimal(
50
+ aggregatorAccountData.latestConfirmedRound.stdDeviation
51
+ ),
52
+ price.divn(1000)
53
+ );
54
+
55
+ const hasSufficientNumberOfDataPoints =
56
+ aggregatorAccountData.latestConfirmedRound.numSuccess >=
57
+ aggregatorAccountData.minOracleResults;
58
+
59
+ const slot: BN = aggregatorAccountData.latestConfirmedRound.roundOpenSlot;
60
+ return {
61
+ price,
62
+ slot,
63
+ confidence,
64
+ hasSufficientNumberOfDataPoints,
65
+ };
66
+ }
67
+ }
68
+
69
+ function convertSwitchboardDecimal(switchboardDecimal: {
70
+ scale: number;
71
+ mantissa: BN;
72
+ }): BN {
73
+ const switchboardPrecision = TEN.pow(new BN(switchboardDecimal.scale));
74
+ return switchboardDecimal.mantissa
75
+ .mul(PRICE_PRECISION)
76
+ .div(switchboardPrecision);
77
+ }
package/src/types.ts CHANGED
@@ -93,7 +93,7 @@ export class OracleSource {
93
93
  static readonly PYTH = { pyth: {} };
94
94
  static readonly PYTH_1K = { pyth1K: {} };
95
95
  static readonly PYTH_1M = { pyth1M: {} };
96
- // static readonly SWITCHBOARD = { switchboard: {} };
96
+ static readonly SWITCHBOARD = { switchboard: {} };
97
97
  static readonly QUOTE_ASSET = { quoteAsset: {} };
98
98
  static readonly PYTH_STABLE_COIN = { pythStableCoin: {} };
99
99
  static readonly Prelaunch = { prelaunch: {} };
package/tests/amm/test.ts CHANGED
@@ -1193,12 +1193,12 @@ describe('AMM Tests', () => {
1193
1193
  };
1194
1194
 
1195
1195
  // good oracle
1196
- assert(isOracleValid(mockAmm, oraclePriceData, oracleGuardRails, slot + 5));
1196
+ assert(isOracleValid(mockMarket1, oraclePriceData, oracleGuardRails, slot + 5));
1197
1197
 
1198
1198
  // conf too high
1199
1199
  assert(
1200
1200
  !isOracleValid(
1201
- mockAmm,
1201
+ mockMarket1,
1202
1202
  {
1203
1203
  price: new BN(13.553 * PRICE_PRECISION.toNumber()),
1204
1204
  slot: new BN(slot),
@@ -1213,7 +1213,7 @@ describe('AMM Tests', () => {
1213
1213
  // not hasSufficientNumberOfDataPoints
1214
1214
  assert(
1215
1215
  !isOracleValid(
1216
- mockAmm,
1216
+ mockMarket1,
1217
1217
  {
1218
1218
  price: new BN(13.553 * PRICE_PRECISION.toNumber()),
1219
1219
  slot: new BN(slot),
@@ -1228,7 +1228,7 @@ describe('AMM Tests', () => {
1228
1228
  // negative oracle price
1229
1229
  assert(
1230
1230
  !isOracleValid(
1231
- mockAmm,
1231
+ mockMarket1,
1232
1232
  {
1233
1233
  price: new BN(-1 * PRICE_PRECISION.toNumber()),
1234
1234
  slot: new BN(slot),
@@ -1243,7 +1243,7 @@ describe('AMM Tests', () => {
1243
1243
  // too delayed for amm
1244
1244
  assert(
1245
1245
  !isOracleValid(
1246
- mockAmm,
1246
+ mockMarket1,
1247
1247
  {
1248
1248
  price: new BN(13.553 * PRICE_PRECISION.toNumber()),
1249
1249
  slot: new BN(slot),
@@ -1258,7 +1258,7 @@ describe('AMM Tests', () => {
1258
1258
  // im passing stale slot (should not call oracle invalid)
1259
1259
  assert(
1260
1260
  isOracleValid(
1261
- mockAmm,
1261
+ mockMarket1,
1262
1262
  {
1263
1263
  price: new BN(13.553 * PRICE_PRECISION.toNumber()),
1264
1264
  slot: new BN(slot + 100),
@@ -1273,7 +1273,7 @@ describe('AMM Tests', () => {
1273
1273
  // too volatile (more than 5x higher)
1274
1274
  assert(
1275
1275
  !isOracleValid(
1276
- mockAmm,
1276
+ mockMarket1,
1277
1277
  {
1278
1278
  price: new BN(113.553 * PRICE_PRECISION.toNumber()),
1279
1279
  slot: new BN(slot + 5),
@@ -1288,7 +1288,7 @@ describe('AMM Tests', () => {
1288
1288
  // too volatile (more than 1/5 lower)
1289
1289
  assert(
1290
1290
  !isOracleValid(
1291
- mockAmm,
1291
+ mockMarket1,
1292
1292
  {
1293
1293
  price: new BN(0.553 * PRICE_PRECISION.toNumber()),
1294
1294
  slot: new BN(slot + 5),