@drift-labs/sdk 2.67.0-beta.1 → 2.67.0-beta.3

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.
@@ -0,0 +1,11 @@
1
+ /// <reference types="node" />
2
+ import { Connection, PublicKey } from '@solana/web3.js';
3
+ import { OracleClient, OraclePriceData } from './types';
4
+ import { BorshAccountsCoder } from '@coral-xyz/anchor';
5
+ export declare class SwitchboardClient implements OracleClient {
6
+ connection: Connection;
7
+ coder: BorshAccountsCoder;
8
+ constructor(connection: Connection);
9
+ getOraclePriceData(pricePublicKey: PublicKey): Promise<OraclePriceData>;
10
+ getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData;
11
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SwitchboardClient = void 0;
7
+ const numericConstants_1 = require("../constants/numericConstants");
8
+ const switchboard_json_1 = __importDefault(require("../idl/switchboard.json"));
9
+ const anchor_1 = require("@coral-xyz/anchor");
10
+ class SwitchboardClient {
11
+ constructor(connection) {
12
+ this.connection = connection;
13
+ this.coder = new anchor_1.BorshAccountsCoder(switchboard_json_1.default);
14
+ }
15
+ async getOraclePriceData(pricePublicKey) {
16
+ const accountInfo = await this.connection.getAccountInfo(pricePublicKey);
17
+ return this.getOraclePriceDataFromBuffer(accountInfo.data);
18
+ }
19
+ getOraclePriceDataFromBuffer(buffer) {
20
+ const aggregatorAccountData = this.coder.decodeUnchecked('AggregatorAccountData', buffer);
21
+ const price = convertSwitchboardDecimal(aggregatorAccountData.latestConfirmedRound.result);
22
+ const confidence = convertSwitchboardDecimal(aggregatorAccountData.latestConfirmedRound.stdDeviation);
23
+ const hasSufficientNumberOfDataPoints = aggregatorAccountData.latestConfirmedRound.numSuccess >=
24
+ aggregatorAccountData.minOracleResults;
25
+ const slot = aggregatorAccountData.latestConfirmedRound.roundOpenSlot;
26
+ return {
27
+ price,
28
+ slot,
29
+ confidence,
30
+ hasSufficientNumberOfDataPoints,
31
+ };
32
+ }
33
+ }
34
+ exports.SwitchboardClient = SwitchboardClient;
35
+ function convertSwitchboardDecimal(switchboardDecimal) {
36
+ const switchboardPrecision = numericConstants_1.TEN.pow(new anchor_1.BN(switchboardDecimal.scale));
37
+ return switchboardDecimal.mantissa
38
+ .mul(numericConstants_1.PRICE_PRECISION)
39
+ .div(switchboardPrecision);
40
+ }
package/lib/types.d.ts CHANGED
@@ -143,6 +143,9 @@ export declare class OracleSource {
143
143
  static readonly PYTH_1M: {
144
144
  pyth1M: {};
145
145
  };
146
+ static readonly SWITCHBOARD: {
147
+ switchboard: {};
148
+ };
146
149
  static readonly QUOTE_ASSET: {
147
150
  quoteAsset: {};
148
151
  };
package/lib/types.js CHANGED
@@ -95,7 +95,7 @@ exports.OracleSource = OracleSource;
95
95
  OracleSource.PYTH = { pyth: {} };
96
96
  OracleSource.PYTH_1K = { pyth1K: {} };
97
97
  OracleSource.PYTH_1M = { pyth1M: {} };
98
- // static readonly SWITCHBOARD = { switchboard: {} };
98
+ OracleSource.SWITCHBOARD = { switchboard: {} };
99
99
  OracleSource.QUOTE_ASSET = { quoteAsset: {} };
100
100
  OracleSource.PYTH_STABLE_COIN = { pythStableCoin: {} };
101
101
  class OrderType {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.67.0-beta.1",
3
+ "version": "2.67.0-beta.3",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -116,8 +116,8 @@ export class PollingDriftClientAccountSubscriber
116
116
  this.eventEmitter.emit('update');
117
117
  }
118
118
 
119
- this.setPerpOracleMap();
120
- this.setSpotOracleMap();
119
+ await this.setPerpOracleMap();
120
+ await this.setSpotOracleMap();
121
121
 
122
122
  this.isSubscribing = false;
123
123
  this.isSubscribed = subscriptionSucceeded;
@@ -389,7 +389,7 @@ export class PollingDriftClientAccountSubscriber
389
389
  await this.addPerpMarketAccountToPoll(marketIndex);
390
390
  const accountToPoll = this.accountsToPoll.get(marketPublicKey.toString());
391
391
  await this.addAccountToAccountLoader(accountToPoll);
392
- this.setPerpOracleMap();
392
+ await this.setPerpOracleMap();
393
393
  return true;
394
394
  }
395
395
 
@@ -402,29 +402,60 @@ export class PollingDriftClientAccountSubscriber
402
402
  }
