@drift-labs/sdk 0.1.19-master.1 → 0.1.21-master.3
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 +31 -0
- package/lib/accounts/bulkAccountLoader.js +177 -0
- package/lib/accounts/bulkUserSubscription.d.ts +7 -0
- package/lib/accounts/bulkUserSubscription.js +28 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.d.ts +49 -0
- package/lib/accounts/pollingClearingHouseAccountSubscriber.js +228 -0
- package/lib/accounts/pollingTokenAccountSubscriber.d.ts +25 -0
- package/lib/accounts/pollingTokenAccountSubscriber.js +79 -0
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +32 -0
- package/lib/accounts/pollingUserAccountSubscriber.js +136 -0
- package/lib/accounts/types.d.ts +38 -3
- package/lib/accounts/utils.d.ts +1 -0
- package/lib/accounts/utils.js +7 -0
- package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -3
- package/lib/accounts/webSocketAccountSubscriber.js +43 -12
- package/lib/accounts/{defaultClearingHouseAccountSubscriber.d.ts → webSocketClearingHouseAccountSubscriber.d.ts} +8 -3
- package/lib/accounts/{defaultClearingHouseAccountSubscriber.js → webSocketClearingHouseAccountSubscriber.js} +30 -4
- package/lib/accounts/{defaultUserAccountSubscriber.d.ts → webSocketUserAccountSubscriber.d.ts} +6 -3
- package/lib/accounts/{defaultUserAccountSubscriber.js → webSocketUserAccountSubscriber.js} +16 -4
- package/lib/addresses.d.ts +4 -1
- package/lib/addresses.js +28 -1
- package/lib/admin.d.ts +10 -4
- package/lib/admin.js +54 -17
- package/lib/assert/assert.d.ts +0 -1
- package/lib/clearingHouse.d.ts +39 -4
- package/lib/clearingHouse.js +334 -23
- package/lib/clearingHouseUser.d.ts +26 -20
- package/lib/clearingHouseUser.js +149 -118
- package/lib/config.d.ts +0 -1
- package/lib/constants/markets.d.ts +2 -2
- package/lib/constants/markets.js +28 -15
- package/lib/constants/numericConstants.d.ts +4 -2
- package/lib/constants/numericConstants.js +16 -17
- package/lib/examples/makeTradeExample.d.ts +0 -1
- package/lib/examples/makeTradeExample.js +6 -6
- package/lib/factory/clearingHouse.d.ts +25 -0
- package/lib/factory/clearingHouse.js +64 -0
- package/lib/factory/clearingHouseUser.d.ts +19 -0
- package/lib/factory/clearingHouseUser.js +34 -0
- package/lib/idl/clearing_house.json +1066 -39
- package/lib/index.d.ts +11 -3
- package/lib/index.js +12 -2
- package/lib/math/amm.d.ts +1 -1
- package/lib/math/amm.js +38 -15
- package/lib/math/conversion.d.ts +1 -2
- package/lib/math/conversion.js +1 -1
- package/lib/math/funding.d.ts +0 -1
- package/lib/math/funding.js +1 -1
- package/lib/math/insuranceFund.d.ts +2 -2
- package/lib/math/insuranceFund.js +3 -6
- package/lib/math/market.d.ts +2 -2
- package/lib/math/market.js +12 -2
- package/lib/math/orders.d.ts +3 -0
- package/lib/math/orders.js +32 -0
- package/lib/math/position.d.ts +6 -3
- package/lib/math/position.js +21 -10
- package/lib/math/trade.d.ts +0 -1
- package/lib/math/trade.js +16 -16
- package/lib/math/utils.d.ts +2 -2
- package/lib/math/utils.js +3 -3
- package/lib/mockUSDCFaucet.d.ts +2 -2
- package/lib/orderParams.d.ts +7 -0
- package/lib/orderParams.js +108 -0
- package/lib/orders.d.ts +6 -0
- package/lib/orders.js +136 -0
- package/lib/pythClient.d.ts +0 -1
- package/lib/pythClient.js +1 -1
- package/lib/token/index.d.ts +3 -0
- package/lib/token/index.js +38 -0
- package/lib/tx/defaultTxSender.d.ts +0 -1
- package/lib/tx/types.d.ts +0 -1
- package/lib/tx/utils.d.ts +0 -1
- package/lib/types.d.ts +147 -3
- package/lib/types.js +36 -1
- package/lib/util/computeUnits.d.ts +0 -1
- package/lib/util/tps.d.ts +0 -1
- package/lib/wallet.d.ts +0 -1
- package/package.json +11 -3
- package/src/accounts/bulkAccountLoader.ts +215 -0
- package/src/accounts/bulkUserSubscription.ts +28 -0
- package/src/accounts/pollingClearingHouseAccountSubscriber.ts +326 -0
- package/src/accounts/pollingTokenAccountSubscriber.ts +99 -0
- package/src/accounts/pollingUserAccountSubscriber.ts +194 -0
- package/src/accounts/types.ts +48 -1
- package/src/accounts/utils.ts +3 -0
- package/src/accounts/webSocketAccountSubscriber.ts +67 -17
- package/src/accounts/{defaultClearingHouseAccountSubscriber.ts → webSocketClearingHouseAccountSubscriber.ts} +51 -1
- package/src/accounts/{defaultUserAccountSubscriber.ts → webSocketUserAccountSubscriber.ts} +33 -3
- package/src/addresses.ts +37 -0
- package/src/admin.ts +92 -24
- package/src/clearingHouse.ts +455 -22
- package/src/clearingHouseUser.ts +190 -108
- package/src/constants/markets.ts +17 -1
- package/src/constants/numericConstants.ts +3 -1
- package/src/examples/makeTradeExample.ts +4 -1
- package/src/factory/clearingHouse.ts +125 -0
- package/src/factory/clearingHouseUser.ts +73 -0
- package/src/idl/clearing_house.json +1066 -39
- package/src/index.ts +11 -2
- package/src/math/amm.ts +47 -14
- package/src/math/conversion.ts +1 -1
- package/src/math/insuranceFund.ts +1 -1
- package/src/math/market.ts +28 -2
- package/src/math/orders.ts +44 -0
- package/src/math/position.ts +24 -4
- package/src/math/utils.ts +1 -1
- package/src/mockUSDCFaucet.ts +1 -1
- package/src/orderParams.ts +151 -0
- package/src/orders.ts +236 -0
- package/src/token/index.ts +37 -0
- package/src/types.ts +130 -2
- package/tsconfig.json +0 -1
- package/lib/accounts/defaultClearingHouseAccountSubscriber.d.ts.map +0 -1
- package/lib/accounts/defaultUserAccountSubscriber.d.ts.map +0 -1
- package/lib/accounts/types.d.ts.map +0 -1
- package/lib/accounts/webSocketAccountSubscriber.d.ts.map +0 -1
- package/lib/addresses.d.ts.map +0 -1
- package/lib/admin.d.ts.map +0 -1
- package/lib/assert/assert.d.ts.map +0 -1
- package/lib/clearingHouse.d.ts.map +0 -1
- package/lib/clearingHouseUser.d.ts.map +0 -1
- package/lib/config.d.ts.map +0 -1
- package/lib/constants/markets.d.ts.map +0 -1
- package/lib/constants/numericConstants.d.ts.map +0 -1
- package/lib/examples/makeTradeExample.d.ts.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/math/amm.d.ts.map +0 -1
- package/lib/math/conversion.d.ts.map +0 -1
- package/lib/math/funding.d.ts.map +0 -1
- package/lib/math/insuranceFund.d.ts.map +0 -1
- package/lib/math/market.d.ts.map +0 -1
- package/lib/math/position.d.ts.map +0 -1
- package/lib/math/trade.d.ts.map +0 -1
- package/lib/math/utils.d.ts.map +0 -1
- package/lib/mockUSDCFaucet.d.ts.map +0 -1
- package/lib/pythClient.d.ts.map +0 -1
- package/lib/tx/defaultTxSender.d.ts.map +0 -1
- package/lib/tx/types.d.ts.map +0 -1
- package/lib/tx/utils.d.ts.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/util/computeUnits.d.ts.map +0 -1
- package/lib/util/tps.d.ts.map +0 -1
- package/lib/wallet.d.ts.map +0 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { Commitment, Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { AccountData } from './types';
|
|
4
|
+
|
|
5
|
+
type AccountToLoad = {
|
|
6
|
+
publicKey: PublicKey;
|
|
7
|
+
callbacks: Map<string, (buffer: Buffer) => void>;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE = 99;
|
|
11
|
+
|
|
12
|
+
export class BulkAccountLoader {
|
|
13
|
+
connection: Connection;
|
|
14
|
+
commitment: Commitment;
|
|
15
|
+
pollingFrequency: number;
|
|
16
|
+
accountsToLoad = new Map<string, AccountToLoad>();
|
|
17
|
+
accountData = new Map<string, AccountData>();
|
|
18
|
+
errorCallbacks = new Map<string, (e) => void>();
|
|
19
|
+
intervalId?: NodeJS.Timer;
|
|
20
|
+
// to handle clients spamming load
|
|
21
|
+
loadPromise?: Promise<void>;
|
|
22
|
+
loadPromiseResolver: () => void;
|
|
23
|
+
|
|
24
|
+
public constructor(
|
|
25
|
+
connection: Connection,
|
|
26
|
+
commitment: Commitment,
|
|
27
|
+
pollingFrequency: number
|
|
28
|
+
) {
|
|
29
|
+
this.connection = connection;
|
|
30
|
+
this.commitment = commitment;
|
|
31
|
+
this.pollingFrequency = pollingFrequency;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public addAccount(
|
|
35
|
+
publicKey: PublicKey,
|
|
36
|
+
callback: (buffer: Buffer) => void
|
|
37
|
+
): string {
|
|
38
|
+
const existingSize = this.accountsToLoad.size;
|
|
39
|
+
|
|
40
|
+
const callbackId = uuidv4();
|
|
41
|
+
const existingAccountToLoad = this.accountsToLoad.get(publicKey.toString());
|
|
42
|
+
if (existingAccountToLoad) {
|
|
43
|
+
existingAccountToLoad.callbacks.set(callbackId, callback);
|
|
44
|
+
} else {
|
|
45
|
+
const callbacks = new Map<string, (buffer: Buffer) => void>();
|
|
46
|
+
callbacks.set(callbackId, callback);
|
|
47
|
+
const newAccountToLoad = {
|
|
48
|
+
publicKey,
|
|
49
|
+
callbacks,
|
|
50
|
+
};
|
|
51
|
+
this.accountsToLoad.set(publicKey.toString(), newAccountToLoad);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (existingSize === 0) {
|
|
55
|
+
this.startPolling();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// if a new account needs to be polled, remove the cached loadPromise in case client calls load immediately after
|
|
59
|
+
this.loadPromise = undefined;
|
|
60
|
+
|
|
61
|
+
return callbackId;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public removeAccount(publicKey: PublicKey, callbackId: string): void {
|
|
65
|
+
const existingAccountToLoad = this.accountsToLoad.get(publicKey.toString());
|
|
66
|
+
if (existingAccountToLoad) {
|
|
67
|
+
existingAccountToLoad.callbacks.delete(callbackId);
|
|
68
|
+
if (existingAccountToLoad.callbacks.size === 0) {
|
|
69
|
+
this.accountsToLoad.delete(existingAccountToLoad.publicKey.toString());
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (this.accountsToLoad.size === 0) {
|
|
74
|
+
this.stopPolling();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public addErrorCallbacks(callback: (error: Error) => void): string {
|
|
79
|
+
const callbackId = uuidv4();
|
|
80
|
+
this.errorCallbacks.set(callbackId, callback);
|
|
81
|
+
return callbackId;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public removeErrorCallbacks(callbackId: string): void {
|
|
85
|
+
this.errorCallbacks.delete(callbackId);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
chunks<T>(array: readonly T[], size: number): T[][] {
|
|
89
|
+
return new Array(Math.ceil(array.length / size))
|
|
90
|
+
.fill(null)
|
|
91
|
+
.map((_, index) => index * size)
|
|
92
|
+
.map((begin) => array.slice(begin, begin + size));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public async load(): Promise<void> {
|
|
96
|
+
if (this.loadPromise) {
|
|
97
|
+
return this.loadPromise;
|
|
98
|
+
}
|
|
99
|
+
this.loadPromise = new Promise((resolver) => {
|
|
100
|
+
this.loadPromiseResolver = resolver;
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
const chunks = this.chunks(
|
|
105
|
+
Array.from(this.accountsToLoad.values()),
|
|
106
|
+
GET_MULTIPLE_ACCOUNTS_CHUNK_SIZE
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
await Promise.all(
|
|
110
|
+
chunks.map((chunk) => {
|
|
111
|
+
return this.loadChunk(chunk);
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
} catch (e) {
|
|
115
|
+
console.error(`Error in bulkAccountLoader.load()`);
|
|
116
|
+
console.error(e);
|
|
117
|
+
for (const [_, callback] of this.errorCallbacks) {
|
|
118
|
+
callback(e);
|
|
119
|
+
}
|
|
120
|
+
} finally {
|
|
121
|
+
this.loadPromiseResolver();
|
|
122
|
+
this.loadPromise = undefined;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async loadChunk(accountsToLoad: AccountToLoad[]): Promise<void> {
|
|
127
|
+
if (accountsToLoad.length === 0) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const args = [
|
|
132
|
+
accountsToLoad.map((accountToLoad) => {
|
|
133
|
+
return accountToLoad.publicKey.toBase58();
|
|
134
|
+
}),
|
|
135
|
+
{ commitment: this.commitment },
|
|
136
|
+
];
|
|
137
|
+
|
|
138
|
+
let rpcResponse;
|
|
139
|
+
try {
|
|
140
|
+
// @ts-ignore
|
|
141
|
+
rpcResponse = await this.connection._rpcRequest(
|
|
142
|
+
'getMultipleAccounts',
|
|
143
|
+
args
|
|
144
|
+
);
|
|
145
|
+
} catch (e) {
|
|
146
|
+
for (const [_, callback] of this.errorCallbacks) {
|
|
147
|
+
callback(e);
|
|
148
|
+
}
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const newSlot = rpcResponse.result.context.slot;
|
|
153
|
+
|
|
154
|
+
for (const i in accountsToLoad) {
|
|
155
|
+
const accountToLoad = accountsToLoad[i];
|
|
156
|
+
const key = accountToLoad.publicKey.toString();
|
|
157
|
+
const oldRPCResponse = this.accountData.get(key);
|
|
158
|
+
|
|
159
|
+
let newBuffer: Buffer | undefined = undefined;
|
|
160
|
+
if (rpcResponse.result.value[i]) {
|
|
161
|
+
const raw: string = rpcResponse.result.value[i].data[0];
|
|
162
|
+
const dataType = rpcResponse.result.value[i].data[1];
|
|
163
|
+
newBuffer = Buffer.from(raw, dataType);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!oldRPCResponse) {
|
|
167
|
+
this.accountData.set(key, {
|
|
168
|
+
slot: newSlot,
|
|
169
|
+
buffer: newBuffer,
|
|
170
|
+
});
|
|
171
|
+
this.handleAccountCallbacks(accountToLoad, newBuffer);
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (newSlot <= oldRPCResponse.slot) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const oldBuffer = oldRPCResponse.buffer;
|
|
180
|
+
if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) {
|
|
181
|
+
this.accountData.set(key, {
|
|
182
|
+
slot: newSlot,
|
|
183
|
+
buffer: newBuffer,
|
|
184
|
+
});
|
|
185
|
+
this.handleAccountCallbacks(accountToLoad, newBuffer);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
handleAccountCallbacks(accountToLoad: AccountToLoad, buffer: Buffer): void {
|
|
191
|
+
for (const [_, callback] of accountToLoad.callbacks) {
|
|
192
|
+
callback(buffer);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
public getAccountData(publicKey: PublicKey): Buffer | undefined {
|
|
197
|
+
const accountData = this.accountData.get(publicKey.toString());
|
|
198
|
+
return accountData?.buffer;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
public startPolling(): void {
|
|
202
|
+
if (this.intervalId) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.intervalId = setInterval(this.load.bind(this), this.pollingFrequency);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
public stopPolling(): void {
|
|
210
|
+
if (this.intervalId) {
|
|
211
|
+
clearInterval(this.intervalId);
|
|
212
|
+
this.intervalId = undefined;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ClearingHouseUser } from '../clearingHouseUser';
|
|
2
|
+
import { BulkAccountLoader } from './bulkAccountLoader';
|
|
3
|
+
import { PollingUserAccountSubscriber } from './pollingUserAccountSubscriber';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param users
|
|
7
|
+
* @param accountLoader
|
|
8
|
+
*/
|
|
9
|
+
export async function bulkPollingUserSubscribe(
|
|
10
|
+
users: ClearingHouseUser[],
|
|
11
|
+
accountLoader: BulkAccountLoader
|
|
12
|
+
): Promise<void> {
|
|
13
|
+
await Promise.all(
|
|
14
|
+
users.map((user) => {
|
|
15
|
+
return (
|
|
16
|
+
user.accountSubscriber as PollingUserAccountSubscriber
|
|
17
|
+
).addToAccountLoader();
|
|
18
|
+
})
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
await accountLoader.load();
|
|
22
|
+
|
|
23
|
+
await Promise.all(
|
|
24
|
+
users.map(async (user) => {
|
|
25
|
+
return user.subscribe();
|
|
26
|
+
})
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,326 @@
|
|
|
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
|
+
OrderHistoryAccount,
|
|
19
|
+
OrderStateAccount,
|
|
20
|
+
StateAccount,
|
|
21
|
+
TradeHistoryAccount,
|
|
22
|
+
} from '../types';
|
|
23
|
+
import { getClearingHouseStateAccountPublicKey } from '../addresses';
|
|
24
|
+
import { BulkAccountLoader } from './bulkAccountLoader';
|
|
25
|
+
import { capitalize } from './utils';
|
|
26
|
+
import { ClearingHouseConfigType } from '../factory/clearingHouse';
|
|
27
|
+
|
|
28
|
+
export class PollingClearingHouseAccountSubscriber
|
|
29
|
+
implements ClearingHouseAccountSubscriber
|
|
30
|
+
{
|
|
31
|
+
isSubscribed: boolean;
|
|
32
|
+
program: Program;
|
|
33
|
+
eventEmitter: StrictEventEmitter<EventEmitter, ClearingHouseAccountEvents>;
|
|
34
|
+
|
|
35
|
+
accountLoader: BulkAccountLoader;
|
|
36
|
+
accountsToPoll = new Map<string, AccountToPoll>();
|
|
37
|
+
errorCallbackId?: string;
|
|
38
|
+
|
|
39
|
+
state?: StateAccount;
|
|
40
|
+
markets?: MarketsAccount;
|
|
41
|
+
orderState?: OrderStateAccount;
|
|
42
|
+
tradeHistory?: TradeHistoryAccount;
|
|
43
|
+
depositHistory?: DepositHistoryAccount;
|
|
44
|
+
fundingPaymentHistory?: FundingPaymentHistoryAccount;
|
|
45
|
+
fundingRateHistory?: FundingRateHistoryAccount;
|
|
46
|
+
liquidationHistory?: LiquidationHistoryAccount;
|
|
47
|
+
extendedCurveHistory: ExtendedCurveHistoryAccount;
|
|
48
|
+
orderHistory?: OrderHistoryAccount;
|
|
49
|
+
|
|
50
|
+
optionalExtraSubscriptions: ClearingHouseAccountTypes[] = [];
|
|
51
|
+
|
|
52
|
+
type: ClearingHouseConfigType = 'polling';
|
|
53
|
+
|
|
54
|
+
private isSubscribing = false;
|
|
55
|
+
private subscriptionPromise: Promise<boolean>;
|
|
56
|
+
private subscriptionPromiseResolver: (val: boolean) => void;
|
|
57
|
+
|
|
58
|
+
public constructor(program: Program, accountLoader: BulkAccountLoader) {
|
|
59
|
+
this.isSubscribed = false;
|
|
60
|
+
this.program = program;
|
|
61
|
+
this.eventEmitter = new EventEmitter();
|
|
62
|
+
this.accountLoader = accountLoader;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async subscribe(
|
|
66
|
+
optionalSubscriptions?: ClearingHouseAccountTypes[]
|
|
67
|
+
): Promise<boolean> {
|
|
68
|
+
if (this.isSubscribed) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (this.isSubscribing) {
|
|
73
|
+
return await this.subscriptionPromise;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.optionalExtraSubscriptions = optionalSubscriptions;
|
|
77
|
+
|
|
78
|
+
this.isSubscribing = true;
|
|
79
|
+
|
|
80
|
+
this.subscriptionPromise = new Promise((res) => {
|
|
81
|
+
this.subscriptionPromiseResolver = res;
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await this.updateAccountsToPoll();
|
|
85
|
+
await this.addToAccountLoader();
|
|
86
|
+
await this.fetch();
|
|
87
|
+
this.eventEmitter.emit('update');
|
|
88
|
+
|
|
89
|
+
this.isSubscribing = false;
|
|
90
|
+
this.isSubscribed = true;
|
|
91
|
+
this.subscriptionPromiseResolver(true);
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async updateAccountsToPoll(): Promise<void> {
|
|
97
|
+
if (this.accountsToPoll.size > 0) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const statePublicKey = await getClearingHouseStateAccountPublicKey(
|
|
102
|
+
this.program.programId
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
const state = (await this.program.account.state.fetch(
|
|
106
|
+
statePublicKey
|
|
107
|
+
)) as StateAccount;
|
|
108
|
+
|
|
109
|
+
this.accountsToPoll.set(statePublicKey.toString(), {
|
|
110
|
+
key: 'state',
|
|
111
|
+
publicKey: statePublicKey,
|
|
112
|
+
eventType: 'stateAccountUpdate',
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
this.accountsToPoll.set(state.markets.toString(), {
|
|
116
|
+
key: 'markets',
|
|
117
|
+
publicKey: state.markets,
|
|
118
|
+
eventType: 'marketsAccountUpdate',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
this.accountsToPoll.set(state.orderState.toString(), {
|
|
122
|
+
key: 'orderState',
|
|
123
|
+
publicKey: state.orderState,
|
|
124
|
+
eventType: 'orderStateAccountUpdate',
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (this.optionalExtraSubscriptions?.includes('tradeHistoryAccount')) {
|
|
128
|
+
this.accountsToPoll.set(state.tradeHistory.toString(), {
|
|
129
|
+
key: 'tradeHistory',
|
|
130
|
+
publicKey: state.tradeHistory,
|
|
131
|
+
eventType: 'tradeHistoryAccountUpdate',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (this.optionalExtraSubscriptions?.includes('depositHistoryAccount')) {
|
|
136
|
+
this.accountsToPoll.set(state.depositHistory.toString(), {
|
|
137
|
+
key: 'depositHistory',
|
|
138
|
+
publicKey: state.depositHistory,
|
|
139
|
+
eventType: 'depositHistoryAccountUpdate',
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
this.optionalExtraSubscriptions?.includes('fundingPaymentHistoryAccount')
|
|
145
|
+
) {
|
|
146
|
+
this.accountsToPoll.set(state.fundingPaymentHistory.toString(), {
|
|
147
|
+
key: 'fundingPaymentHistory',
|
|
148
|
+
publicKey: state.fundingPaymentHistory,
|
|
149
|
+
eventType: 'fundingPaymentHistoryAccountUpdate',
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (
|
|
154
|
+
this.optionalExtraSubscriptions?.includes('fundingRateHistoryAccount')
|
|
155
|
+
) {
|
|
156
|
+
this.accountsToPoll.set(state.fundingRateHistory.toString(), {
|
|
157
|
+
key: 'fundingRateHistory',
|
|
158
|
+
publicKey: state.fundingRateHistory,
|
|
159
|
+
eventType: 'fundingRateHistoryAccountUpdate',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (this.optionalExtraSubscriptions?.includes('curveHistoryAccount')) {
|
|
164
|
+
this.accountsToPoll.set(state.extendedCurveHistory.toString(), {
|
|
165
|
+
key: 'extendedCurveHistory',
|
|
166
|
+
publicKey: state.extendedCurveHistory,
|
|
167
|
+
eventType: 'curveHistoryAccountUpdate',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (
|
|
172
|
+
this.optionalExtraSubscriptions?.includes('liquidationHistoryAccount')
|
|
173
|
+
) {
|
|
174
|
+
this.accountsToPoll.set(state.liquidationHistory.toString(), {
|
|
175
|
+
key: 'liquidationHistory',
|
|
176
|
+
publicKey: state.liquidationHistory,
|
|
177
|
+
eventType: 'liquidationHistoryAccountUpdate',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (this.optionalExtraSubscriptions?.includes('orderHistoryAccount')) {
|
|
182
|
+
const orderState = (await this.program.account.orderState.fetch(
|
|
183
|
+
state.orderState
|
|
184
|
+
)) as OrderStateAccount;
|
|
185
|
+
|
|
186
|
+
this.accountsToPoll.set(orderState.orderHistory.toString(), {
|
|
187
|
+
key: 'orderHistory',
|
|
188
|
+
publicKey: orderState.orderHistory,
|
|
189
|
+
eventType: 'orderHistoryAccountUpdate',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async addToAccountLoader(): Promise<void> {
|
|
195
|
+
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
196
|
+
accountToPoll.callbackId = this.accountLoader.addAccount(
|
|
197
|
+
accountToPoll.publicKey,
|
|
198
|
+
(buffer) => {
|
|
199
|
+
const account = this.program.account[
|
|
200
|
+
accountToPoll.key
|
|
201
|
+
].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
|
|
202
|
+
this[accountToPoll.key] = account;
|
|
203
|
+
// @ts-ignore
|
|
204
|
+
this.eventEmitter.emit(accountToPoll.eventType, account);
|
|
205
|
+
this.eventEmitter.emit('update');
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
|
|
211
|
+
this.eventEmitter.emit('error', error);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
public async fetch(): Promise<void> {
|
|
216
|
+
await this.accountLoader.load();
|
|
217
|
+
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
218
|
+
const buffer = this.accountLoader.getAccountData(accountToPoll.publicKey);
|
|
219
|
+
if (buffer) {
|
|
220
|
+
this[accountToPoll.key] = this.program.account[
|
|
221
|
+
accountToPoll.key
|
|
222
|
+
].coder.accounts.decode(capitalize(accountToPoll.key), buffer);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
public async unsubscribe(): Promise<void> {
|
|
228
|
+
if (!this.isSubscribed) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
233
|
+
this.accountLoader.removeAccount(
|
|
234
|
+
accountToPoll.publicKey,
|
|
235
|
+
accountToPoll.callbackId
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
|
|
240
|
+
this.errorCallbackId = undefined;
|
|
241
|
+
|
|
242
|
+
this.accountsToPoll.clear();
|
|
243
|
+
this.isSubscribed = false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
assertIsSubscribed(): void {
|
|
247
|
+
if (!this.isSubscribed) {
|
|
248
|
+
throw new NotSubscribedError(
|
|
249
|
+
'You must call `subscribe` before using this function'
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
assertOptionalIsSubscribed(
|
|
255
|
+
optionalSubscription: ClearingHouseAccountTypes
|
|
256
|
+
): void {
|
|
257
|
+
if (!this.isSubscribed) {
|
|
258
|
+
throw new NotSubscribedError(
|
|
259
|
+
'You must call `subscribe` before using this function'
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (!this.optionalExtraSubscriptions.includes(optionalSubscription)) {
|
|
264
|
+
throw new NotSubscribedError(
|
|
265
|
+
`You need to subscribe to the optional Clearing House account "${optionalSubscription}" to use this method`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
public getStateAccount(): StateAccount {
|
|
271
|
+
this.assertIsSubscribed();
|
|
272
|
+
return this.state;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
public getMarketsAccount(): MarketsAccount {
|
|
276
|
+
this.assertIsSubscribed();
|
|
277
|
+
return this.markets;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
public getOrderStateAccount(): OrderStateAccount {
|
|
281
|
+
this.assertIsSubscribed();
|
|
282
|
+
return this.orderState;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
public getTradeHistoryAccount(): TradeHistoryAccount {
|
|
286
|
+
this.assertIsSubscribed();
|
|
287
|
+
this.assertOptionalIsSubscribed('tradeHistoryAccount');
|
|
288
|
+
return this.tradeHistory;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
public getDepositHistoryAccount(): DepositHistoryAccount {
|
|
292
|
+
this.assertIsSubscribed();
|
|
293
|
+
this.assertOptionalIsSubscribed('depositHistoryAccount');
|
|
294
|
+
return this.depositHistory;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
public getFundingPaymentHistoryAccount(): FundingPaymentHistoryAccount {
|
|
298
|
+
this.assertIsSubscribed();
|
|
299
|
+
this.assertOptionalIsSubscribed('fundingPaymentHistoryAccount');
|
|
300
|
+
return this.fundingPaymentHistory;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
public getFundingRateHistoryAccount(): FundingRateHistoryAccount {
|
|
304
|
+
this.assertIsSubscribed();
|
|
305
|
+
this.assertOptionalIsSubscribed('fundingRateHistoryAccount');
|
|
306
|
+
return this.fundingRateHistory;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
public getCurveHistoryAccount(): ExtendedCurveHistoryAccount {
|
|
310
|
+
this.assertIsSubscribed();
|
|
311
|
+
this.assertOptionalIsSubscribed('curveHistoryAccount');
|
|
312
|
+
return this.extendedCurveHistory;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
public getLiquidationHistoryAccount(): LiquidationHistoryAccount {
|
|
316
|
+
this.assertIsSubscribed();
|
|
317
|
+
this.assertOptionalIsSubscribed('liquidationHistoryAccount');
|
|
318
|
+
return this.liquidationHistory;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
public getOrderHistoryAccount(): OrderHistoryAccount {
|
|
322
|
+
this.assertIsSubscribed();
|
|
323
|
+
this.assertOptionalIsSubscribed('orderHistoryAccount');
|
|
324
|
+
return this.orderHistory;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -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
|
+
}
|