@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/constants/spotMarkets.js +4 -4
- package/lib/driftClient.d.ts +2 -1
- package/lib/driftClient.js +123 -36
- package/lib/factory/oracleClient.js +3 -0
- package/lib/idl/drift.json +13 -2
- package/lib/oracles/pythClient.d.ts +2 -1
- package/lib/oracles/pythClient.js +18 -3
- package/lib/tx/utils.js +6 -2
- package/lib/types.d.ts +4 -0
- package/lib/types.js +1 -0
- package/lib/user.d.ts +3 -3
- package/lib/user.js +44 -25
- package/package.json +1 -1
- package/src/constants/spotMarkets.ts +4 -4
- package/src/driftClient.ts +127 -30
- package/src/factory/oracleClient.ts +4 -0
- package/src/idl/drift.json +13 -2
- package/src/oracles/pythClient.ts +38 -12
- package/src/tx/utils.ts +2 -0
- package/src/types.ts +2 -0
- package/src/user.ts +104 -44
- package/tests/dlob/helpers.ts +3 -0
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
|
|
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
|
|
359
|
-
netQuoteValue = netQuoteValue.add(
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
@@ -27,8 +27,8 @@ export const DevnetSpotMarkets: SpotMarketConfig[] = [
|
|
|
27
27
|
{
|
|
28
28
|
symbol: 'USDC',
|
|
29
29
|
marketIndex: 0,
|
|
30
|
-
oracle: PublicKey
|
|
31
|
-
oracleSource: OracleSource.
|
|
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
|
|
63
|
-
oracleSource: OracleSource.
|
|
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,
|
package/src/driftClient.ts
CHANGED
|
@@ -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
|
|
957
|
+
const perpMarketAccount = this.getPerpMarketAccount(marketIndex);
|
|
938
958
|
perpMarketAccountMap.set(marketIndex, {
|
|
939
|
-
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
|
-
|
|
944
|
-
|
|
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
|
|
995
|
+
const spotMarketAccount = this.getSpotMarketAccount(marketIndex);
|
|
961
996
|
spotMarketAccountMap.set(marketIndex, {
|
|
962
|
-
pubkey:
|
|
997
|
+
pubkey: spotMarketAccount.pubkey,
|
|
963
998
|
isSigner: false,
|
|
964
999
|
isWritable: false,
|
|
965
1000
|
});
|
|
966
|
-
if (!
|
|
967
|
-
oracleAccountMap.set(
|
|
968
|
-
pubkey:
|
|
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
|
|
1015
|
+
const perpMarketAccount = this.getPerpMarketAccount(
|
|
981
1016
|
params.readablePerpMarketIndex
|
|
982
1017
|
);
|
|
983
1018
|
perpMarketAccountMap.set(params.readablePerpMarketIndex, {
|
|
984
|
-
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
|
-
|
|
989
|
-
|
|
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
|
|
1067
|
+
const perpMarketAccount = this.getPerpMarketAccount(
|
|
998
1068
|
writablePerpMarketIndex
|
|
999
1069
|
);
|
|
1000
1070
|
perpMarketAccountMap.set(writablePerpMarketIndex, {
|
|
1001
|
-
pubkey:
|
|
1071
|
+
pubkey: perpMarketAccount.pubkey,
|
|
1002
1072
|
isSigner: false,
|
|
1003
1073
|
isWritable: true,
|
|
1004
1074
|
});
|
|
1005
|
-
oracleAccountMap.set(
|
|
1006
|
-
pubkey:
|
|
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
|
-
|
|
1081
|
+
perpMarketAccount.quoteSpotMarketIndex
|
|
1017
1082
|
);
|
|
1018
|
-
spotMarketAccountMap.set(
|
|
1083
|
+
spotMarketAccountMap.set(perpMarketAccount.quoteSpotMarketIndex, {
|
|
1019
1084
|
pubkey: spotMarketAccount.pubkey,
|
|
1020
1085
|
isSigner: false,
|
|
1021
1086
|
isWritable: false,
|
|
1022
1087
|
});
|
|
1023
|
-
if (spotMarketAccount.
|
|
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:
|
|
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
|
|
1176
|
+
const perpMarketAccount = this.getPerpMarketAccount(
|
|
1177
|
+
position.marketIndex
|
|
1178
|
+
);
|
|
1104
1179
|
perpMarketAccountMap.set(position.marketIndex, {
|
|
1105
|
-
pubkey:
|
|
1180
|
+
pubkey: perpMarketAccount.pubkey,
|
|
1106
1181
|
isWritable: false,
|
|
1107
1182
|
isSigner: false,
|
|
1108
1183
|
});
|
|
1109
|
-
oracleAccountMap.set(
|
|
1110
|
-
pubkey:
|
|
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
|
// }
|
package/src/idl/drift.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2.
|
|
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
|
-
|
|
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 {
|
|
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(
|
|
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
|
|
27
|
-
priceData.aggregate.price,
|
|
28
|
-
priceData.exponent,
|
|
29
|
-
this.multiple
|
|
30
|
-
),
|
|
51
|
+
price,
|
|
31
52
|
slot: new BN(priceData.lastSlot.toString()),
|
|
32
|
-
confidence
|
|
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 = {
|