@drift-labs/sdk 2.22.0 → 2.23.0-beta.1

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.
package/lib/user.js CHANGED
@@ -268,7 +268,7 @@ class User {
268
268
  * @returns The margin requirement of a certain type (Initial or Maintenance) in USDC. : QUOTE_PRECISION
269
269
  */
270
270
  getMarginRequirement(marginCategory, liquidationBuffer, strict = false) {
271
- return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true, strict));
271
+ return this.getTotalPerpPositionValue(marginCategory, liquidationBuffer, true, strict).add(this.getSpotMarketLiabilityValue(undefined, marginCategory, liquidationBuffer, true, strict));
272
272
  }
273
273
  /**
274
274
  * @returns The initial margin requirement in USDC. : QUOTE_PRECISION
@@ -292,17 +292,31 @@ class User {
292
292
  * calculates unrealized position price pnl
293
293
  * @returns : Precision QUOTE_PRECISION
294
294
  */
295
- getUnrealizedPNL(withFunding, marketIndex, withWeightMarginCategory) {
296
- const quoteSpotMarket = this.driftClient.getQuoteSpotMarketAccount();
295
+ getUnrealizedPNL(withFunding, marketIndex, withWeightMarginCategory, strict = false) {
297
296
  return this.getActivePerpPositions()
298
297
  .filter((pos) => (marketIndex ? pos.marketIndex === marketIndex : true))
299
298
  .reduce((unrealizedPnl, perpPosition) => {
300
299
  const market = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
301
300
  const oraclePriceData = this.getOracleDataForPerpMarket(market.marketIndex);
301
+ const quoteSpotMarket = this.driftClient.getSpotMarketAccount(market.quoteSpotMarketIndex);
302
+ const quoteOraclePriceData = this.getOracleDataForSpotMarket(market.quoteSpotMarketIndex);
302
303
  if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
303
304
  perpPosition = this.getSettledLPPosition(perpPosition.marketIndex)[0];
304
305
  }
305
306
  let positionUnrealizedPnl = (0, _1.calculatePositionPNL)(market, perpPosition, withFunding, oraclePriceData);
307
+ let quotePrice;
308
+ if (strict && positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
309
+ quotePrice = _1.BN.min(quoteOraclePriceData.price, quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min);
310
+ }
311
+ else if (strict && positionUnrealizedPnl.lt(numericConstants_1.ZERO)) {
312
+ quotePrice = _1.BN.max(quoteOraclePriceData.price, quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min);
313
+ }
314
+ else {
315
+ quotePrice = quoteOraclePriceData.price;
316
+ }
317
+ positionUnrealizedPnl = positionUnrealizedPnl
318
+ .mul(quotePrice)
319
+ .div(new _1.BN(numericConstants_1.PRICE_PRECISION));
306
320
  if (withWeightMarginCategory !== undefined) {
307
321
  if (positionUnrealizedPnl.gt(numericConstants_1.ZERO)) {
308
322
  positionUnrealizedPnl = positionUnrealizedPnl
@@ -340,31 +354,24 @@ class User {
340
354
  continue;
341
355
  }
342
356
  const spotMarketAccount = this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
357
+ const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
343
358
  if (spotPosition.marketIndex === numericConstants_1.QUOTE_SPOT_MARKET_INDEX &&
344
359
  countForQuote) {
360
+ const tokenAmount = (0, _1.getSignedTokenAmount)((0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType), spotPosition.balanceType);
345
361
  if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
346
- const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
347
- let weight = numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION;
348
- if (marginCategory === 'Initial') {
349
- weight = _1.BN.max(weight, new _1.BN(this.getUserAccount().maxMarginRatio));
350
- }
351
- const weightedTokenValue = tokenAmount
352
- .mul(weight)
353
- .div(numericConstants_1.SPOT_MARKET_WEIGHT_PRECISION);
362
+ const weightedTokenValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now).abs();
354
363
  netQuoteValue = netQuoteValue.sub(weightedTokenValue);
355
- continue;
356
364
  }
357
365
  else {
358
- const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
359
- netQuoteValue = netQuoteValue.add(tokenAmount);
360
- continue;
366
+ const weightedTokenValue = this.getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict, now);
367
+ netQuoteValue = netQuoteValue.add(weightedTokenValue);
361
368
  }
369
+ continue;
362
370
  }
363
- const oraclePriceData = this.getOracleDataForSpotMarket(spotPosition.marketIndex);
364
371
  if (!includeOpenOrders && countForBase) {
365
372
  if ((0, types_1.isVariant)(spotPosition.balanceType, 'borrow')) {
366
- const tokenAmount = (0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType);
367
- const liabilityValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now);
373
+ const tokenAmount = (0, _1.getSignedTokenAmount)((0, spotBalance_1.getTokenAmount)(spotPosition.scaledBalance, spotMarketAccount, spotPosition.balanceType), _1.SpotBalanceType.BORROW);
374
+ const liabilityValue = this.getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now).abs();
368
375
  totalLiabilityValue = totalLiabilityValue.add(liabilityValue);
369
376
  continue;
370
377
  }
@@ -381,7 +388,7 @@ class User {
381
388
  totalAssetValue = totalAssetValue.add(baseAssetValue);
382
389
  }
383
390
  if (worstCaseTokenAmount.lt(numericConstants_1.ZERO) && countForBase) {
384
- const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount.abs(), oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now);
391
+ const baseLiabilityValue = this.getSpotLiabilityValue(worstCaseTokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict, now).abs();
385
392
  totalLiabilityValue = totalLiabilityValue.add(baseLiabilityValue);
386
393
  }
