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

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 (64) hide show
  1. package/lib/auctionSubscriber/auctionSubscriber.d.ts +13 -0
  2. package/lib/auctionSubscriber/auctionSubscriber.js +22 -0
  3. package/lib/auctionSubscriber/index.d.ts +2 -0
  4. package/lib/auctionSubscriber/index.js +18 -0
  5. package/lib/auctionSubscriber/types.d.ts +10 -0
  6. package/lib/auctionSubscriber/types.js +2 -0
  7. package/lib/driftClient.d.ts +2 -0
  8. package/lib/driftClient.js +22 -9
  9. package/lib/events/eventSubscriber.d.ts +1 -0
  10. package/lib/events/eventSubscriber.js +5 -3
  11. package/lib/events/fetchLogs.d.ts +1 -1
  12. package/lib/events/fetchLogs.js +2 -2
  13. package/lib/events/pollingLogProvider.d.ts +2 -2
  14. package/lib/events/pollingLogProvider.js +3 -3
  15. package/lib/events/types.d.ts +2 -1
  16. package/lib/events/webSocketLogProvider.d.ts +2 -2
  17. package/lib/events/webSocketLogProvider.js +3 -3
  18. package/lib/idl/drift.json +61 -2
  19. package/lib/index.d.ts +1 -0
  20. package/lib/index.js +1 -0
  21. package/lib/math/spotBalance.js +1 -1
  22. package/lib/memcmp.d.ts +5 -0
  23. package/lib/memcmp.js +44 -0
  24. package/lib/orderSubscriber/OrderSubscriber.d.ts +24 -0
  25. package/lib/orderSubscriber/OrderSubscriber.js +100 -0
  26. package/lib/orderSubscriber/PollingSubscription.d.ts +13 -0
  27. package/lib/orderSubscriber/PollingSubscription.js +23 -0
  28. package/lib/orderSubscriber/WebsocketSubscription.d.ts +12 -0
  29. package/lib/orderSubscriber/WebsocketSubscription.js +30 -0
  30. package/lib/orderSubscriber/index.d.ts +2 -0
  31. package/lib/orderSubscriber/index.js +18 -0
  32. package/lib/orderSubscriber/types.d.ts +11 -0
  33. package/lib/orderSubscriber/types.js +2 -0
  34. package/lib/tx/retryTxSender.js +5 -2
  35. package/lib/types.d.ts +5 -1
  36. package/lib/types.js +1 -1
  37. package/lib/userMap/userMap.js +4 -19
  38. package/package.json +1 -1
  39. package/src/assert/assert.js +9 -0
  40. package/src/auctionSubscriber/auctionSubscriber.ts +50 -0
  41. package/src/auctionSubscriber/index.ts +2 -0
  42. package/src/auctionSubscriber/types.ts +16 -0
  43. package/src/driftClient.ts +41 -10
  44. package/src/events/eventSubscriber.ts +6 -4
  45. package/src/events/fetchLogs.ts +2 -2
  46. package/src/events/pollingLogProvider.ts +2 -2
  47. package/src/events/types.ts +2 -1
  48. package/src/events/webSocketLogProvider.ts +2 -2
  49. package/src/idl/drift.json +61 -2
  50. package/src/index.ts +1 -0
  51. package/src/math/spotBalance.ts +1 -3
  52. package/src/memcmp.ts +39 -0
  53. package/src/orderSubscriber/OrderSubscriber.ts +132 -0
  54. package/src/orderSubscriber/PollingSubscription.ts +39 -0
  55. package/src/orderSubscriber/WebsocketSubscription.ts +54 -0
  56. package/src/orderSubscriber/index.ts +2 -0
  57. package/src/orderSubscriber/types.ts +14 -0
  58. package/src/token/index.js +38 -0
  59. package/src/tx/retryTxSender.ts +8 -2
  60. package/src/types.ts +5 -1
  61. package/src/userMap/userMap.ts +4 -16
  62. package/src/util/computeUnits.js +27 -0
  63. package/src/util/promiseTimeout.js +14 -0
  64. package/src/util/tps.js +27 -0
