@drift-labs/sdk 2.31.0-beta.2 → 2.31.0-beta.5

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 (65) hide show
  1. package/get_events.ts +47 -0
  2. package/lib/auctionSubscriber/auctionSubscriber.d.ts +13 -0
  3. package/lib/auctionSubscriber/auctionSubscriber.js +22 -0
  4. package/lib/auctionSubscriber/index.d.ts +2 -0
  5. package/lib/auctionSubscriber/index.js +18 -0
  6. package/lib/auctionSubscriber/types.d.ts +10 -0
  7. package/lib/auctionSubscriber/types.js +2 -0
  8. package/lib/config.js +2 -2
  9. package/lib/dlob/orderBookLevels.d.ts +1 -1
  10. package/lib/dlob/orderBookLevels.js +7 -4
  11. package/lib/driftClient.d.ts +2 -0
  12. package/lib/driftClient.js +22 -9
  13. package/lib/events/eventSubscriber.d.ts +1 -0
  14. package/lib/events/eventSubscriber.js +5 -3
  15. package/lib/events/fetchLogs.d.ts +1 -1
  16. package/lib/events/fetchLogs.js +2 -2
  17. package/lib/events/pollingLogProvider.d.ts +2 -2
  18. package/lib/events/pollingLogProvider.js +3 -3
  19. package/lib/events/types.d.ts +2 -1
  20. package/lib/events/webSocketLogProvider.d.ts +2 -2
  21. package/lib/events/webSocketLogProvider.js +3 -3
  22. package/lib/idl/drift.json +61 -2
  23. package/lib/index.d.ts +1 -0
  24. package/lib/index.js +1 -0
  25. package/lib/math/spotBalance.js +1 -1
  26. package/lib/memcmp.d.ts +6 -0
  27. package/lib/memcmp.js +53 -0
  28. package/lib/orderSubscriber/OrderSubscriber.d.ts +24 -0
  29. package/lib/orderSubscriber/OrderSubscriber.js +100 -0
  30. package/lib/orderSubscriber/PollingSubscription.d.ts +13 -0
  31. package/lib/orderSubscriber/PollingSubscription.js +23 -0
  32. package/lib/orderSubscriber/WebsocketSubscription.d.ts +12 -0
  33. package/lib/orderSubscriber/WebsocketSubscription.js +30 -0
  34. package/lib/orderSubscriber/index.d.ts +2 -0
  35. package/lib/orderSubscriber/index.js +18 -0
  36. package/lib/orderSubscriber/types.d.ts +11 -0
  37. package/lib/orderSubscriber/types.js +2 -0
  38. package/lib/tx/retryTxSender.js +5 -2
  39. package/lib/types.d.ts +5 -1
  40. package/lib/types.js +1 -1
  41. package/lib/userMap/userMap.js +4 -19
  42. package/package.json +1 -1
  43. package/src/auctionSubscriber/auctionSubscriber.ts +50 -0
  44. package/src/auctionSubscriber/index.ts +2 -0
  45. package/src/auctionSubscriber/types.ts +16 -0
  46. package/src/config.ts +6 -3
  47. package/src/dlob/orderBookLevels.ts +13 -4
  48. package/src/driftClient.ts +41 -10
  49. package/src/events/eventSubscriber.ts +6 -4
  50. package/src/events/fetchLogs.ts +2 -2
  51. package/src/events/pollingLogProvider.ts +2 -2
  52. package/src/events/types.ts +2 -1
  53. package/src/events/webSocketLogProvider.ts +2 -2
  54. package/src/idl/drift.json +61 -2
  55. package/src/index.ts +1 -0
  56. package/src/math/spotBalance.ts +1 -3
  57. package/src/memcmp.ts +48 -0
  58. package/src/orderSubscriber/OrderSubscriber.ts +132 -0
  59. package/src/orderSubscriber/PollingSubscription.ts +39 -0
  60. package/src/orderSubscriber/WebsocketSubscription.ts +54 -0
  61. package/src/orderSubscriber/index.ts +2 -0
  62. package/src/orderSubscriber/types.ts +14 -0
  63. package/src/tx/retryTxSender.ts +8 -2
  64. package/src/types.ts +5 -1
  65. package/src/userMap/userMap.ts +4 -16
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.31.0-beta.2",
2
+ "version": "2.31.0-beta.5",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
@@ -1305,6 +1305,32 @@
1305
1305
  ],
