@drift-labs/sdk 2.25.4 → 2.26.0-beta.0

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.
Files changed (90) hide show
  1. package/lib/accounts/pollingUserAccountSubscriber.d.ts +5 -4
  2. package/lib/accounts/pollingUserAccountSubscriber.js +35 -48
  3. package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +4 -4
  4. package/lib/accounts/pollingUserStatsAccountSubscriber.js +30 -47
  5. package/lib/accounts/types.d.ts +2 -1
  6. package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
  7. package/lib/accounts/webSocketAccountSubscriber.js +4 -3
  8. package/lib/accounts/webSocketUserAccountSubscriber.d.ts +1 -0
  9. package/lib/accounts/webSocketUserAccountSubscriber.js +9 -0
  10. package/lib/addresses/pda.d.ts +2 -0
  11. package/lib/addresses/pda.js +8 -1
  12. package/lib/adminClient.d.ts +2 -0
  13. package/lib/adminClient.js +20 -0
  14. package/lib/constants/numericConstants.d.ts +61 -59
  15. package/lib/constants/spotMarkets.d.ts +1 -0
  16. package/lib/dlob/DLOB.d.ts +1 -0
  17. package/lib/dlob/DLOBApiClient.d.ts +14 -0
  18. package/lib/dlob/DLOBApiClient.js +34 -0
  19. package/lib/dlob/DLOBNode.d.ts +1 -0
  20. package/lib/dlob/DLOBSubscriber.d.ts +19 -0
  21. package/lib/dlob/DLOBSubscriber.js +42 -0
  22. package/lib/dlob/NodeList.d.ts +1 -0
  23. package/lib/dlob/types.d.ts +16 -0
  24. package/lib/dlob/types.js +2 -0
  25. package/lib/driftClient.d.ts +14 -10
  26. package/lib/driftClient.js +124 -28
  27. package/lib/examples/loadDlob.js +2 -4
  28. package/lib/factory/bigNum.d.ts +8 -7
  29. package/lib/idl/drift.json +264 -82
  30. package/lib/index.d.ts +2 -0
  31. package/lib/index.js +2 -0
  32. package/lib/math/amm.d.ts +2 -1
  33. package/lib/math/auction.d.ts +1 -0
  34. package/lib/math/conversion.d.ts +2 -1
  35. package/lib/math/funding.d.ts +1 -0
  36. package/lib/math/insurance.d.ts +1 -0
  37. package/lib/math/margin.d.ts +1 -0
  38. package/lib/math/market.d.ts +1 -0
  39. package/lib/math/oracles.d.ts +1 -0
  40. package/lib/math/orders.d.ts +1 -0
  41. package/lib/math/position.d.ts +1 -0
  42. package/lib/math/repeg.d.ts +1 -0
  43. package/lib/math/spotBalance.d.ts +1 -0
  44. package/lib/math/spotMarket.d.ts +1 -0
  45. package/lib/math/spotPosition.d.ts +1 -0
  46. package/lib/math/trade.d.ts +1 -0
  47. package/lib/math/utils.d.ts +1 -0
  48. package/lib/oracles/pythClient.d.ts +2 -1
  49. package/lib/oracles/types.d.ts +1 -0
  50. package/lib/orderParams.d.ts +1 -0
  51. package/lib/serum/serumSubscriber.d.ts +1 -0
  52. package/lib/slot/SlotSubscriber.js +3 -0
  53. package/lib/tokenFaucet.d.ts +1 -0
  54. package/lib/tx/retryTxSender.d.ts +2 -1
  55. package/lib/tx/retryTxSender.js +5 -1
  56. package/lib/tx/types.d.ts +4 -1
  57. package/lib/types.d.ts +12 -0
  58. package/lib/user.d.ts +1 -0
  59. package/lib/userMap/userMap.d.ts +17 -6
  60. package/lib/userMap/userMap.js +74 -46
  61. package/lib/userMap/userStatsMap.d.ts +6 -3
  62. package/lib/userMap/userStatsMap.js +30 -30
  63. package/package.json +2 -1
  64. package/src/accounts/pollingUserAccountSubscriber.ts +51 -63
  65. package/src/accounts/pollingUserStatsAccountSubscriber.ts +46 -62
  66. package/src/accounts/types.ts +2 -1
  67. package/src/accounts/webSocketAccountSubscriber.ts +4 -3
  68. package/src/accounts/webSocketUserAccountSubscriber.ts +10 -0
  69. package/src/addresses/pda.ts +13 -0
  70. package/src/adminClient.ts +34 -0
  71. package/src/dlob/DLOBApiClient.ts +37 -0
  72. package/src/dlob/DLOBSubscriber.ts +57 -0
  73. package/src/dlob/types.ts +20 -0
  74. package/src/driftClient.ts +216 -59
  75. package/src/examples/loadDlob.ts +2 -4
  76. package/src/idl/drift.json +264 -82
  77. package/src/index.ts +2 -0
  78. package/src/slot/SlotSubscriber.ts +4 -0
  79. package/src/tx/retryTxSender.ts +19 -2
  80. package/src/tx/types.ts +13 -0
  81. package/src/types.ts +12 -0
  82. package/src/userMap/userMap.ts +111 -60
  83. package/src/userMap/userStatsMap.ts +43 -44
  84. package/tests/dlob/helpers.ts +3 -1
  85. package/src/assert/assert.js +0 -9
  86. package/src/token/index.js +0 -38
  87. package/src/util/computeUnits.js +0 -27
  88. package/src/util/getTokenAddress.js +0 -9
  89. package/src/util/promiseTimeout.js +0 -14
  90. package/src/util/tps.js +0 -27
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  DataAndSlot,
3
- AccountToPoll,
4
3
  NotSubscribedError,