@@ -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
+ };
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseTokenAccount = void 0;
4
+ const spl_token_1 = require("@solana/spl-token");
5
+ const web3_js_1 = require("@solana/web3.js");
6
+ function parseTokenAccount(data) {
7
+ const accountInfo = spl_token_1.AccountLayout.decode(data);
8
+ accountInfo.mint = new web3_js_1.PublicKey(accountInfo.mint);
9
+ accountInfo.owner = new web3_js_1.PublicKey(accountInfo.owner);
10
+ accountInfo.amount = spl_token_1.u64.fromBuffer(accountInfo.amount);
11
+ if (accountInfo.delegateOption === 0) {
12
+ accountInfo.delegate = null;
13
+ // eslint-disable-next-line new-cap
14
+ accountInfo.delegatedAmount = new spl_token_1.u64(0);
15
+ }
16
+ else {
17
+ accountInfo.delegate = new web3_js_1.PublicKey(accountInfo.delegate);
18
+ accountInfo.delegatedAmount = spl_token_1.u64.fromBuffer(accountInfo.delegatedAmount);
19
+ }
20
+ accountInfo.isInitialized = accountInfo.state !== 0;
21
+ accountInfo.isFrozen = accountInfo.state === 2;
22
+ if (accountInfo.isNativeOption === 1) {
23
+ accountInfo.rentExemptReserve = spl_token_1.u64.fromBuffer(accountInfo.isNative);
24
+ accountInfo.isNative = true;
25
+ }
26
+ else {
27
+ accountInfo.rentExemptReserve = null;
28
+ accountInfo.isNative = false;
29
+ }
30
+ if (accountInfo.closeAuthorityOption === 0) {
31
+ accountInfo.closeAuthority = null;
32
+ }
33
+ else {
34
+ accountInfo.closeAuthority = new web3_js_1.PublicKey(accountInfo.closeAuthority);
35
+ }
36
+ return accountInfo;
37
+ }
38
+ exports.parseTokenAccount = parseTokenAccount;
@@ -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
  },
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.findComputeUnitConsumption = void 0;
13
+ function findComputeUnitConsumption(programId, connection, txSignature, commitment = 'confirmed') {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ const tx = yield connection.getTransaction(txSignature, { commitment });
16
+ const computeUnits = [];
17
+ const regex = new RegExp(`Program ${programId.toString()} consumed ([0-9]{0,6}) of ([0-9]{0,7}) compute units`);
18
+ tx.meta.logMessages.forEach((logMessage) => {
19
+ const match = logMessage.match(regex);
20
+ if (match && match[1]) {
21
+ computeUnits.push(match[1]);
22
+ }
23
+ });
24
+ return computeUnits;
25
+ });
26
+ }
27
+ exports.findComputeUnitConsumption = findComputeUnitConsumption;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.promiseTimeout = void 0;
4
+ function promiseTimeout(promise, timeoutMs) {
5
+ let timeoutId;
6
+ const timeoutPromise = new Promise((resolve) => {
7
+ timeoutId = setTimeout(() => resolve(null), timeoutMs);
8
+ });
9
+ return Promise.race([promise, timeoutPromise]).then((result) => {
10
+ clearTimeout(timeoutId);
11
+ return result;
12
+ });
13
+ }
14
+ exports.promiseTimeout = promiseTimeout;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.estimateTps = void 0;
13
+ function estimateTps(programId, connection, failed) {
14
+ return __awaiter(this, void 0, void 0, function* () {
15
+ let signatures = yield connection.getSignaturesForAddress(programId, undefined, 'finalized');
16
+ if (failed) {
17
+ signatures = signatures.filter((signature) => signature.err);
18
+ }
19
+ const numberOfSignatures = signatures.length;
20
+ if (numberOfSignatures === 0) {
21
+ return 0;
22
+ }
23
+ return (numberOfSignatures /
24
+ (signatures[0].blockTime - signatures[numberOfSignatures - 1].blockTime));
25
+ });
26
+ }
27
+ exports.estimateTps = estimateTps;