1306
1306
  "args": []
1307
1307
  },
1308
+ {
1309
+ "name": "updateUserOpenOrdersCount",
1310
+ "accounts": [
1311
+ {
1312
+ "name": "state",
1313
+ "isMut": false,
1314
+ "isSigner": false
1315
+ },
1316
+ {
1317
+ "name": "authority",
1318
+ "isMut": false,
1319
+ "isSigner": true
1320
+ },
1321
+ {
1322
+ "name": "filler",
1323
+ "isMut": true,
1324
+ "isSigner": false
1325
+ },
1326
+ {
1327
+ "name": "user",
1328
+ "isMut": true,
1329
+ "isSigner": false
1330
+ }
1331
+ ],
1332
+ "args": []
1333
+ },
1308
1334
  {
1309
1335
  "name": "settlePnl",
1310
1336
  "accounts": [
@@ -5438,12 +5464,40 @@
5438
5464
  ],
5439
5465
  "type": "bool"
5440
5466
  },
5467
+ {
5468
+ "name": "openOrders",
5469
+ "docs": [
5470
+ "number of open orders"
5471
+ ],
5472
+ "type": "u8"
5473
+ },
5474
+ {
5475
+ "name": "hasOpenOrder",
5476
+ "docs": [
5477
+ "Whether or not user has open order"
5478
+ ],
5479
+ "type": "bool"
5480
+ },
5481
+ {
5482
+ "name": "openAuctions",
5483
+ "docs": [
5484
+ "number of open orders with auction"
5485
+ ],
5486
+ "type": "u8"
5487
+ },
5488
+ {
5489
+ "name": "hasOpenAuction",
5490
+ "docs": [
5491
+ "Whether or not user has open order with auction"
5492
+ ],
5493
+ "type": "bool"
5494
+ },
5441
5495
  {
5442
5496
  "name": "padding",
5443
5497
  "type": {
5444
5498
  "array": [
5445
5499
  "u8",
5446
- 25
5500
+ 21
5447
5501
  ]
5448
5502
  }
5449
5503
  }
@@ -10433,6 +10487,11 @@
10433
10487
  "code": 6250,
10434
10488
  "name": "SpotMarketReduceOnly",
10435
10489
  "msg": "SpotMarketReduceOnly"
10490
+ },
10491
+ {
10492
+ "code": 6251,
10493
+ "name": "FundingWasNotUpdated",
10494
+ "msg": "FundingWasNotUpdated"
10436
10495
  }
10437
10496
  ]
10438
10497
  }
package/src/index.ts CHANGED
@@ -76,5 +76,6 @@ export * from './dlob/orderBookLevels';
76
76
  export * from './userMap/userMap';
77
77
  export * from './userMap/userStatsMap';
78
78
  export * from './math/bankruptcy';
79
+ export * from './orderSubscriber';
79
80
 
80
81
  export { BN, PublicKey, pyth };