387
394
  if (worstCaseQuoteTokenAmount.gt(numericConstants_1.ZERO) && countForQuote) {
@@ -416,7 +423,7 @@ class User {
416
423
  }
417
424
  getSpotLiabilityValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, liquidationBuffer, strict = false, now) {
418
425
  let liabilityValue = null;
419
- if (strict && spotMarketAccount.marketIndex != numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
426
+ if (strict) {
420
427
  const estOracleTwap = (0, oracles_1.calculateLiveOracleTwap)(spotMarketAccount.historicalOracleData, oraclePriceData, now, numericConstants_1.FIVE_MINUTE // 5MIN
421
428
  );
422
429
  liabilityValue = (0, _1.getStrictTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData, estOracleTwap);
@@ -444,7 +451,7 @@ class User {
444
451
  }
445
452
  getSpotAssetValue(tokenAmount, oraclePriceData, spotMarketAccount, marginCategory, strict = false, now) {
446
453
  let assetValue = null;
447
- if (strict && spotMarketAccount.marketIndex != numericConstants_1.QUOTE_SPOT_MARKET_INDEX) {
454
+ if (strict) {
448
455
  const estOracleTwap = (0, oracles_1.calculateLiveOracleTwap)(spotMarketAccount.historicalOracleData, oraclePriceData, now, numericConstants_1.FIVE_MINUTE // 5MIN
449
456
  );
450
457
  assetValue = (0, _1.getStrictTokenValue)(tokenAmount, spotMarketAccount.decimals, oraclePriceData, estOracleTwap);
@@ -474,8 +481,8 @@ class User {
474
481
  * calculates TotalCollateral: collateral + unrealized pnl
475
482
  * @returns : Precision QUOTE_PRECISION
476
483
  */
477
- getTotalCollateral(marginCategory = 'Initial') {
478
- return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
484
+ getTotalCollateral(marginCategory = 'Initial', strict = false) {
485
+ return this.getSpotMarketAssetValue(undefined, marginCategory, true, strict).add(this.getUnrealizedPNL(true, undefined, marginCategory, strict));
479
486
  }
480
487
  /**
481
488
  * calculates User Health by comparing total collateral and maint. margin requirement
@@ -513,7 +520,7 @@ class User {
513
520
  * calculates sum of position value across all positions in margin system
514
521
  * @returns : Precision QUOTE_PRECISION
515
522
  */
516
- getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders) {
523
+ getTotalPerpPositionValue(marginCategory, liquidationBuffer, includeOpenOrders, strict = false) {
517
524
  return this.getActivePerpPositions().reduce((totalPerpValue, perpPosition) => {
518
525
  const market = this.driftClient.getPerpMarketAccount(perpPosition.marketIndex);
519
526
  if (perpPosition.lpShares.gt(numericConstants_1.ZERO)) {
@@ -551,7 +558,18 @@ class User {
551
558
  if ((0, types_1.isVariant)(market.status, 'settlement')) {
552
559
  marginRatio = numericConstants_1.ZERO;
553
560
  }
561
+ const quoteSpotMarket = this.driftClient.getSpotMarketAccount(market.quoteSpotMarketIndex);
562
+ const quoteOraclePriceData = this.driftClient.getOraclePriceDataAndSlot(quoteSpotMarket.oracle).data;
563
+ let quotePrice;
564
+ if (strict) {
565
+ quotePrice = _1.BN.max(quoteOraclePriceData.price, quoteSpotMarket.historicalOracleData.lastOraclePriceTwap5Min);
566
+ }
567
+ else {
568
+ quotePrice = quoteOraclePriceData.price;
569
+ }
554
570
  baseAssetValue = baseAssetValue
571
+ .mul(quotePrice)
572
+ .div(numericConstants_1.PRICE_PRECISION)
555
573
  .mul(marginRatio)
556
574
  .div(numericConstants_1.MARGIN_PRECISION);
557
575
  if (includeOpenOrders) {
@@ -1218,7 +1236,8 @@ class User {
1218
1236
  return false;
1219
1237
  }
1220
1238
  const userLastActiveSlot = userAccount.lastActiveSlot;
1221
- if (userLastActiveSlot.lt(slotsBeforeIdle)) {
1239
+ const slotsSinceLastActive = slot.sub(userLastActiveSlot);
1240
+ if (slotsSinceLastActive.lt(slotsBeforeIdle)) {
1222
1241
  return false;
1223
1242
  }
1224
1243
  if (this.isBeingLiquidated()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.22.0",
3
+ "version": "2.23.0-beta.1",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -27,8 +27,8 @@ export const DevnetSpotMarkets: SpotMarketConfig[] = [
27
27
  {
28
28
  symbol: 'USDC',
29
29
  marketIndex: 0,
30
- oracle: PublicKey.default,
31
- oracleSource: OracleSource.QUOTE_ASSET,
30
+ oracle: new PublicKey('5SSkXsEKQepHHAewytPVwdej4epN1nxgLVM84L4KXgy7'),
31
+ oracleSource: OracleSource.PYTH_STABLE_COIN,
32
32
  mint: new PublicKey('8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2'),
33
33
  precision: new BN(10).pow(SIX),
34
34
  precisionExp: SIX,
@@ -59,8 +59,8 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
59
59
  {
60
60
  symbol: 'USDC',
61
61
  marketIndex: 0,
62
- oracle: PublicKey.default,
63
- oracleSource: OracleSource.QUOTE_ASSET,
62
+ oracle: new PublicKey('Gnt27xtC473ZT2Mw5u8wZ68Z3gULkSTb5DuxJy7eJotD'),
63
+ oracleSource: OracleSource.PYTH_STABLE_COIN,
64
64
  mint: new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v'),
65
65
  precision: QUOTE_PRECISION,
66
66
  precisionExp: QUOTE_PRECISION_EXP,
@@ -37,6 +37,7 @@ import {
37
37
  PostOnlyParams,
38
38
  SpotBalanceType,
39
39
  PerpMarketExtendedInfo,
40
+ UserStatsAccount,
40
41
  } from './types';
41
42
  import * as anchor from '@project-serum/anchor';
42
43
  import driftIDL from './idl/drift.json';
@@ -417,6 +418,7 @@ export class DriftClient {
417
418
  throw Error('Market lookup table address not set');
418
419
  }
419
420
 
421
+ // @ts-ignore
420
422
  return (await this.connection.getAddressLookupTable(this.marketLookupTable))
421
423
  .value;
422
424
  }
@@ -776,6 +778,24 @@ export class DriftClient {
776
778
  );
777
779
  }
778
780
 
781
+ public async getReferredUserStatsAccountsByReferrer(
782
+ referrer: PublicKey
783
+ ): Promise<UserStatsAccount[]> {
784
+ const programAccounts = await this.program.account.userStats.all([
785
+ {
786
+ memcmp: {
787
+ offset: 40,
788
+ /** data to match, as base-58 encoded string and limited to less than 129 bytes */
789
+ bytes: bs58.encode(referrer.toBuffer()),
790
+ },
791
+ },
792
+ ]);
793
+
794
+ return programAccounts.map(
795
+ (programAccount) => programAccount.account as UserStatsAccount
796
+ );
797
+ }
798
+
779
799
  public async getReferrerNameAccountsForAuthority(
780
800
  authority: PublicKey
781
801
  ): Promise<ReferrerNameAccount[]> {
@@ -934,17 +954,32 @@ export class DriftClient {
934
954
  // if cache has more recent slot than user positions account slot, add market to remaining accounts
935
955
  // otherwise remove from slot
936
956
  if (slot > lastUserSlot) {
937
- const marketAccount = this.getPerpMarketAccount(marketIndex);
957
+ const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
938
958
  perpMarketAccountMap.set(marketIndex, {
939
- pubkey: marketAccount.pubkey,
959
+ pubkey: perpMarketAccount.pubkey,
960
+ isSigner: false,
961
+ isWritable: false,
962
+ });
963
+ oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
964
+ pubkey: perpMarketAccount.amm.oracle,
940
965
  isSigner: false,
941
966
  isWritable: false,
942
967
  });
943
- oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
944
- pubkey: marketAccount.amm.oracle,
968
+ const spotMarketAccount = this.getSpotMarketAccount(
969
+ perpMarketAccount.quoteSpotMarketIndex
970
+ );
971
+ spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
972
+ pubkey: spotMarketAccount.pubkey,
945
973
  isSigner: false,
946
974
  isWritable: false,
947
975
  });
976
+ if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
977
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
978
+ pubkey: spotMarketAccount.oracle,
979
+ isSigner: false,
980
+ isWritable: false,
981
+ });
982
+ }
948
983
  } else {
949
984
  this.perpMarketLastSlotCache.delete(marketIndex);
950
985
  }
@@ -957,15 +992,15 @@ export class DriftClient {
957
992
  // if cache has more recent slot than user positions account slot, add market to remaining accounts
958
993
  // otherwise remove from slot
959
994
  if (slot > lastUserSlot) {
960
- const marketAccount = this.getSpotMarketAccount(marketIndex);
995
+ const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
961
996
  spotMarketAccountMap.set(marketIndex, {
962
- pubkey: marketAccount.pubkey,
997
+ pubkey: spotMarketAccount.pubkey,
963
998
  isSigner: false,
964
999
  isWritable: false,
965
1000
  });
966
- if (!marketAccount.oracle.equals(PublicKey.default)) {
967
- oracleAccountMap.set(marketAccount.oracle.toString(), {
968
- pubkey: marketAccount.oracle,
1001
+ if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1002
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1003
+ pubkey: spotMarketAccount.oracle,
969
1004
  isSigner: false,
970
1005
  isWritable: false,
971
1006
  });
@@ -977,50 +1012,80 @@ export class DriftClient {
977
1012
  }
978
1013
 
979
1014
  if (params.readablePerpMarketIndex !== undefined) {
980
- const marketAccount = this.getPerpMarketAccount(
1015
+ const perpMarketAccount = this.getPerpMarketAccount(
981
1016
  params.readablePerpMarketIndex
982
1017
  );
983
1018
  perpMarketAccountMap.set(params.readablePerpMarketIndex, {
984
- pubkey: marketAccount.pubkey,
1019
+ pubkey: perpMarketAccount.pubkey,
1020
+ isSigner: false,
1021
+ isWritable: false,
1022
+ });
1023
+ oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1024
+ pubkey: perpMarketAccount.amm.oracle,
985
1025
  isSigner: false,
986
1026
  isWritable: false,
987
1027
  });
988
- oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
989
- pubkey: marketAccount.amm.oracle,
1028
+ const spotMarketAccount = this.getSpotMarketAccount(
1029
+ perpMarketAccount.quoteSpotMarketIndex
1030
+ );
1031
+ spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1032
+ pubkey: spotMarketAccount.pubkey,
990
1033
  isSigner: false,
991
1034
  isWritable: false,
992
1035
  });
1036
+ if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1037
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1038
+ pubkey: spotMarketAccount.oracle,
1039
+ isSigner: false,
1040
+ isWritable: false,
1041
+ });
1042
+ }
1043
+ }
1044
+
1045
+ if (params.readableSpotMarketIndexes !== undefined) {
1046
+ for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
1047
+ const spotMarketAccount = this.getSpotMarketAccount(
1048
+ readableSpotMarketIndex
1049
+ );
1050
+ spotMarketAccountMap.set(readableSpotMarketIndex, {
1051
+ pubkey: spotMarketAccount.pubkey,
1052
+ isSigner: false,
1053
+ isWritable: false,
1054
+ });
1055
+ if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1056
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1057
+ pubkey: spotMarketAccount.oracle,
1058
+ isSigner: false,
1059
+ isWritable: false,
1060
+ });
1061
+ }
1062
+ }
993
1063
  }
994
1064
 
995
1065
  if (params.writablePerpMarketIndexes !== undefined) {
996
1066
  for (const writablePerpMarketIndex of params.writablePerpMarketIndexes) {
997
- const marketAccount = this.getPerpMarketAccount(
1067
+ const perpMarketAccount = this.getPerpMarketAccount(
998
1068
  writablePerpMarketIndex
999
1069
  );
1000
1070
  perpMarketAccountMap.set(writablePerpMarketIndex, {
1001
- pubkey: marketAccount.pubkey,
1071
+ pubkey: perpMarketAccount.pubkey,
1002
1072
  isSigner: false,
1003
1073
  isWritable: true,
1004
1074
  });
1005
- oracleAccountMap.set(marketAccount.amm.oracle.toString(), {
1006
- pubkey: marketAccount.amm.oracle,
1075
+ oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1076
+ pubkey: perpMarketAccount.amm.oracle,
1007
1077
  isSigner: false,
1008
1078
  isWritable: false,
1009
1079
  });
1010
- }
1011
- }
1012
-
1013
- if (params.readableSpotMarketIndexes !== undefined) {
1014
- for (const readableSpotMarketIndex of params.readableSpotMarketIndexes) {
1015
1080
  const spotMarketAccount = this.getSpotMarketAccount(
1016
- readableSpotMarketIndex
1081
+ perpMarketAccount.quoteSpotMarketIndex
1017
1082
  );
1018
- spotMarketAccountMap.set(readableSpotMarketIndex, {
1083
+ spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1019
1084
  pubkey: spotMarketAccount.pubkey,
1020
1085
  isSigner: false,
1021
1086
  isWritable: false,
1022
1087
  });
1023
- if (spotMarketAccount.marketIndex !== 0) {
1088
+ if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1024
1089
  oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1025
1090
  pubkey: spotMarketAccount.oracle,
1026
1091
  isSigner: false,
@@ -1090,27 +1155,52 @@ export class DriftClient {
1090
1155
  !spotPosition.openAsks.eq(ZERO) ||
1091
1156
  !spotPosition.openBids.eq(ZERO)
1092
1157
  ) {
1158
+ const quoteSpotMarket = this.getQuoteSpotMarketAccount();
1093
1159
  spotMarketAccountMap.set(QUOTE_SPOT_MARKET_INDEX, {
1094
- pubkey: this.getQuoteSpotMarketAccount().pubkey,
1160
+ pubkey: quoteSpotMarket.pubkey,
1095
1161
  isSigner: false,
1096
1162
  isWritable: false,
1097
1163
  });
1164
+ if (!quoteSpotMarket.oracle.equals(PublicKey.default)) {
1165
+ oracleAccountMap.set(quoteSpotMarket.oracle.toString(), {
1166
+ pubkey: quoteSpotMarket.oracle,
1167
+ isSigner: false,
1168
+ isWritable: false,
1169
+ });
1170
+ }
1098
1171
  }
1099
1172
  }
1100
1173
  }
1101
1174
  for (const position of userAccount.perpPositions) {
1102
1175
  if (!positionIsAvailable(position)) {
1103
- const market = this.getPerpMarketAccount(position.marketIndex);
1176
+ const perpMarketAccount = this.getPerpMarketAccount(
1177
+ position.marketIndex
1178
+ );
1104
1179
  perpMarketAccountMap.set(position.marketIndex, {
1105
- pubkey: market.pubkey,
1180
+ pubkey: perpMarketAccount.pubkey,
1106
1181
  isWritable: false,
1107
1182
  isSigner: false,
1108
1183
  });
1109
- oracleAccountMap.set(market.amm.oracle.toString(), {
1110
- pubkey: market.amm.oracle,
1184
+ oracleAccountMap.set(perpMarketAccount.amm.oracle.toString(), {
1185
+ pubkey: perpMarketAccount.amm.oracle,
1111
1186
  isWritable: false,
1112
1187
  isSigner: false,
1113
1188
  });
1189
+ const spotMarketAccount = this.getSpotMarketAccount(
1190
+ perpMarketAccount.quoteSpotMarketIndex
1191
+ );
1192
+ spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
1193
+ pubkey: spotMarketAccount.pubkey,
1194
+ isSigner: false,
1195
+ isWritable: false,
1196
+ });
1197
+ if (!spotMarketAccount.oracle.equals(PublicKey.default)) {
1198
+ oracleAccountMap.set(spotMarketAccount.oracle.toString(), {
1199
+ pubkey: spotMarketAccount.oracle,
1200
+ isSigner: false,
1201
+ isWritable: false,
1202
+ });
1203
+ }
1114
1204
  }
1115
1205
  }
1116
1206
  }
@@ -1143,6 +1233,7 @@ export class DriftClient {
1143
1233
  ): Promise<TransactionSignature> {
1144
1234
  const tx = new Transaction();
1145
1235
  tx.add(
1236
+ // @ts-ignore
1146
1237
  ComputeBudgetProgram.setComputeUnitLimit({
1147
1238
  units: 600_000,
1148
1239
  })
@@ -1368,6 +1459,7 @@ export class DriftClient {
1368
1459
  const tx = new Transaction();
1369
1460
 
1370
1461
  tx.add(
1462
+ // @ts-ignore
1371
1463
  ComputeBudgetProgram.setComputeUnitLimit({
1372
1464
  units: txParams?.computeUnits ?? 600_000,
1373
1465
  })
@@ -1375,6 +1467,7 @@ export class DriftClient {
1375
1467
 
1376
1468
  if (txParams?.computeUnitsPrice) {
1377
1469
  tx.add(
1470
+ // @ts-ignore
1378
1471
  ComputeBudgetProgram.setComputeUnitPrice({
1379
1472
  microLamports: txParams.computeUnitsPrice,
1380
1473
  })
@@ -1505,6 +1598,7 @@ export class DriftClient {
1505
1598
  ): Promise<TransactionSignature> {
1506
1599
  const tx = new Transaction();
1507
1600
  tx.add(
1601
+ // @ts-ignore
1508
1602
  ComputeBudgetProgram.setComputeUnitLimit({
1509
1603
  units: 600_000,
1510
1604
  })
@@ -3364,6 +3458,7 @@ export class DriftClient {
3364
3458
 
3365
3459
  const tx = new Transaction();
3366
3460
  tx.add(
3461
+ // @ts-ignore
3367
3462
  ComputeBudgetProgram.setComputeUnitLimit({
3368
3463
  units: 1_000_000,
3369
3464
  })
@@ -3475,6 +3570,7 @@ export class DriftClient {
3475
3570
 
3476
3571
  const tx = new Transaction();
3477
3572
  tx.add(
3573
+ // @ts-ignore
3478
3574
  ComputeBudgetProgram.setComputeUnitLimit({
3479
3575
  units: 1_000_000,
3480
3576
  })
@@ -3515,6 +3611,7 @@ export class DriftClient {
3515
3611
 
3516
3612
  const tx = new Transaction()
3517
3613
  .add(
3614
+ // @ts-ignore
3518
3615
  ComputeBudgetProgram.setComputeUnitLimit({
3519
3616
  units: 1_000_000,
3520
3617
  })
@@ -22,6 +22,10 @@ export function getOracleClient(
22
22
  return new PythClient(connection, new BN(1000000));
23
23
  }
24
24
 
25
+ if (isVariant(oracleSource, 'pythStableCoin')) {
26
+ return new PythClient(connection, undefined, true);
27
+ }
28
+
25
29
  // if (isVariant(oracleSource, 'switchboard')) {
26
30
  // return new SwitchboardClient(connection);
27
31
  // }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.22.0",
2
+ "version": "2.23.0-beta.1",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -4081,12 +4081,20 @@
4081
4081
  "defined": "ContractTier"
4082
4082
  }
4083
4083
  },
4084
+ {
4085
+ "name": "padding1",
4086
+ "type": "bool"
4087
+ },
4088
+ {
4089
+ "name": "quoteSpotMarketIndex",
4090
+ "type": "u16"
4091
+ },
4084
4092
  {
4085
4093
  "name": "padding",
4086
4094
  "type": {
4087
4095
  "array": [
4088
4096
  "u8",
4089
- 51
4097
+ 48
4090
4098
  ]
4091
4099
  }
4092
4100
  }
@@ -6383,6 +6391,9 @@
6383
6391
  },
6384
6392
  {
6385
6393
  "name": "Pyth1M"
6394
+ },
6395
+ {
6396
+ "name": "PythStableCoin"
6386
6397
  }
6387
6398
  ]
6388
6399
  }
@@ -2,15 +2,26 @@ import { parsePriceData } from '@pythnetwork/client';
2
2
  import { Connection, PublicKey } from '@solana/web3.js';
3
3
  import { OracleClient, OraclePriceData } from './types';
4
4
  import { BN } from '@project-serum/anchor';
5
- import { ONE, PRICE_PRECISION, TEN } from '../constants/numericConstants';
5
+ import {
6
+ ONE,
7
+ PRICE_PRECISION,
8
+ QUOTE_PRECISION,
9
+ TEN,
10
+ } from '../constants/numericConstants';
6
11
 
7
12
  export class PythClient implements OracleClient {
8
13
  private connection: Connection;
9
14
  private multiple: BN;
15
+ private stableCoin: boolean;
10
16
 
11
- public constructor(connection: Connection, multiple = ONE) {
17
+ public constructor(
18
+ connection: Connection,
19
+ multiple = ONE,
20
+ stableCoin = false
21
+ ) {
12
22
  this.connection = connection;
13
23
  this.multiple = multiple;
24
+ this.stableCoin = stableCoin;
14
25
  }
15
26
 
16
27
  public async getOraclePriceData(
@@ -22,18 +33,24 @@ export class PythClient implements OracleClient {
22
33
 
23
34
  public getOraclePriceDataFromBuffer(buffer: Buffer): OraclePriceData {
24
35
  const priceData = parsePriceData(buffer);
36
+ const confidence = convertPythPrice(
37
+ priceData.confidence,
38
+ priceData.exponent,
39
+ this.multiple
40
+ );
41
+ let price = convertPythPrice(
42
+ priceData.aggregate.price,
43
+ priceData.exponent,
44
+ this.multiple
45
+ );
46
+ if (this.stableCoin) {
47
+ price = getStableCoinPrice(price, confidence);
48
+ }
49
+
25
50
  return {
26
- price: convertPythPrice(
27
- priceData.aggregate.price,
28
- priceData.exponent,
29
- this.multiple
30
- ),
51
+ price,
31
52
  slot: new BN(priceData.lastSlot.toString()),
32
- confidence: convertPythPrice(
33
- priceData.confidence,
34
- priceData.exponent,
35
- this.multiple
36
- ),
53
+ confidence,
37
54
  twap: convertPythPrice(
38
55
  priceData.twap.value,
39
56
  priceData.exponent,
@@ -60,3 +77,12 @@ export function convertPythPrice(
60
77
  .mul(PRICE_PRECISION)
61
78
  .div(pythPrecision);
62
79
  }
80
+
81
+ const fiveBPS = new BN(500);
82
+ function getStableCoinPrice(price: BN, confidence: BN): BN {
83
+ if (price.sub(QUOTE_PRECISION).abs().lt(BN.min(confidence, fiveBPS))) {
84
+ return QUOTE_PRECISION;
85
+ } else {
86
+ return price;
87
+ }
88
+ }
package/src/tx/utils.ts CHANGED
@@ -14,6 +14,7 @@ export function wrapInTx(
14
14
  const tx = new Transaction();
15
15
  if (computeUnits != COMPUTE_UNITS_DEFAULT) {
16
16
  tx.add(
17
+ // @ts-ignore
17
18
  ComputeBudgetProgram.setComputeUnitLimit({
18
19
  units: computeUnits,
19
20
  })
@@ -22,6 +23,7 @@ export function wrapInTx(
22
23
 
23
24
  if (computeUnitsPrice != 0) {
24
25
  tx.add(
26
+ // @ts-ignore
25
27
  ComputeBudgetProgram.setComputeUnitPrice({
26
28
  microLamports: computeUnitsPrice,
27
29
  })
package/src/types.ts CHANGED
@@ -80,6 +80,7 @@ export class OracleSource {
80
80
  static readonly PYTH_1M = { pyth1M: {} };
81
81
  // static readonly SWITCHBOARD = { switchboard: {} };
82
82
  static readonly QUOTE_ASSET = { quoteAsset: {} };
83
+ static readonly PYTH_STABLE_COIN = { pythStableCoin: {} };
83
84
  }
84
85
 
85
86
  export class OrderType {
@@ -560,6 +561,7 @@ export type PerpMarketAccount = {
560
561
  quoteSettledInsurance: BN;
561
562
  quoteMaxInsurance: BN;
562
563
  };
564
+ quoteSpotMarketIndex: number;
563
565
  };
564
566
 
565
567
  export type HistoricalOracleData = {