@drift-labs/sdk 0.1.18 → 0.1.20

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 (101) hide show
  1. package/lib/accounts/bulkAccountLoader.d.ts +32 -0
  2. package/lib/accounts/bulkAccountLoader.d.ts.map +1 -0
  3. package/lib/accounts/bulkAccountLoader.js +166 -0
  4. package/lib/accounts/bulkUserSubscription.d.ts +8 -0
  5. package/lib/accounts/bulkUserSubscription.d.ts.map +1 -0
  6. package/lib/accounts/bulkUserSubscription.js +28 -0
  7. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +46 -0
  8. package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts.map +1 -0
  9. package/lib/accounts/pollingClearingHouseAccountSubscriber.js +206 -0
  10. package/lib/accounts/pollingTokenAccountSubscriber.d.ts +26 -0
  11. package/lib/accounts/pollingTokenAccountSubscriber.d.ts.map +1 -0
  12. package/lib/accounts/pollingTokenAccountSubscriber.js +79 -0
  13. package/lib/accounts/pollingUserAccountSubscriber.d.ts +31 -0
  14. package/lib/accounts/pollingUserAccountSubscriber.d.ts.map +1 -0
  15. package/lib/accounts/pollingUserAccountSubscriber.js +123 -0
  16. package/lib/accounts/types.d.ts +30 -0
  17. package/lib/accounts/types.d.ts.map +1 -1
  18. package/lib/accounts/utils.d.ts +2 -0
  19. package/lib/accounts/utils.d.ts.map +1 -0
  20. package/lib/accounts/utils.js +7 -0
  21. package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -2
  22. package/lib/accounts/webSocketAccountSubscriber.d.ts.map +1 -1
  23. package/lib/accounts/webSocketAccountSubscriber.js +43 -12
  24. package/lib/accounts/{defaultClearingHouseAccountSubscriber.d.ts → webSocketClearingHouseAccountSubscriber.d.ts} +4 -2
  25. package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts.map +1 -0
  26. package/lib/accounts/{defaultClearingHouseAccountSubscriber.js → webSocketClearingHouseAccountSubscriber.js} +4 -3
  27. package/lib/accounts/{defaultUserAccountSubscriber.d.ts → webSocketUserAccountSubscriber.d.ts} +4 -2
  28. package/lib/accounts/webSocketUserAccountSubscriber.d.ts.map +1 -0
  29. package/lib/accounts/{defaultUserAccountSubscriber.js → webSocketUserAccountSubscriber.js} +4 -3
  30. package/lib/admin.d.ts +1 -0
  31. package/lib/admin.d.ts.map +1 -1
  32. package/lib/admin.js +20 -11
  33. package/lib/clearingHouse.d.ts +12 -2
  34. package/lib/clearingHouse.d.ts.map +1 -1
  35. package/lib/clearingHouse.js +19 -8
  36. package/lib/clearingHouseUser.d.ts +11 -3
  37. package/lib/clearingHouseUser.d.ts.map +1 -1
  38. package/lib/clearingHouseUser.js +33 -23
  39. package/lib/constants/markets.d.ts +2 -1
  40. package/lib/constants/markets.d.ts.map +1 -1
  41. package/lib/constants/markets.js +20 -15
  42. package/lib/constants/numericConstants.d.ts +3 -1
  43. package/lib/constants/numericConstants.d.ts.map +1 -1
  44. package/lib/constants/numericConstants.js +15 -17
  45. package/lib/examples/makeTradeExample.js +1 -1
  46. package/lib/factory/clearingHouse.d.ts +26 -0
  47. package/lib/factory/clearingHouse.d.ts.map +1 -0
  48. package/lib/factory/clearingHouse.js +64 -0
  49. package/lib/factory/clearingHouseUser.d.ts +20 -0
  50. package/lib/factory/clearingHouseUser.d.ts.map +1 -0
  51. package/lib/factory/clearingHouseUser.js +34 -0
  52. package/lib/index.d.ts +8 -2
  53. package/lib/index.d.ts.map +1 -1
  54. package/lib/index.js +9 -2
  55. package/lib/math/conversion.d.ts +1 -1
  56. package/lib/math/conversion.d.ts.map +1 -1
  57. package/lib/math/insuranceFund.d.ts +2 -1
  58. package/lib/math/insuranceFund.d.ts.map +1 -1
  59. package/lib/math/insuranceFund.js +3 -6
  60. package/lib/math/position.d.ts +2 -1
  61. package/lib/math/position.d.ts.map +1 -1
  62. package/lib/math/position.js +2 -5
  63. package/lib/math/utils.d.ts +2 -1
  64. package/lib/math/utils.d.ts.map +1 -1
  65. package/lib/math/utils.js +3 -3
  66. package/lib/mockUSDCFaucet.d.ts +2 -1
  67. package/lib/mockUSDCFaucet.d.ts.map +1 -1
  68. package/lib/token/index.d.ts +4 -0
  69. package/lib/token/index.d.ts.map +1 -0
  70. package/lib/token/index.js +38 -0
  71. package/lib/types.d.ts +3 -1
  72. package/lib/types.d.ts.map +1 -1
  73. package/package.json +11 -3
  74. package/src/accounts/bulkAccountLoader.ts +207 -0
  75. package/src/accounts/bulkUserSubscription.ts +28 -0
  76. package/src/accounts/pollingClearingHouseAccountSubscriber.ts +293 -0
  77. package/src/accounts/pollingTokenAccountSubscriber.ts +99 -0
  78. package/src/accounts/pollingUserAccountSubscriber.ts +166 -0
  79. package/src/accounts/types.ts +38 -0
  80. package/src/accounts/utils.ts +3 -0
  81. package/src/accounts/webSocketAccountSubscriber.ts +67 -17
  82. package/src/accounts/{defaultClearingHouseAccountSubscriber.ts → webSocketClearingHouseAccountSubscriber.ts} +4 -1
  83. package/src/accounts/{defaultUserAccountSubscriber.ts → webSocketUserAccountSubscriber.ts} +4 -1
  84. package/src/admin.ts +27 -18
  85. package/src/clearingHouse.ts +25 -17
  86. package/src/clearingHouseUser.ts +26 -6
  87. package/src/constants/markets.ts +9 -1
  88. package/src/constants/numericConstants.ts +2 -1
  89. package/src/examples/makeTradeExample.ts +4 -1
  90. package/src/factory/clearingHouse.ts +125 -0
  91. package/src/factory/clearingHouseUser.ts +73 -0
  92. package/src/index.ts +8 -2
  93. package/src/math/conversion.ts +1 -1
  94. package/src/math/insuranceFund.ts +1 -1
  95. package/src/math/position.ts +1 -1
  96. package/src/math/utils.ts +1 -1
  97. package/src/mockUSDCFaucet.ts +1 -1
  98. package/src/token/index.ts +37 -0
  99. package/src/types.ts +2 -1
  100. package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts.map +0 -1
  101. package/lib/accounts/defaultUserAccountSubscriber.d.ts.map +0 -1
