@drift-labs/sdk 2.86.0-beta.2 → 2.86.0-beta.21

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/src/user.ts CHANGED
@@ -31,10 +31,12 @@ import {
31
31
  QUOTE_PRECISION_EXP,
32
32
  QUOTE_SPOT_MARKET_INDEX,
33
33
  SPOT_MARKET_WEIGHT_PRECISION,
34
+ GOV_SPOT_MARKET_INDEX,
34
35
  TEN,
35
36
  TEN_THOUSAND,
36
37
  TWO,
37
38
  ZERO,
39
+ FUEL_START_TS,
38
40
  } from './constants/numericConstants';
39
41
  import {
40
42
  DataAndSlot,
@@ -88,6 +90,12 @@ import { calculateLiveOracleTwap } from './math/oracles';
88
90
  import { getPerpMarketTierNumber, getSpotMarketTierNumber } from './math/tiers';
89
91
  import { StrictOraclePrice } from './oracles/strictOraclePrice';
90
92
 
93
+ import {
94
+ calculateSpotFuelBonus,
95
+ calculatePerpFuelBonus,
96
+ calculateInsuranceFuelBonus,
97
+ } from './math/fuel';
98
+
91
99
  export class User {
92
100
  driftClient: DriftClient;
93
101
  userAccountPublicKey: PublicKey;
@@ -873,6 +881,152 @@ export class User {
873
881
  }, ZERO);
874
882
  }
875
883
 