403
403
 
404
404
  this.addOracleToPoll(oracleInfo);
405
- const oracleToPoll = this.oraclesToPoll.get(
406
- oracleInfo.publicKey.toString()
407
- );
405
+ const oracleString = oracleInfo.publicKey.toBase58();
406
+ const oracleToPoll = this.oraclesToPoll.get(oracleString);
408
407
  await this.addOracleToAccountLoader(oracleToPoll);
408
+
409
+ await this.pauseForOracleToBeAdded(3, oracleString);
410
+
409
411
  return true;
410
412
  }
411
413
 
412
- private setPerpOracleMap() {
414
+ private async pauseForOracleToBeAdded(
415
+ tries: number,
416
+ oracle: string
417
+ ): Promise<void> {
418
+ let i = 0;
419
+ while (i < tries) {
420
+ await new Promise((r) =>
421
+ setTimeout(r, this.accountLoader.pollingFrequency)
422
+ );
423
+ if (this.accountLoader.bufferAndSlotMap.has(oracle)) {
424
+ return;
425
+ }
426
+ i++;
427
+ }
428
+ console.log(`Pausing to find oracle ${oracle} failed`);
429
+ }
430
+
431
+ private async setPerpOracleMap() {
413
432
  const perpMarkets = this.getMarketAccountsAndSlots();
414
433
  for (const perpMarket of perpMarkets) {
415
434
  const perpMarketAccount = perpMarket.data;
416
435
  const perpMarketIndex = perpMarketAccount.marketIndex;
417
436
  const oracle = perpMarketAccount.amm.oracle;
437
+ if (!this.oracles.has(oracle.toBase58())) {
438
+ await this.addOracle({
439
+ publicKey: oracle,
440
+ source: perpMarketAccount.amm.oracleSource,
441
+ });
442
+ }
418
443
  this.perpOracleMap.set(perpMarketIndex, oracle);
419
444
  }
420
445
  }
421
446
 
422
- private setSpotOracleMap() {
447
+ private async setSpotOracleMap() {
423
448
  const spotMarkets = this.getSpotMarketAccountsAndSlots();
424
449
  for (const spotMarket of spotMarkets) {
425
450
  const spotMarketAccount = spotMarket.data;
426
451
  const spotMarketIndex = spotMarketAccount.marketIndex;
427
452
  const oracle = spotMarketAccount.oracle;
453
+ if (!this.oracles.has(oracle.toBase58())) {
454
+ await this.addOracle({
455
+ publicKey: oracle,
456
+ source: spotMarketAccount.oracleSource,
457
+ });
458
+ }
428
459
  this.spotOracleMap.set(spotMarketIndex, oracle);
429
460
  }
430
461
  }
@@ -481,18 +512,14 @@ export class PollingDriftClientAccountSubscriber
481
512
  ): DataAndSlot<OraclePriceData> | undefined {
482
513
  const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex);
483
514
  const oracle = this.perpOracleMap.get(marketIndex);
515
+
484
516
  if (!perpMarketAccount || !oracle) {
485
517
  return undefined;
486
518
  }
487
519
 
488
520
  if (!perpMarketAccount.data.amm.oracle.equals(oracle)) {
489
521
  // If the oracle has changed, we need to update the oracle map in background
490
- this.addOracle({
491
- source: perpMarketAccount.data.amm.oracleSource,
492
- publicKey: perpMarketAccount.data.amm.oracle,
493
- }).then(() => {
494
- this.setPerpOracleMap();
495
- });
522
+ this.setPerpOracleMap();
496
523
  }
497
524
 
498
525
  return this.getOraclePriceDataAndSlot(oracle);
@@ -506,14 +533,10 @@ export class PollingDriftClientAccountSubscriber
506
533
  if (!spotMarketAccount || !oracle) {
507
534
  return undefined;
508
535
  }
536
+
509
537
  if (!spotMarketAccount.data.oracle.equals(oracle)) {
510
538
  // If the oracle has changed, we need to update the oracle map in background
511
- this.addOracle({
512
- source: spotMarketAccount.data.oracleSource,
513
- publicKey: spotMarketAccount.data.oracle,
514
- }).then(() => {
515
- this.setSpotOracleMap();
516
- });
539
+ this.setSpotOracleMap();
517
540
  }
518
541
 
519
542
  return this.getOraclePriceDataAndSlot(oracle);
@@ -125,8 +125,8 @@ export class WebSocketDriftClientAccountSubscriber
125
125
 
126
126
  this.eventEmitter.emit('update');
127
127
 
128
- this.setPerpOracleMap();
129
- this.setSpotOracleMap();
128
+ await this.setPerpOracleMap();
129
+ await this.setSpotOracleMap();
130
130
 
131
131
  this.isSubscribing = false;
132
132
  this.isSubscribed = true;
@@ -286,7 +286,7 @@ export class WebSocketDriftClientAccountSubscriber
286
286
  return true;
287
287
  }