@@ -0,0 +1,293 @@
1
+ import {
2
+ AccountToPoll,
3
+ ClearingHouseAccountEvents,
4
+ ClearingHouseAccountSubscriber,
5
+ ClearingHouseAccountTypes,
6
+ NotSubscribedError,
7
+ } from './types';
8
+ import { Program } from '@project-serum/anchor';
9
+ import StrictEventEmitter from 'strict-event-emitter-types';
10
+ import { EventEmitter } from 'events';
11
+ import {
12
+ DepositHistoryAccount,
13
+ ExtendedCurveHistoryAccount,
14
+ FundingPaymentHistoryAccount,
15
+ FundingRateHistoryAccount,
16
+ LiquidationHistoryAccount,
17
+ MarketsAccount,
18
+ StateAccount,
19
+ TradeHistoryAccount,
20
+ } from '../types';
21
+ import { getClearingHouseStateAccountPublicKey } from '../addresses';
22
+ import { BulkAccountLoader } from './bulkAccountLoader';
23
+ import { capitalize } from './utils';
24
+ import { ClearingHouseConfigType } from '../factory/clearingHouse';
25
+
26
+ export class PollingClearingHouseAccountSubscriber
27
+ implements ClearingHouseAccountSubscriber
28
+ {
29
+ isSubscribed: boolean;
30
+ program: Program;
31
+ eventEmitter: StrictEventEmitter<EventEmitter, ClearingHouseAccountEvents>;
32
+
33
+ accountLoader: BulkAccountLoader;
34
+ accountsToPoll = new Map<string, AccountToPoll>();
35
+ errorCallbackId?: string;
36
+
37
+ state?: StateAccount;
38
+ markets?: MarketsAccount;
39
+ tradeHistory?: TradeHistoryAccount;
40
+ depositHistory?: DepositHistoryAccount;
41
+ fundingPaymentHistory?: FundingPaymentHistoryAccount;
42
+ fundingRateHistory?: FundingRateHistoryAccount;
43
+ liquidationHistory?: LiquidationHistoryAccount;
44
+ extendedCurveHistory: ExtendedCurveHistoryAccount;
45
+
46
+ optionalExtraSubscriptions: ClearingHouseAccountTypes[] = [];
47
+
48
+ type: ClearingHouseConfigType = 'polling';
49
+
50
+ private isSubscribing = false;
51
+ private subscriptionPromise: Promise<boolean>;
52
+ private subscriptionPromiseResolver: (val: boolean) => void;
53
+
54
+ public constructor(program: Program, accountLoader: BulkAccountLoader) {
55
+ this.isSubscribed = false;
56
+ this.program = program;
57
+ this.eventEmitter = new EventEmitter();
58
+ this.accountLoader = accountLoader;
59
+ }
60
+
61
+ public async subscribe(
62
+ optionalSubscriptions?: ClearingHouseAccountTypes[]
63
+ ): Promise<boolean> {
64
+ if (this.isSubscribed) {
65
+ return true;
66
+ }
67
+
68
+ if (this.isSubscribing) {
69
+ return await this.subscriptionPromise;
70
+ }
71
+
72
+ this.optionalExtraSubscriptions = optionalSubscriptions;
73
+
74
+ this.isSubscribing = true;
75
+
76
+ this.subscriptionPromise = new Promise((res) => {
77
+ this.subscriptionPromiseResolver = res;
78
+ });
79
+
80
+ await this.updateAccountsToPoll();
81
+ await this.addToAccountLoader();
82
+ await this.fetch();
83
+ this.eventEmitter.emit('update');
84
+
85
+ this.isSubscribing = false;
86
+ this.isSubscribed = true;
87
+ this.subscriptionPromiseResolver(true);
88
+
89
+ return true;
90
+ }
91
+
92
+ async updateAccountsToPoll(): Promise<void> {
93
+ if (this.accountsToPoll.size > 0) {
94
+ return;
95
+ }
96
+
97
+ const statePublicKey = await getClearingHouseStateAccountPublicKey(
98
+ this.program.programId
99
+ );
100
+
101
+ const state = (await this.program.account.state.fetch(
102
+ statePublicKey
103
+ )) as StateAccount;
104
+
105
+ this.accountsToPoll.set(statePublicKey.toString(), {
106
+ key: 'state',
107
+ publicKey: statePublicKey,
108
+ eventType: 'stateAccountUpdate',
109
+ });
110
+
111
+ this.accountsToPoll.set(state.markets.toString(), {
112
+ key: 'markets',
113
+ publicKey: state.markets,
114
+ eventType: 'marketsAccountUpdate',
115
+ });
116
+
117
+ if (this.optionalExtraSubscriptions?.includes('tradeHistoryAccount')) {
118
+ this.accountsToPoll.set(state.tradeHistory.toString(), {
119
+ key: 'tradeHistory',
120
+ publicKey: state.tradeHistory,
121
+ eventType: 'tradeHistoryAccountUpdate',
122
+ });
123
+ }
124
+
125
+ if (this.optionalExtraSubscriptions?.includes('depositHistoryAccount')) {
126
+ this.accountsToPoll.set(state.depositHistory.toString(), {
127
+ key: 'depositHistory',
128
+ publicKey: state.depositHistory,
129
+ eventType: 'depositHistoryAccountUpdate',
130
+ });
131
+ }
132
+
133
+ if (
134
+ this.optionalExtraSubscriptions?.includes('fundingPaymentHistoryAccount')
135
+ ) {
136
+ this.accountsToPoll.set(state.fundingPaymentHistory.toString(), {
137
+ key: 'fundingPaymentHistory',
138
+ publicKey: state.fundingPaymentHistory,
139
+ eventType: 'fundingPaymentHistoryAccountUpdate',
140
+ });
141
+ }
142
+
143
+ if (
144
+ this.optionalExtraSubscriptions?.includes('fundingRateHistoryAccount')
145
+ ) {
146
+ this.accountsToPoll.set(state.fundingRateHistory.toString(), {
147
+ key: 'fundingRateHistory',
148
+ publicKey: state.fundingRateHistory,
149
+ eventType: 'fundingRateHistoryAccountUpdate',
150
+ });
151
+ }
152
+
153
+ if (this.optionalExtraSubscriptions?.includes('curveHistoryAccount')) {
154
+ this.accountsToPoll.set(state.extendedCurveHistory.toString(), {
155
+ key: 'extendedCurveHistory',
156
+ publicKey: state.extendedCurveHistory,
157
+ eventType: 'curveHistoryAccountUpdate',
158
+ });
159
+ }
160
+
161
+ if (
162
+ this.optionalExtraSubscriptions?.includes('liquidationHistoryAccount')
163
+ ) {
164
+ this.accountsToPoll.set(state.liquidationHistory.toString(), {
165
+ key: 'liquidationHistory',
166
+ publicKey: state.liquidationHistory,
167
+ eventType: 'liquidationHistoryAccountUpdate',
168
+ });
169
+ }
170
+ }
171
+
172
+ async addToAccountLoader(): Promise<void> {
173
+ for (const [_, accountToPoll] of this.accountsToPoll) {
174
+ accountToPoll.callbackId = this.accountLoader.addAccount(
175
+ accountToPoll.publicKey,
176
+ (buffer) => {
177
+ const account = this.program.account[
178
+ accountToPoll.key
179
+ ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
180
+ this[accountToPoll.key] = account;
181
+ // @ts-ignore
182
+ this.eventEmitter.emit(accountToPoll.eventType, account);
183
+ this.eventEmitter.emit('update');
184
+ }
185
+ );
186
+ }
187
+
188
+ this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
189
+ this.eventEmitter.emit('error', error);
190
+ });
191
+ }
192
+
193
+ public async fetch(): Promise<void> {
194
+ await this.accountLoader.load();
195
+ for (const [_, accountToPoll] of this.accountsToPoll) {
196
+ const buffer = this.accountLoader.getAccountData(accountToPoll.publicKey);
197
+ if (buffer) {
198
+ this[accountToPoll.key] = this.program.account[
199
+ accountToPoll.key
200
+ ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
201
+ }
202
+ }
203
+ }
204
+
205
+ public async unsubscribe(): Promise<void> {
206
+ if (!this.isSubscribed) {
207
+ return;
208
+ }
209
+
210
+ for (const [_, accountToPoll] of this.accountsToPoll) {
211
+ this.accountLoader.removeAccount(
212
+ accountToPoll.publicKey,
213
+ accountToPoll.callbackId
214
+ );
215
+ }
216
+
217
+ this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
218
+ this.errorCallbackId = undefined;
219
+
220
+ this.accountsToPoll.clear();
221
+ this.isSubscribed = false;
222
+ }
223
+
224
+ assertIsSubscribed(): void {
225
+ if (!this.isSubscribed) {
226
+ throw new NotSubscribedError(
227
+ 'You must call `subscribe` before using this function'
228
+ );
229
+ }
230
+ }
231
+
232
+ assertOptionalIsSubscribed(
233
+ optionalSubscription: ClearingHouseAccountTypes
234
+ ): void {
235
+ if (!this.isSubscribed) {
236
+ throw new NotSubscribedError(
237
+ 'You must call `subscribe` before using this function'
238
+ );
239
+ }
240
+
241
+ if (!this.optionalExtraSubscriptions.includes(optionalSubscription)) {
242
+ throw new NotSubscribedError(
243
+ `You need to subscribe to the optional Clearing House account "${optionalSubscription}" to use this method`
244
+ );
245
+ }
246
+ }
247
+
248
+ public getStateAccount(): StateAccount {
249
+ this.assertIsSubscribed();
250
+ return this.state;
251
+ }
252
+
253
+ public getMarketsAccount(): MarketsAccount {
254
+ this.assertIsSubscribed();
255
+ return this.markets;
256
+ }
257
+
258
+ public getTradeHistoryAccount(): TradeHistoryAccount {
259
+ this.assertIsSubscribed();
260
+ this.assertOptionalIsSubscribed('tradeHistoryAccount');
261
+ return this.tradeHistory;
262
+ }
263
+
264
+ public getDepositHistoryAccount(): DepositHistoryAccount {
265
+ this.assertIsSubscribed();
266
+ this.assertOptionalIsSubscribed('depositHistoryAccount');
267
+ return this.depositHistory;
268
+ }
269
+
270
+ public getFundingPaymentHistoryAccount(): FundingPaymentHistoryAccount {
271
+ this.assertIsSubscribed();
272
+ this.assertOptionalIsSubscribed('fundingPaymentHistoryAccount');
273
+ return this.fundingPaymentHistory;
274
+ }
275
+
276
+ public getFundingRateHistoryAccount(): FundingRateHistoryAccount {
277
+ this.assertIsSubscribed();
278
+ this.assertOptionalIsSubscribed('fundingRateHistoryAccount');
279
+ return this.fundingRateHistory;
280
+ }
281
+
282
+ public getCurveHistoryAccount(): ExtendedCurveHistoryAccount {
283
+ this.assertIsSubscribed();
284
+ this.assertOptionalIsSubscribed('curveHistoryAccount');
285
+ return this.extendedCurveHistory;
286
+ }
287
+
288
+ public getLiquidationHistoryAccount(): LiquidationHistoryAccount {
289
+ this.assertIsSubscribed();
290
+ this.assertOptionalIsSubscribed('liquidationHistoryAccount');
291
+ return this.liquidationHistory;
292
+ }
293
+ }
@@ -0,0 +1,99 @@
1
+ import {
2
+ NotSubscribedError,
3
+ TokenAccountEvents,
4
+ TokenAccountSubscriber,
5
+ } from './types';
6
+ import { Program } from '@project-serum/anchor';
7
+ import StrictEventEmitter from 'strict-event-emitter-types';
8
+ import { EventEmitter } from 'events';
9
+ import { PublicKey } from '@solana/web3.js';
10
+ import { BulkAccountLoader } from './bulkAccountLoader';
11
+ import { AccountInfo } from '@solana/spl-token';
12
+ import { parseTokenAccount } from '../token';
13
+
14
+ export class PollingTokenAccountSubscriber implements TokenAccountSubscriber {
15
+ isSubscribed: boolean;
16
+ program: Program;
17
+ eventEmitter: StrictEventEmitter<EventEmitter, TokenAccountEvents>;
18
+ publicKey: PublicKey;
19
+
20
+ accountLoader: BulkAccountLoader;
21
+ callbackId?: string;
22
+ errorCallbackId?: string;
23
+
24
+ tokenAccount?: AccountInfo;
25
+
26
+ public constructor(publicKey: PublicKey, accountLoader: BulkAccountLoader) {
27
+ this.isSubscribed = false;
28
+ this.publicKey = publicKey;
29
+ this.accountLoader = accountLoader;
30
+ this.eventEmitter = new EventEmitter();
31
+ }
32
+
33
+ async subscribe(): Promise<boolean> {
34
+ if (this.isSubscribed) {
35
+ return true;
36
+ }
37
+
38
+ this.addToAccountLoader();
39
+ await this.fetch();
40
+ this.eventEmitter.emit('update');
41
+
42
+ this.isSubscribed = true;
43
+ return true;
44
+ }
45
+
46
+ addToAccountLoader(): void {
47
+ if (this.callbackId) {
48
+ return;
49
+ }
50
+
51
+ this.callbackId = this.accountLoader.addAccount(
52
+ this.publicKey,
53
+ (buffer) => {
54
+ const tokenAccount = parseTokenAccount(buffer);
55
+ this.tokenAccount = tokenAccount;
56
+ // @ts-ignore
57
+ this.eventEmitter.emit('tokenAccountUpdate', tokenAccount);
58
+ this.eventEmitter.emit('update');
59
+ }
60
+ );
61
+
62
+ this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
63
+ this.eventEmitter.emit('error', error);
64
+ });
65
+ }
66
+
67
+ async fetch(): Promise<void> {
68
+ await this.accountLoader.load();
69
+ const buffer = this.accountLoader.getAccountData(this.publicKey);
70
+ this.tokenAccount = parseTokenAccount(buffer);
71
+ }
72
+
73
+ async unsubscribe(): Promise<void> {
74
+ if (!this.isSubscribed) {
75
+ return;
76
+ }
77
+
78
+ this.accountLoader.removeAccount(this.publicKey, this.callbackId);
79
+ this.callbackId = undefined;
80
+
81
+ this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
82
+ this.errorCallbackId = undefined;
83
+
84
+ this.isSubscribed = false;
85
+ }
86
+
87
+ assertIsSubscribed(): void {
88
+ if (!this.isSubscribed) {
89
+ throw new NotSubscribedError(
90
+ 'You must call `subscribe` before using this function'
91
+ );
92
+ }
93
+ }
94
+
95
+ public getTokenAccount(): AccountInfo {
96
+ this.assertIsSubscribed();
97
+ return this.tokenAccount;
98
+ }
99
+ }
@@ -0,0 +1,166 @@
1
+ import {
2
+ AccountToPoll,
3
+ NotSubscribedError,
4
+ UserAccountEvents,
5
+ UserAccountSubscriber,
6
+ } from './types';
7
+ import { Program } from '@project-serum/anchor';
8
+ import StrictEventEmitter from 'strict-event-emitter-types';
9
+ import { EventEmitter } from 'events';
10
+ import { PublicKey } from '@solana/web3.js';
11
+ import { getUserAccountPublicKey } from '../addresses';
12
+ import { UserAccount, UserPositionsAccount } from '../types';
13
+ import { BulkAccountLoader } from './bulkAccountLoader';
14
+ import { capitalize } from './utils';
15
+ import { ClearingHouseConfigType } from '../factory/clearingHouse';
16
+
17
+ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
18
+ isSubscribed: boolean;
19
+ program: Program;
20
+ eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
21
+ authority: PublicKey;
22
+
23
+ accountLoader: BulkAccountLoader;
24
+ accountsToPoll = new Map<string, AccountToPoll>();
25
+ errorCallbackId?: string;
26
+
27
+ user?: UserAccount;
28
+ userPositions?: UserPositionsAccount;
29
+
30
+ type: ClearingHouseConfigType = 'polling';
31
+
32
+ public constructor(
33
+ program: Program,
34
+ authority: PublicKey,
35
+ accountLoader: BulkAccountLoader
36
+ ) {
37
+ this.isSubscribed = false;
38
+ this.program = program;
39
+ this.authority = authority;
40
+ this.accountLoader = accountLoader;
41
+ this.eventEmitter = new EventEmitter();
42
+ }
43
+
44
+ async subscribe(): Promise<boolean> {
45
+ if (this.isSubscribed) {
46
+ return true;
47
+ }
48
+
49
+ await this.addToAccountLoader();
50
+ await this.fetchIfUnloaded();
51
+ this.eventEmitter.emit('update');
52
+
53
+ this.isSubscribed = true;
54
+ return true;
55
+ }
56
+
57
+ async addToAccountLoader(): Promise<void> {
58
+ if (this.accountsToPoll.size > 0) {
59
+ return;
60
+ }
61
+
62
+ const userPublicKey = await getUserAccountPublicKey(
63
+ this.program.programId,
64
+ this.authority
65
+ );
66
+
67
+ const userAccount = (await this.program.account.user.fetch(
68
+ userPublicKey
69
+ )) as UserAccount;
70
+
71
+ this.accountsToPoll.set(userPublicKey.toString(), {
72
+ key: 'user',
73
+ publicKey: userPublicKey,
74
+ eventType: 'userAccountData',
75
+ });
76
+
77
+ this.accountsToPoll.set(userAccount.positions.toString(), {
78
+ key: 'userPositions',
79
+ publicKey: userAccount.positions,
80
+ eventType: 'userPositionsData',
81
+ });
82
+
83
+ for (const [_, accountToPoll] of this.accountsToPoll) {
84
+ accountToPoll.callbackId = this.accountLoader.addAccount(
85
+ accountToPoll.publicKey,
86
+ (buffer) => {
87
+ const account = this.program.account[
88
+ accountToPoll.key
89
+ ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
90
+ this[accountToPoll.key] = account;
91
+ // @ts-ignore
92
+ this.eventEmitter.emit(accountToPoll.eventType, account);
93
+ this.eventEmitter.emit('update');
94
+ }
95
+ );
96
+ }
97
+
98
+ this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
99
+ this.eventEmitter.emit('error', error);
100
+ });
101
+ }
102
+
103
+ async fetchIfUnloaded(): Promise<void> {
104
+ let shouldFetch = false;
105
+ for (const [_, accountToPoll] of this.accountsToPoll) {
106
+ if (this[accountToPoll.key] === undefined) {
107
+ shouldFetch = true;
108
+ break;
109
+ }
110
+ }
111
+
112
+ if (shouldFetch) {
113
+ await this.fetch();
114
+ }
115
+ }
116
+
117
+ async fetch(): Promise<void> {
118
+ await this.accountLoader.load();
119
+ for (const [_, accountToPoll] of this.accountsToPoll) {
120
+ const buffer = this.accountLoader.getAccountData(accountToPoll.publicKey);
121
+ if (buffer) {
122
+ this[accountToPoll.key] = this.program.account[
123
+ accountToPoll.key
124
+ ].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
125
+ }
126
+ }
127
+ }
128
+
129
+ async unsubscribe(): Promise<void> {
130
+ if (!this.isSubscribed) {
131
+ return;
132
+ }
133
+
134
+ for (const [_, accountToPoll] of this.accountsToPoll) {
135
+ this.accountLoader.removeAccount(
136
+ accountToPoll.publicKey,
137
+ accountToPoll.callbackId
138
+ );
139
+ }
140
+
141
+ this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
142
+ this.errorCallbackId = undefined;
143
+
144
+ this.accountsToPoll.clear();
145
+
146
+ this.isSubscribed = false;
147
+ }
148
+
149
+ assertIsSubscribed(): void {
150
+ if (!this.isSubscribed) {
151
+ throw new NotSubscribedError(
152
+ 'You must call `subscribe` before using this function'
153
+ );
154
+ }
155
+ }
156
+
157
+ public getUserAccount(): UserAccount {
158
+ this.assertIsSubscribed();
159
+ return this.user;
160
+ }
161
+
162
+ public getUserPositionsAccount(): UserPositionsAccount {
163
+ this.assertIsSubscribed();
164
+ return this.userPositions;
165
+ }
166
+ }
@@ -12,6 +12,9 @@ import {
12
12
  } from '../types';