884
+ public getFuelBonus(
885
+ now: BN,
886
+ includeSettled = true,
887
+ includeUnsettled = true
888
+ ): {
889
+ depositFuel: BN;
890
+ borrowFuel: BN;
891
+ positionFuel: BN;
892
+ takerFuel: BN;
893
+ makerFuel: BN;
894
+ insuranceFuel: BN;
895
+ } {
896
+ const userAccount: UserAccount = this.getUserAccount();
897
+
898
+ const result = {
899
+ insuranceFuel: ZERO,
900
+ takerFuel: ZERO,
901
+ makerFuel: ZERO,
902
+ depositFuel: ZERO,
903
+ borrowFuel: ZERO,
904
+ positionFuel: ZERO,
905
+ };
906
+
907
+ if (includeSettled) {
908
+ const userStats: UserStatsAccount = this.driftClient
909
+ .getUserStats()
910
+ .getAccount();
911
+ result.insuranceFuel = result.insuranceFuel.add(
912
+ new BN(userStats.fuelInsurance)
913
+ );
914
+ result.takerFuel = result.takerFuel.add(new BN(userStats.fuelTaker));
915
+ result.makerFuel = result.makerFuel.add(new BN(userStats.fuelMaker));
916
+ result.depositFuel = result.depositFuel.add(
917
+ new BN(userStats.fuelDeposits)
918
+ );
919
+ result.borrowFuel = result.borrowFuel.add(new BN(userStats.fuelBorrows));
920
+ result.positionFuel = result.positionFuel.add(
921
+ new BN(userStats.fuelPositions)
922
+ );
923
+ }
924
+
925
+ if (includeUnsettled) {
926
+ const fuelBonusNumerator = BN.max(
927
+ now.sub(
928
+ BN.max(new BN(userAccount.lastFuelBonusUpdateTs), FUEL_START_TS)
929
+ ),
930
+ ZERO
931
+ );
932
+
933
+ if (fuelBonusNumerator.gt(ZERO)) {
934
+ for (const spotPosition of this.getActiveSpotPositions()) {
935
+ const spotMarketAccount: SpotMarketAccount =
936
+ this.driftClient.getSpotMarketAccount(spotPosition.marketIndex);
937
+
938
+ const tokenAmount = this.getTokenAmount(spotPosition.marketIndex);
939
+ const oraclePriceData = this.getOracleDataForSpotMarket(
940
+ spotPosition.marketIndex
941
+ );
942
+
943
+ const twap5min = calculateLiveOracleTwap(
944
+ spotMarketAccount.historicalOracleData,
945
+ oraclePriceData,
946
+ now,
947
+ FIVE_MINUTE // 5MIN
948
+ );
949
+ const strictOraclePrice = new StrictOraclePrice(
950
+ oraclePriceData.price,
951
+ twap5min
952
+ );
953
+
954
+ const signedTokenValue = getStrictTokenValue(
955
+ tokenAmount,
956
+ spotMarketAccount.decimals,
957
+ strictOraclePrice
958
+ );
959
+
960
+ if (signedTokenValue.gt(ZERO)) {
961
+ result.depositFuel = result.depositFuel.add(
962
+ calculateSpotFuelBonus(
963
+ spotMarketAccount,
964
+ signedTokenValue,
965
+ fuelBonusNumerator
966
+ )
967
+ );
968
+ } else {
969
+ result.borrowFuel = result.borrowFuel.add(
970
+ calculateSpotFuelBonus(
971
+ spotMarketAccount,
972
+ signedTokenValue,
973
+ fuelBonusNumerator
974
+ )
975
+ );
976
+ }
977
+ }
978
+
979
+ for (const perpPosition of this.getActivePerpPositions()) {
980
+ const oraclePriceData = this.getOracleDataForPerpMarket(
981
+ perpPosition.marketIndex
982
+ );
983
+
984
+ const perpMarketAccount = this.driftClient.getPerpMarketAccount(
985
+ perpPosition.marketIndex
986
+ );
987
+
988
+ const baseAssetValue = this.getPerpPositionValue(
989
+ perpPosition.marketIndex,
990
+ oraclePriceData,
991
+ false
992
+ );
993
+
994
+ result.positionFuel = result.positionFuel.add(
995
+ calculatePerpFuelBonus(
996
+ perpMarketAccount,
997
+ baseAssetValue,
998
+ fuelBonusNumerator
999
+ )
1000
+ );
1001
+ }
1002
+ }
1003
+
1004
+ const userStats: UserStatsAccount = this.driftClient
1005
+ .getUserStats()
1006
+ .getAccount();
1007
+
1008
+ // todo: get real time ifStakedGovTokenAmount using ifStakeAccount
1009
+ if (userStats.ifStakedGovTokenAmount.gt(ZERO)) {
1010
+ const spotMarketAccount: SpotMarketAccount =
1011
+ this.driftClient.getSpotMarketAccount(GOV_SPOT_MARKET_INDEX);
1012
+
1013
+ const fuelBonusNumeratorUserStats = now.sub(
1014
+ new BN(userStats.lastFuelBonusUpdateTs)
1015
+ );
1016
+
1017
+ result.insuranceFuel = result.insuranceFuel.add(
1018
+ calculateInsuranceFuelBonus(
1019
+ spotMarketAccount,
1020
+ userStats.ifStakedGovTokenAmount,
1021
+ fuelBonusNumeratorUserStats
1022
+ )
1023
+ );
1024
+ }
1025
+ }
1026
+
1027
+ return result;
1028
+ }
1029
+
876
1030
  public getSpotMarketAssetAndLiabilityValue(
877
1031
  marketIndex?: number,
878
1032
  marginCategory?: MarginCategory,
@@ -0,0 +1,214 @@
1
+ import {
2
+ DriftClient,
3
+ DevnetSpotMarkets,
4
+ MainnetSpotMarkets,
5
+ DevnetPerpMarkets,
6
+ MainnetPerpMarkets,
7
+ BulkAccountLoader,
8
+ getVariant,
9
+ } from '../../src';
10
+ import { Connection, Keypair } from '@solana/web3.js';
11
+ import { Wallet } from '@coral-xyz/anchor';
12
+ import dotenv from 'dotenv';
13
+ import { assert } from 'chai';
14
+
15
+ dotenv.config();
16
+
17
+ describe('Verify Constants', function () {
18
+ this.timeout(100_000);
19
+ const MAINNET_RPC_ENDPOINT = process.env.MAINNET_RPC_ENDPOINT;
20
+ const DEVNET_RPC_ENDPOINT = process.env.DEVNET_RPC_ENDPOINT;
21
+
22
+ if (MAINNET_RPC_ENDPOINT === undefined) {
23
+ throw new Error("MAINNET_RPC_ENDPOINT not found in .env");
24
+ }
25
+
26
+ if (DEVNET_RPC_ENDPOINT === undefined) {
27
+ throw new Error("DEVNET_RPC_ENDPOINT not found in .env");
28
+ }
29
+
30
+ const wallet = new Wallet(Keypair.generate());
31
+
32
+ const devnetConnection = new Connection(DEVNET_RPC_ENDPOINT);
33
+ const mainnetConnection = new Connection(MAINNET_RPC_ENDPOINT);
34
+
35
+ const devnetBulkAccountLoader = new BulkAccountLoader(
36
+ devnetConnection,
37
+ 'processed',
38
+ 1
39
+ );
40
+
41
+ const mainnetBulkAccountLoader = new BulkAccountLoader(
42
+ mainnetConnection,
43
+ 'processed',
44
+ 1
45
+ );
46
+
47
+ const devnetDriftClient = new DriftClient({
48
+ connection: devnetConnection,
49
+ wallet,
50
+ env: 'devnet',
51
+ accountSubscription: {
52
+ type: 'polling',
53
+ accountLoader: devnetBulkAccountLoader,
54
+ },
55
+ });
56
+
57
+ const mainnetDriftClient = new DriftClient({
58
+ connection: mainnetConnection,
59
+ wallet,
60
+ env: 'mainnet-beta',
61
+ accountSubscription: {
62
+ type: 'polling',
63
+ accountLoader: mainnetBulkAccountLoader,
64
+ },
65
+ });
66
+
67
+ before(async () => {
68
+ await devnetDriftClient.subscribe();
69
+ await mainnetDriftClient.subscribe();
70
+ });
71
+
72
+ after(async () => {
73
+ await devnetDriftClient.unsubscribe();
74
+ await mainnetDriftClient.unsubscribe();
75
+ });
76
+
77
+ it('has all mainnet markets', async () => {
78
+ const spotMarkets = mainnetDriftClient.getSpotMarketAccounts();
79
+ spotMarkets.sort((a, b) => a.marketIndex - b.marketIndex);
80
+
81
+ for (const market of spotMarkets) {
82
+ const correspondingConfigMarket = MainnetSpotMarkets.find(
83
+ (configMarket) => configMarket.marketIndex === market.marketIndex
84
+ );
85
+ assert(
86
+ correspondingConfigMarket !== undefined,
87
+ `Market ${
88
+ market.marketIndex
89
+ } not found in MainnetSpotMarkets. market: ${market.pubkey.toBase58()}`
90
+ );
91
+ assert(
92
+ correspondingConfigMarket.oracle.toBase58() == market.oracle.toBase58(),
93
+ `Oracle mismatch for mainnet spot market ${
94
+ market.marketIndex
95
+ }, market: ${market.pubkey.toBase58()}, constants: ${correspondingConfigMarket.oracle.toBase58()}, chain: ${market.oracle.toBase58()}`
96
+ );
97
+ assert(
98
+ getVariant(correspondingConfigMarket.oracleSource) ===
99
+ getVariant(market.oracleSource),
100
+ `Oracle source mismatch for mainnet spot market ${
101
+ market.marketIndex
102
+ }, market: ${market.pubkey.toBase58()}, constants: ${getVariant(
103
+ correspondingConfigMarket.oracleSource
104
+ )}, chain: ${getVariant(market.oracleSource)}`
105
+ );
106
+ assert(
107
+ correspondingConfigMarket.mint.toBase58() === market.mint.toBase58(),
108
+ `Mint mismatch for mainnet spot market ${
109
+ market.marketIndex
110
+ }, market: ${market.pubkey.toBase58()}, constants: ${correspondingConfigMarket.mint.toBase58()}, chain: ${market.mint.toBase58()}`
111
+ );
112
+ }
113
+
114
+ const perpMarkets = mainnetDriftClient.getPerpMarketAccounts();
115
+ perpMarkets.sort((a, b) => a.marketIndex - b.marketIndex);
116
+
117
+ for (const market of perpMarkets) {
118
+ const correspondingConfigMarket = MainnetPerpMarkets.find(
119
+ (configMarket) => configMarket.marketIndex === market.marketIndex
120
+ );
121
+ assert(
122
+ correspondingConfigMarket !== undefined,
123
+ `Market ${
124
+ market.marketIndex
125
+ } not found in MainnetPerpMarkets, market: ${market.pubkey.toBase58()}`
126
+ );
127
+ assert(
128
+ correspondingConfigMarket.oracle.toBase58() ==
129
+ market.amm.oracle.toBase58(),
130
+ `Oracle mismatch for mainnet perp market ${
131
+ market.marketIndex
132
+ }, market: ${market.pubkey.toBase58()}, constants: ${correspondingConfigMarket.oracle.toBase58()}, chain: ${market.amm.oracle.toBase58()}`
133
+ );
134
+ assert(
135
+ getVariant(correspondingConfigMarket.oracleSource) ===
136
+ getVariant(market.amm.oracleSource),
137
+ `Oracle source mismatch for mainnet perp market ${
138
+ market.marketIndex
139
+ }, market: ${market.pubkey.toBase58()}, constants: ${getVariant(
140
+ correspondingConfigMarket.oracleSource
141
+ )}, chain: ${getVariant(market.amm.oracleSource)}`
142
+ );
143
+ }
144
+ });
145
+
146
+ it('has all devnet markets', async () => {
147
+ const spotMarkets = devnetDriftClient.getSpotMarketAccounts();
148
+ spotMarkets.sort((a, b) => a.marketIndex - b.marketIndex);
149
+
150
+ for (const market of spotMarkets) {
151
+ const correspondingConfigMarket = DevnetSpotMarkets.find(
152
+ (configMarket) => configMarket.marketIndex === market.marketIndex
153
+ );
154
+ assert(
155
+ correspondingConfigMarket !== undefined,
156
+ `Market ${
157
+ market.marketIndex
158
+ } not found in DevnetSpotMarkets, market: ${market.pubkey.toBase58()}`
159
+ );
160
+ assert(
161
+ correspondingConfigMarket.oracle.toBase58() == market.oracle.toBase58(),
162
+ `Oracle mismatch for devnet spot market ${
163
+ market.marketIndex
164
+ }, market: ${market.pubkey.toBase58()}, constants: ${correspondingConfigMarket.oracle.toBase58()}, chain: ${market.oracle.toBase58()}`
165
+ );
166
+ assert(
167
+ getVariant(correspondingConfigMarket.oracleSource) ===
168
+ getVariant(market.oracleSource),
169
+ `Oracle source mismatch for devnet spot market ${
170
+ market.marketIndex
171
+ }, market: ${market.pubkey.toBase58()}, constants: ${getVariant(
172
+ correspondingConfigMarket.oracleSource
173
+ )}, chain: ${getVariant(market.oracleSource)}`
174
+ );
175
+ assert(
176
+ correspondingConfigMarket.mint.toBase58() === market.mint.toBase58(),
177
+ `Mint mismatch for devnet spot market ${
178
+ market.marketIndex
179
+ }, market: ${market.pubkey.toBase58()}, constants: ${correspondingConfigMarket.mint.toBase58()}, chain: ${market.mint.toBase58()}`
180
+ );
181
+ }
182
+
183
+ const perpMarkets = devnetDriftClient.getPerpMarketAccounts();
184
+ perpMarkets.sort((a, b) => a.marketIndex - b.marketIndex);
185
+
186
+ for (const market of perpMarkets) {
187
+ const correspondingConfigMarket = DevnetPerpMarkets.find(
188
+ (configMarket) => configMarket.marketIndex === market.marketIndex
189
+ );
190
+ assert(
191
+ correspondingConfigMarket !== undefined,
192
+ `Market ${
193
+ market.marketIndex
194
+ } not found in DevnetPerpMarkets, market: ${market.pubkey.toBase58()}`
195
+ );
196
+ assert(
197
+ correspondingConfigMarket.oracle.toBase58() ==
198
+ market.amm.oracle.toBase58(),
199
+ `Oracle mismatch for devnet perp market ${
200
+ market.marketIndex
201
+ }, market: ${market.pubkey.toBase58()}, constants: ${correspondingConfigMarket.oracle.toBase58()}, chain: ${market.amm.oracle.toBase58()}`
202
+ );
203
+ assert(
204
+ getVariant(correspondingConfigMarket.oracleSource) ===
205
+ getVariant(market.amm.oracleSource),
206
+ `Oracle source mismatch for devnet perp market ${
207
+ market.marketIndex
208
+ }, market: ${market.pubkey.toBase58()}, constants: ${getVariant(
209
+ correspondingConfigMarket.oracleSource
210
+ )}, chain: ${getVariant(market.amm.oracleSource)}`
211
+ );
212
+ }
213
+ });
214
+ });
@@ -187,6 +187,9 @@ export const mockPerpMarkets: Array<PerpMarketAccount> = [
187
187
  quoteSpotMarketIndex: 0,
188
188
  feeAdjustment: 0,
189
189
  pausedOperations: 0,
190
+ fuelBoostPosition: 0,
191
+ fuelBoostMaker: 0,
192
+ fuelBoostTaker: 0,
190
193
  },
191
194
  {
192
195
  status: MarketStatus.INITIALIZED,
@@ -226,6 +229,9 @@ export const mockPerpMarkets: Array<PerpMarketAccount> = [
226
229
  quoteSpotMarketIndex: 0,
227
230
  feeAdjustment: 0,
228
231
  pausedOperations: 0,
232
+ fuelBoostPosition: 0,
233
+ fuelBoostMaker: 0,
234
+ fuelBoostTaker: 0,
229
235
  },
230
236
  {
231
237
  status: MarketStatus.INITIALIZED,
@@ -265,6 +271,9 @@ export const mockPerpMarkets: Array<PerpMarketAccount> = [
265
271
  quoteSpotMarketIndex: 0,
266
272
  feeAdjustment: 0,
267
273
  pausedOperations: 0,
274
+ fuelBoostPosition: 0,
275
+ fuelBoostMaker: 0,
276
+ fuelBoostTaker: 0,
268
277
  },
269
278
  ];
270
279
 
@@ -351,6 +360,13 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
351
360
  },
352
361
  pausedOperations: 0,
353
362
  ifPausedOperations: 0,
363
+ maxTokenBorrowsFraction: 0,
364
+ minBorrowRate: 0,
365
+ fuelBoostDeposits: 0,
366
+ fuelBoostBorrows: 0,
367
+ fuelBoostTaker: 0,
368
+ fuelBoostMaker: 0,
369
+ fuelBoostInsurance: 0,
354
370
  },
