@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.
- package/lib/accounts/bulkAccountLoader.d.ts +32 -0
- package/lib/accounts/bulkAccountLoader.d.ts.map +1 -0
- package/lib/accounts/bulkAccountLoader.js +166 -0
- package/lib/accounts/bulkUserSubscription.d.ts +8 -0
- package/lib/accounts/bulkUserSubscription.d.ts.map +1 -0
- package/lib/accounts/bulkUserSubscription.js +28 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +46 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts.map +1 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +206 -0
- package/lib/accounts/pollingTokenAccountSubscriber.d.ts +26 -0
- package/lib/accounts/pollingTokenAccountSubscriber.d.ts.map +1 -0
- package/lib/accounts/pollingTokenAccountSubscriber.js +79 -0
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +31 -0
- package/lib/accounts/pollingUserAccountSubscriber.d.ts.map +1 -0
- package/lib/accounts/pollingUserAccountSubscriber.js +123 -0
- package/lib/accounts/types.d.ts +30 -0
- package/lib/accounts/types.d.ts.map +1 -1
- package/lib/accounts/utils.d.ts +2 -0
- package/lib/accounts/utils.d.ts.map +1 -0
- package/lib/accounts/utils.js +7 -0
- package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -2
- package/lib/accounts/webSocketAccountSubscriber.d.ts.map +1 -1
- package/lib/accounts/webSocketAccountSubscriber.js +43 -12
- package/lib/accounts/{defaultClearingHouseAccountSubscriber.d.ts → webSocketClearingHouseAccountSubscriber.d.ts} +4 -2
- package/lib/accounts/webSocketClearingHouseAccountSubscriber.d.ts.map +1 -0
- package/lib/accounts/{defaultClearingHouseAccountSubscriber.js → webSocketClearingHouseAccountSubscriber.js} +4 -3
- package/lib/accounts/{defaultUserAccountSubscriber.d.ts → webSocketUserAccountSubscriber.d.ts} +4 -2
- package/lib/accounts/webSocketUserAccountSubscriber.d.ts.map +1 -0
- package/lib/accounts/{defaultUserAccountSubscriber.js → webSocketUserAccountSubscriber.js} +4 -3
- package/lib/admin.d.ts +1 -0
- package/lib/admin.d.ts.map +1 -1
- package/lib/admin.js +20 -11
- package/lib/clearingHouse.d.ts +12 -2
- package/lib/clearingHouse.d.ts.map +1 -1
- package/lib/clearingHouse.js +19 -8
- package/lib/clearingHouseUser.d.ts +11 -3
- package/lib/clearingHouseUser.d.ts.map +1 -1
- package/lib/clearingHouseUser.js +33 -23
- package/lib/constants/markets.d.ts +2 -1
- package/lib/constants/markets.d.ts.map +1 -1
- package/lib/constants/markets.js +20 -15
- package/lib/constants/numericConstants.d.ts +3 -1
- package/lib/constants/numericConstants.d.ts.map +1 -1
- package/lib/constants/numericConstants.js +15 -17
- package/lib/examples/makeTradeExample.js +1 -1
- package/lib/factory/clearingHouse.d.ts +26 -0
- package/lib/factory/clearingHouse.d.ts.map +1 -0
- package/lib/factory/clearingHouse.js +64 -0
- package/lib/factory/clearingHouseUser.d.ts +20 -0
- package/lib/factory/clearingHouseUser.d.ts.map +1 -0
- package/lib/factory/clearingHouseUser.js +34 -0
- package/lib/index.d.ts +8 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +9 -2
- package/lib/math/conversion.d.ts +1 -1
- package/lib/math/conversion.d.ts.map +1 -1
- package/lib/math/insuranceFund.d.ts +2 -1
- package/lib/math/insuranceFund.d.ts.map +1 -1
- package/lib/math/insuranceFund.js +3 -6
- package/lib/math/position.d.ts +2 -1
- package/lib/math/position.d.ts.map +1 -1
- package/lib/math/position.js +2 -5
- package/lib/math/utils.d.ts +2 -1
- package/lib/math/utils.d.ts.map +1 -1
- package/lib/math/utils.js +3 -3
- package/lib/mockUSDCFaucet.d.ts +2 -1
- package/lib/mockUSDCFaucet.d.ts.map +1 -1
- package/lib/token/index.d.ts +4 -0
- package/lib/token/index.d.ts.map +1 -0
- package/lib/token/index.js +38 -0
- package/lib/types.d.ts +3 -1
- package/lib/types.d.ts.map +1 -1
- package/package.json +11 -3
- package/src/accounts/bulkAccountLoader.ts +207 -0
- package/src/accounts/bulkUserSubscription.ts +28 -0
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +293 -0
- package/src/accounts/pollingTokenAccountSubscriber.ts +99 -0
- package/src/accounts/pollingUserAccountSubscriber.ts +166 -0
- package/src/accounts/types.ts +38 -0
- package/src/accounts/utils.ts +3 -0
- package/src/accounts/webSocketAccountSubscriber.ts +67 -17
- package/src/accounts/{defaultClearingHouseAccountSubscriber.ts → webSocketClearingHouseAccountSubscriber.ts} +4 -1
- package/src/accounts/{defaultUserAccountSubscriber.ts → webSocketUserAccountSubscriber.ts} +4 -1
- package/src/admin.ts +27 -18
- package/src/clearingHouse.ts +25 -17
- package/src/clearingHouseUser.ts +26 -6
- package/src/constants/markets.ts +9 -1
- package/src/constants/numericConstants.ts +2 -1
- package/src/examples/makeTradeExample.ts +4 -1
- package/src/factory/clearingHouse.ts +125 -0
- package/src/factory/clearingHouseUser.ts +73 -0
- package/src/index.ts +8 -2
- package/src/math/conversion.ts +1 -1
- package/src/math/insuranceFund.ts +1 -1
- package/src/math/position.ts +1 -1
- package/src/math/utils.ts +1 -1
- package/src/mockUSDCFaucet.ts +1 -1
- package/src/token/index.ts +37 -0
- package/src/types.ts +2 -1
- package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts.map +0 -1
- 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
|
+
}
|
package/src/accounts/types.ts
CHANGED
|
@@ -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
|
+
};
|