13
13
  import StrictEventEmitter from 'strict-event-emitter-types';
14
14
  import { EventEmitter } from 'events';
15
+ import { PublicKey } from '@solana/web3.js';
16
+ import { AccountInfo } from '@solana/spl-token';
17
+ import { ClearingHouseConfigType, ClearingHouseUserConfigType } from '..';
15
18
 
16
19
  export interface AccountSubscriber<T> {
17
20
  data?: T;
@@ -36,6 +39,7 @@ export interface ClearingHouseAccountEvents {
36
39
  depositHistoryAccountUpdate: (payload: DepositHistoryAccount) => void;
37
40
  curveHistoryAccountUpdate: (payload: ExtendedCurveHistoryAccount) => void;
38
41
  update: void;
42
+ error: (e: Error) => void;
39
43
  }
40
44
 
41
45
  export type ClearingHouseAccountTypes =
@@ -66,12 +70,15 @@ export interface ClearingHouseAccountSubscriber {
66
70
  getFundingRateHistoryAccount(): FundingRateHistoryAccount;
67
71
  getCurveHistoryAccount(): ExtendedCurveHistoryAccount;
68
72
  getLiquidationHistoryAccount(): LiquidationHistoryAccount;
73
+
74
+ type: ClearingHouseConfigType;
69
75
  }
70
76
 
71
77
  export interface UserAccountEvents {
72
78
  userAccountData: (payload: UserAccount) => void;
73
79
  userPositionsData: (payload: UserPositionsAccount) => void;
74
80
  update: void;
81
+ error: (e: Error) => void;
75
82
  }
76
83
 
77
84
  export interface UserAccountSubscriber {
@@ -84,4 +91,35 @@ export interface UserAccountSubscriber {
84
91
 
85
92
  getUserAccount(): UserAccount;
86
93
  getUserPositionsAccount(): UserPositionsAccount;
94
+
95
+ type: ClearingHouseUserConfigType;
96
+ }
97
+
98
+ export interface TokenAccountEvents {
99
+ tokenAccountUpdate: (payload: AccountInfo) => void;
100
+ update: void;
101
+ error: (e: Error) => void;
87
102
  }
103
+
104
+ export interface TokenAccountSubscriber {
105
+ eventEmitter: StrictEventEmitter<EventEmitter, TokenAccountEvents>;
106
+ isSubscribed: boolean;
107
+
108
+ subscribe(): Promise<boolean>;
109
+ fetch(): Promise<void>;
110
+ unsubscribe(): Promise<void>;
111
+
112
+ getTokenAccount(): AccountInfo;
113
+ }
114
+
115
+ export type AccountToPoll = {
116
+ key: string;
117
+ publicKey: PublicKey;
118
+ eventType: string;
119
+ callbackId?: string;
120
+ };
121
+
122
+ export type AccountData = {
123
+ slot: number;
124
+ buffer: Buffer | undefined;
125
+ };
@@ -0,0 +1,3 @@
1
+ export function capitalize(value: string): string {
2
+ return value[0].toUpperCase() + value.slice(1);
3
+ }