@@ -282,9 +282,7 @@ export function calculateInterestRate(bank: SpotMarketAccount): BN {
282
282
  } else {
283
283
  const borrowRateSlope = new BN(bank.optimalBorrowRate)
284
284
  .mul(SPOT_MARKET_UTILIZATION_PRECISION)
285
- .div(
286
- SPOT_MARKET_UTILIZATION_PRECISION.sub(new BN(bank.optimalUtilization))
287
- );
285
+ .div(new BN(bank.optimalUtilization));
288
286
 
289
287
  interestRate = utilization
290
288
  .mul(borrowRateSlope)
package/src/memcmp.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { MemcmpFilter } from '@solana/web3.js';
2
+ import bs58 from 'bs58';
3
+ import { BorshAccountsCoder } from '@coral-xyz/anchor';
4
+
5
+ export function getUserFilter(): MemcmpFilter {
6
+ return {
7
+ memcmp: {
8
+ offset: 0,
9
+ bytes: bs58.encode(BorshAccountsCoder.accountDiscriminator('User')),
10
+ },
11
+ };
12
+ }
13
+
14
+ export function getNonIdleUserFilter(): MemcmpFilter {
15
+ return {
16
+ memcmp: {
17
+ offset: 4350,
18
+ bytes: bs58.encode(Uint8Array.from([0])),
19
+ },
20
+ };
21
+ }
22
+
23
+ export function getUserWithOrderFilter(): MemcmpFilter {
24
+ return {
25
+ memcmp: {
26
+ offset: 4352,
27
+ bytes: bs58.encode(Uint8Array.from([1])),
28
+ },
29
+ };
30
+ }
31
+
32
+ export function getUserWithAuctionFilter(): MemcmpFilter {
33
+ return {
34
+ memcmp: {
35
+ offset: 4354,
36
+ bytes: bs58.encode(Uint8Array.from([1])),
37
+ },
38
+ };
39
+ }
40
+
41
+ export function getUserThatHasBeenLP(): MemcmpFilter {
42
+ return {
43
+ memcmp: {
44
+ offset: 4267,
45
+ bytes: bs58.encode(Uint8Array.from([99])),
46
+ },
47
+ };
48
+ }
@@ -0,0 +1,132 @@
1
+ import { DriftClient } from '../driftClient';
2
+ import { UserAccount } from '../types';
3
+ import { getUserFilter, getUserWithOrderFilter } from '../memcmp';
4
+ import { PublicKey, RpcResponseAndContext } from '@solana/web3.js';
5
+ import { Buffer } from 'buffer';
6
+ import { DLOB } from '../dlob/DLOB';
7
+ import { OrderSubscriberConfig } from './types';
8
+ import { PollingSubscription } from './PollingSubscription';
9
+ import { WebsocketSubscription } from './WebsocketSubscription';
10
+
11
+ export class OrderSubscriber {
12
+ driftClient: DriftClient;
13
+ usersAccounts = new Map<string, { slot: number; userAccount: UserAccount }>();
14
+ subscription: PollingSubscription | WebsocketSubscription;
15
+
16
+ fetchPromise?: Promise<void>;
17
+ fetchPromiseResolver: () => void;
18
+
19
+ constructor(config: OrderSubscriberConfig) {
20
+ this.driftClient = config.driftClient;
21
+ if (config.subscriptionConfig.type === 'polling') {
22
+ this.subscription = new PollingSubscription({
23
+ orderSubscriber: this,
24
+ frequency: config.subscriptionConfig.frequency,
25
+ });
26
+ } else {
27
+ this.subscription = new WebsocketSubscription({
28
+ orderSubscriber: this,
29
+ skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
30
+ });
31
+ }
32
+ }
33
+
34
+ public async subscribe(): Promise<void> {
35
+ await this.subscription.subscribe();
36
+ }
37
+
38
+ async fetch(): Promise<void> {
39
+ if (this.fetchPromise) {
40
+ return this.fetchPromise;
41
+ }
42
+
43
+ this.fetchPromise = new Promise((resolver) => {
44
+ this.fetchPromiseResolver = resolver;
45
+ });
46
+
47
+ try {
48
+ const rpcRequestArgs = [
49
+ this.driftClient.program.programId.toBase58(),
50
+ {
51
+ commitment: this.driftClient.opts.commitment,
52
+ filters: [getUserFilter(), getUserWithOrderFilter()],
53
+ encoding: 'base64',
54
+ withContext: true,
55
+ },
56
+ ];
57
+
58
+ const rpcJSONResponse: any =
59
+ // @ts-ignore
60
+ await this.driftClient.connection._rpcRequest(
61
+ 'getProgramAccounts',
62
+ rpcRequestArgs
63
+ );
64
+
65
+ const rpcResponseAndContext: RpcResponseAndContext<
66
+ Array<{
67
+ pubkey: PublicKey;
68
+ account: {
69
+ data: [string, string];
70
+ };
71
+ }>
72
+ > = rpcJSONResponse.result;
73
+
74
+ const slot: number = rpcResponseAndContext.context.slot;
75
+
76
+ const programAccountSet = new Set<string>();
77
+ for (const programAccount of rpcResponseAndContext.value) {
78
+ const key = programAccount.pubkey.toString();
79
+ // @ts-ignore
80
+ const buffer = Buffer.from(
81
+ programAccount.account.data[0],
82
+ programAccount.account.data[1]
83
+ );
84
+ programAccountSet.add(key);
85
+ this.tryUpdateUserAccount(key, buffer, slot);
86
+ }
87
+
88
+ for (const key of this.usersAccounts.keys()) {
89
+ if (!programAccountSet.has(key)) {
90
+ this.usersAccounts.delete(key);
91
+ }
92
+ }
93
+ } catch (e) {
94
+ console.error(e);
95
+ } finally {
96
+ this.fetchPromiseResolver();
97
+ this.fetchPromise = undefined;
98
+ }
99
+ }
100
+
101
+ tryUpdateUserAccount(key: string, buffer: Buffer, slot: number): void {
102
+ const slotAndUserAccount = this.usersAccounts.get(key);
103
+ if (!slotAndUserAccount || slotAndUserAccount.slot < slot) {
104
+ const userAccount =
105
+ this.driftClient.program.account.user.coder.accounts.decode(
106
+ 'User',
107
+ buffer
108
+ ) as UserAccount;
109
+
110
+ if (userAccount.hasOpenOrder) {
111
+ this.usersAccounts.set(key, { slot, userAccount });
112
+ } else {
113
+ this.usersAccounts.delete(key);
114
+ }
115
+ }
116
+ }
117
+
118
+ public async getDLOB(slot: number): Promise<DLOB> {
119
+ const dlob = new DLOB();
120
+ for (const [key, { userAccount }] of this.usersAccounts.entries()) {
121
+ const userAccountPubkey = new PublicKey(key);
122
+ for (const order of userAccount.orders) {
123
+ dlob.insertOrder(order, userAccountPubkey, slot);
124
+ }
125
+ }
126
+ return dlob;
127
+ }
128
+
129
+ public async unsubscribe(): Promise<void> {
130
+ await this.subscription.unsubscribe();
131
+ }
132
+ }
@@ -0,0 +1,39 @@
1
+ import { OrderSubscriber } from './OrderSubscriber';
2
+
3
+ export class PollingSubscription {
4
+ private orderSubscriber: OrderSubscriber;
5
+ private frequency: number;
6
+
7
+ intervalId?: NodeJS.Timer;
8
+
9
+ constructor({
10
+ orderSubscriber,
11
+ frequency,
12
+ }: {
13
+ orderSubscriber: OrderSubscriber;
14
+ frequency: number;
15
+ }) {
16
+ this.orderSubscriber = orderSubscriber;
17
+ this.frequency = frequency;
18
+ }
19
+
20
+ public async subscribe(): Promise<void> {
21
+ if (this.intervalId) {
22
+ return;
23
+ }
24
+
25
+ this.intervalId = setInterval(
26
+ this.orderSubscriber.fetch.bind(this.orderSubscriber),
27
+ this.frequency
28
+ );
29
+
30
+ await this.orderSubscriber.fetch();
31
+ }
32
+
33
+ public async unsubscribe(): Promise<void> {
34
+ if (this.intervalId) {
35
+ clearInterval(this.intervalId);
36
+ this.intervalId = undefined;
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,54 @@
1
+ import { OrderSubscriber } from './OrderSubscriber';
2
+ import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
3
+
4
+ export class WebsocketSubscription {
5
+ private orderSubscriber: OrderSubscriber;
6
+ private skipInitialLoad: boolean;
7
+
8
+ private websocketId: number;
9
+
10
+ constructor({
11
+ orderSubscriber,
12
+ skipInitialLoad = false,
13
+ }: {
14
+ orderSubscriber: OrderSubscriber;
15
+ skipInitialLoad?: boolean;
16
+ }) {
17
+ this.orderSubscriber = orderSubscriber;
18
+ this.skipInitialLoad = skipInitialLoad;
19
+ }
20
+
21
+ public async subscribe(): Promise<void> {
22
+ if (this.websocketId) {
23
+ return;
24
+ }
25
+
26
+ this.websocketId =
27
+ this.orderSubscriber.driftClient.connection.onProgramAccountChange(
28
+ this.orderSubscriber.driftClient.program.programId,
29
+ (keyAccountInfo, context) => {
30
+ const userKey = keyAccountInfo.accountId.toBase58();
31
+ this.orderSubscriber.tryUpdateUserAccount(
32
+ userKey,
33
+ keyAccountInfo.accountInfo.data,
34
+ context.slot
35
+ );
36
+ },
37
+ this.orderSubscriber.driftClient.opts.commitment,
38
+ [getUserFilter(), getNonIdleUserFilter()]
39
+ );
40
+
41
+ if (!this.skipInitialLoad) {
42
+ await this.orderSubscriber.fetch();
43
+ }
44
+ }
45
+
46
+ public async unsubscribe(): Promise<void> {
47
+ if (this.websocketId) {
48
+ await this.orderSubscriber.driftClient.connection.removeProgramAccountChangeListener(
49
+ this.websocketId
50
+ );
51
+ this.websocketId = undefined;
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,2 @@
1
+ export * from './OrderSubscriber';
2
+ export * from './types';
@@ -0,0 +1,14 @@
1
+ import { DriftClient } from '../driftClient';
2
+
3
+ export type OrderSubscriberConfig = {
4
+ driftClient: DriftClient;
5
+ subscriptionConfig:
6
+ | {
7
+ type: 'polling';
8
+ frequency: number;
9
+ }
10
+ | {
11
+ type: 'websocket';
12
+ skipInitialLoad?: boolean;
13
+ };
14
+ };
@@ -119,14 +119,20 @@ export class RetryTxSender implements TxSender {
119
119
  additionalSigners?: Array<Signer>,
120
120
  opts?: ConfirmOptions
121
121
  ): Promise<TxSigAndSlot> {
122
+ additionalSigners
123
+ ?.filter((s): s is Signer => s !== undefined)
124
+ .forEach((kp) => {
125
+ tx.sign([kp]);
126
+ });
127
+
122
128
  // @ts-ignore
123
- tx.sign((additionalSigners ?? []).concat(this.provider.wallet.payer));
129
+ const signedTx = await this.provider.wallet.signTransaction(tx);
124
130
 
125
131
  if (opts === undefined) {
126
132
  opts = this.provider.opts;
127
133
  }
128
134
 
129
- return this.sendRawTransaction(tx.serialize(), opts);
135
+ return this.sendRawTransaction(signedTx.serialize(), opts);
130
136
  }
131
137
 
132
138
  async sendRawTransaction(
package/src/types.ts CHANGED
@@ -137,7 +137,7 @@ export class OrderActionExplanation {
137
137
  orderFilledWithAmmJitLpSplit: {},
138
138
  };
139
139
  static readonly ORDER_FILLED_WITH_LP_JIT = {
140
- orderFilledWithAmmJitLpSplit: {},
140
+ orderFilledWithLpJit: {},
141
141
  };
142
142
  static readonly ORDER_FILLED_WITH_MATCH = {
143
143
  orderFilledWithMatch: {},
@@ -829,6 +829,10 @@ export type UserAccount = {
829
829
  lastActiveSlot: BN;
830
830
  isMarginTradingEnabled: boolean;
831
831
  idle: boolean;
832
+ openOrders: number;
833
+ hasOpenOrder: boolean;
834
+ openAuctions: number;
835
+ hasOpenAuction: boolean;
832
836
  };
833
837
 
834
838
  export type SpotPosition = {
@@ -18,7 +18,7 @@ import {
18
18
 
19
19
  import { PublicKey, RpcResponseAndContext } from '@solana/web3.js';
20
20
  import { Buffer } from 'buffer';
21
- import bs58 from 'bs58';
21
+ import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
22
22
 
23
23
  export interface UserMapInterface {
24
24
  subscribe(): Promise<void>;
@@ -188,28 +188,16 @@ export class UserMap implements UserMapInterface {
188
188
  }
189
189
 
190
190
  public async sync() {
191
- let filters = undefined;
191
+ const filters = [getUserFilter()];
192
192
  if (!this.includeIdle) {
193
- filters = [
194
- {
195
- memcmp: {
196
- offset: 4350,
197
- bytes: bs58.encode(Uint8Array.from([0])),
198
- },
199
- },
200
- ];
193
+ filters.push(getNonIdleUserFilter());
201
194
  }
202
195
 
203
196
  const rpcRequestArgs = [
204
197
  this.driftClient.program.programId.toBase58(),
205
198
  {
206
199
  commitment: this.driftClient.connection.commitment,
207
- filters: [
208
- {
209
- memcmp: this.driftClient.program.coder.accounts.memcmp('User'),
210
- },
211
- ...(Array.isArray(filters) ? filters : []),
212
- ],
200
+ filters,
213
201
  encoding: 'base64',
214
202
  withContext: true,
215
203
  },