288
288
  const subscriptionSuccess = this.subscribeToSpotMarketAccount(marketIndex);
289
- this.setSpotOracleMap();
289
+ await this.setSpotOracleMap();
290
290
  return subscriptionSuccess;
291
291
  }
292
292
 
@@ -295,7 +295,7 @@ export class WebSocketDriftClientAccountSubscriber
295
295
  return true;
296
296
  }
297
297
  const subscriptionSuccess = this.subscribeToPerpMarketAccount(marketIndex);
298
- this.setPerpOracleMap();
298
+ await this.setPerpOracleMap();
299
299
  return subscriptionSuccess;
300
300
  }
301
301
 
@@ -311,7 +311,7 @@ export class WebSocketDriftClientAccountSubscriber
311
311
  return this.subscribeToOracle(oracleInfo);
312
312
  }
313
313
 
314
- private setPerpOracleMap() {
314
+ private async setPerpOracleMap() {
315
315
  const perpMarkets = this.getMarketAccountsAndSlots();
316
316
  for (const perpMarket of perpMarkets) {
317
317
  if (!perpMarket) {
@@ -320,11 +320,17 @@ export class WebSocketDriftClientAccountSubscriber
320
320
  const perpMarketAccount = perpMarket.data;
321
321
  const perpMarketIndex = perpMarketAccount.marketIndex;
322
322
  const oracle = perpMarketAccount.amm.oracle;
323
+ if (!this.oracleSubscribers.has(oracle.toBase58())) {
324
+ await this.addOracle({
325
+ publicKey: oracle,
326
+ source: perpMarket.data.amm.oracleSource,
327
+ });
328
+ }
323
329
  this.perpOracleMap.set(perpMarketIndex, oracle);
324
330
  }
325
331
  }
326
332
 
327
- private setSpotOracleMap() {
333
+ private async setSpotOracleMap() {
328
334
  const spotMarkets = this.getSpotMarketAccountsAndSlots();
329
335
  for (const spotMarket of spotMarkets) {
330
336
  if (!spotMarket) {
@@ -333,6 +339,12 @@ export class WebSocketDriftClientAccountSubscriber
333
339
  const spotMarketAccount = spotMarket.data;
334
340
  const spotMarketIndex = spotMarketAccount.marketIndex;
335
341
  const oracle = spotMarketAccount.oracle;
342
+ if (!this.oracleSubscribers.has(oracle.toBase58())) {
343
+ await this.addOracle({
344
+ publicKey: oracle,
345
+ source: spotMarketAccount.oracleSource,
346
+ });
347
+ }
336
348
  this.spotOracleMap.set(spotMarketIndex, oracle);
337
349
  }
338
350
  }
@@ -400,12 +412,7 @@ export class WebSocketDriftClientAccountSubscriber
400
412
 
401
413
  if (!perpMarketAccount.data.amm.oracle.equals(oracle)) {
402
414
  // If the oracle has changed, we need to update the oracle map in background
403
- this.addOracle({
404
- source: perpMarketAccount.data.amm.oracleSource,
405
- publicKey: perpMarketAccount.data.amm.oracle,
406
- }).then(() => {
407
- this.setPerpOracleMap();
408
- });
415
+ this.setPerpOracleMap();
409
416
  }
410
417
 
411
418
  return this.getOraclePriceDataAndSlot(oracle);
@@ -422,12 +429,7 @@ export class WebSocketDriftClientAccountSubscriber
422
429
 
423
430
  if (!spotMarketAccount.data.oracle.equals(oracle)) {
424
431
  // If the oracle has changed, we need to update the oracle map in background
425
- this.addOracle({
426
- source: spotMarketAccount.data.oracleSource,
427
- publicKey: spotMarketAccount.data.oracle,
428
- }).then(() => {
429
- this.setSpotOracleMap();
430
- });
432
+ this.setSpotOracleMap();
431
433
  }
432
434
 
433
435
  return this.getOraclePriceDataAndSlot(oracle);
@@ -6183,9 +6183,13 @@ export class DriftClient {
6183
6183
  }
6184
6184
  }
6185
6185
 
6186
+ const userAccountExists =
6187
+ !!this.getUser()?.accountSubscriber?.isSubscribed &&
6188
+ (await this.checkIfAccountExists(this.getUser().userAccountPublicKey));
6189
+
6186
6190
  const remainingAccounts = this.getRemainingAccounts({
6187
- userAccounts: [this.getUserAccount()],
6188
- useMarketLastSlotCache: true,
6191
+ userAccounts: userAccountExists ? [this.getUserAccount()] : [],
6192
+ useMarketLastSlotCache: false,
6189
6193
  writableSpotMarketIndexes: [marketIndex],
6190
6194
  });
6191
6195
 
@@ -5,6 +5,7 @@ import { PythClient } from '../oracles/pythClient';
5
5
  // import { SwitchboardClient } from '../oracles/switchboardClient';
6
6
  import { QuoteAssetOracleClient } from '../oracles/quoteAssetOracleClient';
7
7
  import { BN } from '@coral-xyz/anchor';
8
+ import { SwitchboardClient } from '../oracles/switchboardClient';
8
9
 
9
10
  export function getOracleClient(
10
11
  oracleSource: OracleSource,
@@ -26,9 +27,9 @@ export function getOracleClient(
26
27
  return new PythClient(connection, undefined, true);
27
28
  }
28
29
 
29
- // if (isVariant(oracleSource, 'switchboard')) {
30
- // return new SwitchboardClient(connection);
31
- // }
30
+ if (isVariant(oracleSource, 'switchboard')) {
31
+ return new SwitchboardClient(connection);
32
+ }
32
33
 
33
34
  if (isVariant(oracleSource, 'quoteAsset')) {
34
35
  return new QuoteAssetOracleClient();