355
371
  {
356
372
  status: MarketStatus.ACTIVE,
@@ -434,6 +450,13 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
434
450
  },
435
451
  pausedOperations: 0,
436
452
  ifPausedOperations: 0,
453
+ maxTokenBorrowsFraction: 0,
454
+ minBorrowRate: 0,
455
+ fuelBoostDeposits: 0,
456
+ fuelBoostBorrows: 0,
457
+ fuelBoostTaker: 0,
458
+ fuelBoostMaker: 0,
459
+ fuelBoostInsurance: 0,
437
460
  },
438
461
  {
439
462
  status: MarketStatus.ACTIVE,
@@ -517,6 +540,13 @@ export const mockSpotMarkets: Array<SpotMarketAccount> = [
517
540
  },
518
541
  pausedOperations: 0,
519
542
  ifPausedOperations: 0,
543
+ maxTokenBorrowsFraction: 0,
544
+ minBorrowRate: 0,
545
+ fuelBoostDeposits: 0,
546
+ fuelBoostBorrows: 0,
547
+ fuelBoostTaker: 0,
548
+ fuelBoostMaker: 0,
549
+ fuelBoostInsurance: 0,
520
550
  },
521
551
  ];
522
552
 
@@ -85,4 +85,5 @@ export const mockUserAccount: UserAccount = {
85
85
  hasOpenOrder: false,
86
86
  openAuctions: 0,
87
87
  hasOpenAuction: false,
88
+ lastFuelBonusUpdateTs: 0,
88
89
  };
@@ -35,6 +35,8 @@ async function makeMockUser(
35
35
  const mockUser: User = await umap.mustGet('1');
36
36
  mockUser._isSubscribed = true;
37
37
  mockUser.driftClient._isSubscribed = true;
38
+ mockUser.driftClient.accountSubscriber.isSubscribed = true;
39
+
38
40
  const oraclePriceMap = {};
39
41
  // console.log(perpOraclePriceList, myMockPerpMarkets.length);
40
42
  // console.log(spotOraclePriceList, myMockSpotMarkets.length);