5
4
  UserStatsAccountSubscriber,
6
5
  UserStatsAccountEvents,
@@ -11,7 +10,6 @@ import { EventEmitter } from 'events';
11
10
  import { PublicKey } from '@solana/web3.js';
12
11
  import { UserStatsAccount } from '../types';
13
12
  import { BulkAccountLoader } from './bulkAccountLoader';
14
- import { capitalize } from './utils';
15
13
 
16
14
  export class PollingUserStatsAccountSubscriber
17
15
  implements UserStatsAccountSubscriber
@@ -22,7 +20,7 @@ export class PollingUserStatsAccountSubscriber
22
20
  userStatsAccountPublicKey: PublicKey;
23
21
 
24
22
  accountLoader: BulkAccountLoader;
25
- accountsToPoll = new Map<string, AccountToPoll>();
23
+ callbackId?: string;
26
24
  errorCallbackId?: string;
27
25
 
28
26
  userStats?: DataAndSlot<UserStatsAccount>;
@@ -39,16 +37,20 @@ export class PollingUserStatsAccountSubscriber
39
37
  this.userStatsAccountPublicKey = userStatsAccountPublicKey;
40
38
  }
41
39
 
42
- async subscribe(): Promise<boolean> {
40
+ async subscribe(userStatsAccount?: UserStatsAccount): Promise<boolean> {
43
41
  if (this.isSubscribed) {
44
42
  return true;
45
43
  }
46
44
 
45
+ if (userStatsAccount) {
46
+ this.userStats = { data: userStatsAccount, slot: undefined };
47
+ }
48
+
47
49
  await this.addToAccountLoader();
48
50
 
49
51
  await this.fetchIfUnloaded();
50
52
 
51
- if (this.doAccountsExist()) {
53
+ if (this.doesAccountExist()) {
52
54
  this.eventEmitter.emit('update');
53
55
  }
54
56
 
@@ -57,34 +59,30 @@ export class PollingUserStatsAccountSubscriber
57
59
  }
58
60
 
59
61
  async addToAccountLoader(): Promise<void> {
60
- if (this.accountsToPoll.size > 0) {
62
+ if (this.callbackId !== undefined) {
61
63
  return;
62
64
  }
63
65
 
64
- this.accountsToPoll.set(this.userStatsAccountPublicKey.toString(), {
65
- key: 'userStats',
66
- publicKey: this.userStatsAccountPublicKey,
67
- eventType: 'userStatsAccountUpdate',
68
- });
66
+ this.callbackId = await this.accountLoader.addAccount(
67
+ this.userStatsAccountPublicKey,
68
+ (buffer, slot: number) => {
69
+ if (!buffer) {
70
+ return;
71
+ }
69
72
 
70
- for (const [_, accountToPoll] of this.accountsToPoll) {
71
- accountToPoll.callbackId = await this.accountLoader.addAccount(
72
- accountToPoll.publicKey,
73
- (buffer, slot) => {
74
- if (!buffer) {
75
- return;
76
- }
77
-
78
- const account = this.program.account[
79
- accountToPoll.key
80
- ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
81
- this[accountToPoll.key] = { data: account, slot };
82
- // @ts-ignore
83
- this.eventEmitter.emit(accountToPoll.eventType, account);
84
- this.eventEmitter.emit('update');
73
+ if (this.userStats && this.userStats.slot > slot) {
74
+ return;
85
75
  }
86
- );
87
- }
76
+
77
+ const account = this.program.account.userStats.coder.accounts.decode(
78
+ 'UserStats',
79
+ buffer
80
+ );
81
+ this.userStats = { data: account, slot };
82
+ this.eventEmitter.emit('userStatsAccountUpdate', account);
83
+ this.eventEmitter.emit('update');
84
+ }
85
+ );
88
86
 
89
87
  this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
90
88
  this.eventEmitter.emit('error', error);
@@ -92,58 +90,44 @@ export class PollingUserStatsAccountSubscriber
92
90
  }
93
91
 
94
92
  async fetchIfUnloaded(): Promise<void> {
95
- let shouldFetch = false;
96
- for (const [_, accountToPoll] of this.accountsToPoll) {
97
- if (this[accountToPoll.key] === undefined) {
98
- shouldFetch = true;
99
- break;
100
- }
101
- }
102
-
103
- if (shouldFetch) {
93
+ if (this.userStats === undefined) {
104
94
  await this.fetch();
105
95
  }
106
96
  }
107
97
 
108
98
  async fetch(): Promise<void> {
109
99
  await this.accountLoader.load();
110
- for (const [_, accountToPoll] of this.accountsToPoll) {
111
- const { buffer, slot } = this.accountLoader.getBufferAndSlot(
112
- accountToPoll.publicKey
100
+ const { buffer, slot } = this.accountLoader.getBufferAndSlot(
101
+ this.userStatsAccountPublicKey
102
+ );
103
+ const currentSlot = this.userStats?.slot ?? 0;
104
+ if (buffer && slot > currentSlot) {
105
+ const account = this.program.account.userStats.coder.accounts.decode(
106
+ 'UserStats',
107
+ buffer
113
108
  );
114
- if (buffer) {
115
- const account = this.program.account[
116
- accountToPoll.key
117
- ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
118
- this[accountToPoll.key] = { data: account, slot };
119
- }
109
+ this.userStats = { data: account, slot };
120
110
  }
121
111
  }
122
112
 
123
- doAccountsExist(): boolean {
124
- let success = true;
125
- for (const [_, accountToPoll] of this.accountsToPoll) {
126
- if (!this[accountToPoll.key]) {
127
- success = false;
128
- break;
129
- }
130
- }
131
- return success;
113
+ doesAccountExist(): boolean {
114
+ return this.userStats !== undefined;
132
115
  }
133
116
 
134
117
  async unsubscribe(): Promise<void> {
135
- for (const [_, accountToPoll] of this.accountsToPoll) {
136
- this.accountLoader.removeAccount(
137
- accountToPoll.publicKey,
138
- accountToPoll.callbackId
139
- );
118
+ if (!this.isSubscribed) {
119
+ return;
140
120
  }
141
121
 
122
+ this.accountLoader.removeAccount(
123
+ this.userStatsAccountPublicKey,
124
+ this.callbackId
125
+ );
126
+ this.callbackId = undefined;
127
+
142
128
  this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
143
129
  this.errorCallbackId = undefined;
144
130
 
145
- this.accountsToPoll.clear();
146
-
147
131
  this.isSubscribed = false;
148
132
  }
149
133
 
@@ -18,7 +18,7 @@ export interface AccountSubscriber<T> {
18
18
  fetch(): Promise<void>;
19
19
  unsubscribe(): Promise<void>;
20
20
 
21
- setData(userAccount: T): void;
21
+ setData(userAccount: T, slot?: number): void;
22
22
  }
23
23
 
24
24
  export class NotSubscribedError extends Error {
@@ -73,6 +73,7 @@ export interface UserAccountSubscriber {
73
73
 
74
74
  subscribe(userAccount?: UserAccount): Promise<boolean>;
75
75
  fetch(): Promise<void>;
76
+ updateData(userAccount: UserAccount, slot: number): void;
76
77
  unsubscribe(): Promise<void>;
77
78
 
78
79
  getUserAccountAndSlot(): DataAndSlot<UserAccount>;
@@ -45,14 +45,15 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
45
45
  );
46
46
  }
47
47
 
48
- setData(data: T): void {
49
- if (this.dataAndSlot) {
48
+ setData(data: T, slot?: number): void {
49
+ const newSlot = slot || 0;
50
+ if (this.dataAndSlot && this.dataAndSlot.slot > newSlot) {
50
51
  return;
51
52
  }
52
53
 
53
54
  this.dataAndSlot = {
54
55
  data,
55
- slot: undefined,
56
+ slot,
56
57
  };
57
58
  }
58
59
 
@@ -78,4 +78,14 @@ export class WebSocketUserAccountSubscriber implements UserAccountSubscriber {
78
78
  this.assertIsSubscribed();
79
79
  return this.userDataAccountSubscriber.dataAndSlot;
80
80
  }
81
+
82
+ public updateData(userAccount: UserAccount, slot: number) {
83
+ const currentDataSlot =
84
+ this.userDataAccountSubscriber.dataAndSlot?.slot || 0;
85
+ if (currentDataSlot < slot) {
86
+ this.userDataAccountSubscriber.setData(userAccount, slot);
87
+ this.eventEmitter.emit('userAccountUpdate', userAccount);
88
+ this.eventEmitter.emit('update');
89
+ }
90
+ }
81
91
  }
@@ -189,6 +189,19 @@ export function getSerumFulfillmentConfigPublicKey(
189
189
  )[0];
190
190
  }
191
191
 
192
+ export function getPhoenixFulfillmentConfigPublicKey(
193
+ programId: PublicKey,
194
+ market: PublicKey
195
+ ): PublicKey {
196
+ return PublicKey.findProgramAddressSync(
197
+ [
198
+ Buffer.from(anchor.utils.bytes.utf8.encode('phoenix_fulfillment_config')),
199
+ market.toBuffer(),
200
+ ],
201
+ programId
202
+ )[0];
203
+ }
204
+
192
205
  export function getReferrerNamePublicKeySync(
193
206
  programId: PublicKey,
194
207
  nameBuffer: number[]
@@ -24,6 +24,7 @@ import {
24
24
  getInsuranceFundVaultPublicKey,
25
25
  getSerumOpenOrdersPublicKey,
26
26
  getSerumFulfillmentConfigPublicKey,
27
+ getPhoenixFulfillmentConfigPublicKey,
27
28
  } from './addresses/pda';
28
29
  import { squareRootBN } from './math/utils';
29
30
  import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
@@ -31,6 +32,7 @@ import { DriftClient } from './driftClient';
31
32
  import { PEG_PRECISION } from './constants/numericConstants';
32
33
  import { calculateTargetPriceTrade } from './math/trade';
33
34
  import { calculateAmmReservesAfterSwap, getSwapDirection } from './math/amm';
35
+ import { PROGRAM_ID as PHOENIX_PROGRAM_ID } from '@ellipsis-labs/phoenix-sdk';
34
36
 
35
37
  export class AdminClient extends DriftClient {
36
38
  public async initialize(
@@ -181,6 +183,38 @@ export class AdminClient extends DriftClient {
181
183
  return txSig;
182
184
  }
183
185
 
186
+ public async initializePhoenixFulfillmentConfig(
187
+ marketIndex: number,
188
+ phoenixMarket: PublicKey
189
+ ): Promise<TransactionSignature> {
190
+ const phoenixFulfillmentConfig = getPhoenixFulfillmentConfigPublicKey(
191
+ this.program.programId,
192
+ phoenixMarket
193
+ );
194
+
195
+ const tx =
196
+ await this.program.transaction.initializePhoenixFulfillmentConfig(
197
+ marketIndex,
198
+ {
199
+ accounts: {
200
+ admin: this.wallet.publicKey,
201
+ state: await this.getStatePublicKey(),
202
+ baseSpotMarket: this.getSpotMarketAccount(marketIndex).pubkey,
203
+ quoteSpotMarket: this.getQuoteSpotMarketAccount().pubkey,
204
+ driftSigner: this.getSignerPublicKey(),
205
+ phoenixMarket: phoenixMarket,
206
+ phoenixProgram: PHOENIX_PROGRAM_ID,
207
+ rent: SYSVAR_RENT_PUBKEY,
208
+ systemProgram: anchor.web3.SystemProgram.programId,
209
+ phoenixFulfillmentConfig,
210
+ },
211
+ }
212
+ );
213
+
214
+ const { txSig } = await this.sendTransaction(tx, [], this.opts);
215
+ return txSig;
216
+ }
217
+
184
218
  public async initializePerpMarket(
185
219
  marketIndex: number,
186
220
  priceOracle: PublicKey,
@@ -0,0 +1,37 @@
1
+ import fetch from 'node-fetch';
2
+ import { DLOBOrdersCoder } from './DLOBOrders';
3
+ import { DLOB } from './DLOB';
4
+
5
+ type DLOBApiClientConfig = {
6
+ url: string;
7
+ };
8
+
9
+ export class DLOBApiClient {
10
+ url: string;
11
+ dlobCoder = DLOBOrdersCoder.create();
12
+ lastSeenDLOB: DLOB;
13
+ lastSeenSlot = 0;
14
+
15
+ constructor(config: DLOBApiClientConfig) {
16
+ this.url = config.url;
17
+ }
18
+
19
+ public async getDLOB(slot: number): Promise<DLOB> {
20
+ const r = await fetch(this.url);
21
+ if (!r.ok) {
22
+ throw new Error(`Failed to fetch DLOB from ${this.url}`);
23
+ }
24
+
25
+ const resp = await r.json();
26
+ const responseSlot = resp['slot'];
27
+ if (responseSlot > this.lastSeenSlot) {
28
+ const dlobOrdersBuffer = Buffer.from(resp['data'], 'base64');
29
+ const dlobOrders = this.dlobCoder.decode(Buffer.from(dlobOrdersBuffer));
30
+ const dlob = new DLOB();
31
+ dlob.initFromOrders(dlobOrders, slot);
32
+ this.lastSeenDLOB = dlob;
33
+ this.lastSeenSlot = responseSlot;
34
+ }
35
+ return this.lastSeenDLOB;
36
+ }
37
+ }
@@ -0,0 +1,57 @@
1
+ import { DLOB } from './DLOB';
2
+ import { EventEmitter } from 'events';
3
+ import StrictEventEmitter from 'strict-event-emitter-types';
4
+ import {
5
+ DLOBSource,
6
+ DLOBSubscriberEvents,
7
+ DLOBSubscriptionConfig,
8
+ SlotSource,
9
+ } from './types';
10
+
11
+ export class DLOBSubscriber {
12
+ dlobSource: DLOBSource;
13
+ slotSource: SlotSource;
14
+ updateFrequency: number;
15
+ intervalId?: NodeJS.Timeout;
16
+ dlob = new DLOB();
17
+ public eventEmitter: StrictEventEmitter<EventEmitter, DLOBSubscriberEvents>;
18
+
19
+ constructor(config: DLOBSubscriptionConfig) {
20
+ this.dlobSource = config.dlobSource;
21
+ this.slotSource = config.slotSource;
22
+ this.updateFrequency = config.updateFrequency;
23
+ this.eventEmitter = new EventEmitter();
24
+ }
25
+
26
+ public async subscribe(): Promise<void> {
27
+ if (this.intervalId) {
28
+ return;
29
+ }
30
+
31
+ await this.updateDLOB();
32
+
33
+ this.intervalId = setInterval(async () => {
34
+ try {
35
+ await this.updateDLOB();
36
+ this.eventEmitter.emit('update', this.dlob);
37
+ } catch (e) {
38
+ this.eventEmitter.emit('error', e);
39
+ }
40
+ }, this.updateFrequency);
41
+ }
42
+
43
+ async updateDLOB(): Promise<void> {
44
+ this.dlob = await this.dlobSource.getDLOB(this.slotSource.getSlot());
45
+ }
46
+
47
+ public getDLOB(): DLOB {
48
+ return this.dlob;
49
+ }
50
+
51
+ public async unsubscribe(): Promise<void> {
52
+ if (this.intervalId) {
53
+ clearInterval(this.intervalId);
54
+ this.intervalId = undefined;
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,20 @@
1
+ import { DLOB } from './DLOB';
2
+
3
+ export type DLOBSubscriptionConfig = {
4
+ dlobSource: DLOBSource;
5
+ slotSource: SlotSource;
6
+ updateFrequency: number;
7
+ };
8
+
9
+ export interface DLOBSubscriberEvents {
10
+ update: (dlob: DLOB) => void;
11
+ error: (e: Error) => void;
12
+ }
13
+
14
+ export interface DLOBSource {
15
+ getDLOB(slot: number): Promise<DLOB>;
16
+ }
17
+
18
+ export interface SlotSource {
19
+ getSlot(): number;
20
+ }