@drift-labs/sdk-browser 2.109.0-beta.5 → 2.109.0-beta.7
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/VERSION +1 -1
- package/lib/browser/driftClient.d.ts +2 -2
- package/lib/browser/driftClient.js +4 -4
- package/lib/browser/userMap/userMap.d.ts +10 -0
- package/lib/browser/userMap/userMap.js +10 -0
- package/lib/browser/userMap/userStatsMap.d.ts +17 -2
- package/lib/browser/userMap/userStatsMap.js +108 -1
- package/lib/node/driftClient.d.ts +2 -2
- package/lib/node/driftClient.js +4 -4
- package/lib/node/userMap/userMap.d.ts +10 -0
- package/lib/node/userMap/userMap.js +10 -0
- package/lib/node/userMap/userStatsMap.d.ts +17 -2
- package/lib/node/userMap/userStatsMap.js +108 -1
- package/package.json +1 -1
- package/src/driftClient.ts +6 -3
- package/src/userMap/userMap.ts +10 -0
- package/src/userMap/userStatsMap.ts +148 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.109.0-beta.
|
|
1
|
+
2.109.0-beta.7
|
|
@@ -888,8 +888,8 @@ export declare class DriftClient {
|
|
|
888
888
|
disableUserHighLeverageMode(user: PublicKey, userAccount?: UserAccount, txParams?: TxParams): Promise<TransactionSignature>;
|
|
889
889
|
getDisableHighLeverageModeIx(user: PublicKey, userAccount?: UserAccount): Promise<TransactionInstruction>;
|
|
890
890
|
fetchHighLeverageModeConfig(): Promise<HighLeverageModeConfig>;
|
|
891
|
-
updateUserProtectedMakerOrders(subAccountId: number, protectedOrders: boolean, txParams?: TxParams): Promise<TransactionSignature>;
|
|
892
|
-
getUpdateUserProtectedMakerOrdersIx(subAccountId: number, protectedOrders: boolean): Promise<TransactionInstruction>;
|
|
891
|
+
updateUserProtectedMakerOrders(subAccountId: number, protectedOrders: boolean, authority?: PublicKey, txParams?: TxParams): Promise<TransactionSignature>;
|
|
892
|
+
getUpdateUserProtectedMakerOrdersIx(subAccountId: number, protectedOrders: boolean, authority?: PublicKey): Promise<TransactionInstruction>;
|
|
893
893
|
getPauseSpotMarketDepositWithdrawIx(spotMarketIndex: number): Promise<TransactionInstruction>;
|
|
894
894
|
pauseSpotMarketDepositWithdraw(spotMarketIndex: number, txParams?: TxParams): Promise<TransactionSignature>;
|
|
895
895
|
private handleSignedTransaction;
|
|
@@ -4847,15 +4847,15 @@ class DriftClient {
|
|
|
4847
4847
|
const config = await this.program.account.highLeverageModeConfig.fetch((0, pda_1.getHighLeverageModeConfigPublicKey)(this.program.programId));
|
|
4848
4848
|
return config;
|
|
4849
4849
|
}
|
|
4850
|
-
async updateUserProtectedMakerOrders(subAccountId, protectedOrders, txParams) {
|
|
4851
|
-
const { txSig } = await this.sendTransaction(await this.buildTransaction(await this.getUpdateUserProtectedMakerOrdersIx(subAccountId, protectedOrders), txParams), [], this.opts);
|
|
4850
|
+
async updateUserProtectedMakerOrders(subAccountId, protectedOrders, authority, txParams) {
|
|
4851
|
+
const { txSig } = await this.sendTransaction(await this.buildTransaction(await this.getUpdateUserProtectedMakerOrdersIx(subAccountId, protectedOrders, authority), txParams), [], this.opts);
|
|
4852
4852
|
return txSig;
|
|
4853
4853
|
}
|
|
4854
|
-
async getUpdateUserProtectedMakerOrdersIx(subAccountId, protectedOrders) {
|
|
4854
|
+
async getUpdateUserProtectedMakerOrdersIx(subAccountId, protectedOrders, authority) {
|
|
4855
4855
|
const ix = await this.program.instruction.updateUserProtectedMakerOrders(subAccountId, protectedOrders, {
|
|
4856
4856
|
accounts: {
|
|
4857
4857
|
state: await this.getStatePublicKey(),
|
|
4858
|
-
user: (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.
|
|
4858
|
+
user: (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, authority !== null && authority !== void 0 ? authority : this.authority, subAccountId),
|
|
4859
4859
|
authority: this.wallet.publicKey,
|
|
4860
4860
|
protectedMakerModeConfig: (0, pda_1.getProtectedMakerModeConfigPublicKey)(this.program.programId),
|
|
4861
4861
|
},
|
|
@@ -81,7 +81,17 @@ export declare class UserMap implements UserMapInterface {
|
|
|
81
81
|
*/
|
|
82
82
|
getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[];
|
|
83
83
|
sync(): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Syncs the UserMap using the default sync method (single getProgramAccounts call with filters).
|
|
86
|
+
* This method may fail when drift has too many users. (nodejs response size limits)
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
84
89
|
private defaultSync;
|
|
90
|
+
/**
|
|
91
|
+
* Syncs the UserMap using the paginated sync method (multiple getMultipleAccounts calls with filters).
|
|
92
|
+
* This method is more reliable when drift has many users.
|
|
93
|
+
* @returns
|
|
94
|
+
*/
|
|
85
95
|
private paginatedSync;
|
|
86
96
|
unsubscribe(): Promise<void>;
|
|
87
97
|
updateUserAccount(key: string, userAccount: UserAccount, slot: number): Promise<void>;
|
|
@@ -259,6 +259,11 @@ class UserMap {
|
|
|
259
259
|
return this.paginatedSync();
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Syncs the UserMap using the default sync method (single getProgramAccounts call with filters).
|
|
264
|
+
* This method may fail when drift has too many users. (nodejs response size limits)
|
|
265
|
+
* @returns
|
|
266
|
+
*/
|
|
262
267
|
async defaultSync() {
|
|
263
268
|
var _a;
|
|
264
269
|
if (this.syncPromise) {
|
|
@@ -334,6 +339,11 @@ class UserMap {
|
|
|
334
339
|
this.syncPromise = undefined;
|
|
335
340
|
}
|
|
336
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Syncs the UserMap using the paginated sync method (multiple getMultipleAccounts calls with filters).
|
|
344
|
+
* This method is more reliable when drift has many users.
|
|
345
|
+
* @returns
|
|
346
|
+
*/
|
|
337
347
|
async paginatedSync() {
|
|
338
348
|
var _a, _b;
|
|
339
349
|
if (this.syncPromise) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DriftClient, OrderRecord, UserStatsAccount, UserStats, WrappedEvent, BulkAccountLoader } from '..';
|
|
1
|
+
import { DriftClient, OrderRecord, UserStatsAccount, UserStats, WrappedEvent, BulkAccountLoader, SyncConfig } from '..';
|
|
2
2
|
import { PublicKey } from '@solana/web3.js';
|
|
3
3
|
import { UserMap } from './userMap';
|
|
4
4
|
export declare class UserStatsMap {
|
|
@@ -8,13 +8,17 @@ export declare class UserStatsMap {
|
|
|
8
8
|
private userStatsMap;
|
|
9
9
|
private driftClient;
|
|
10
10
|
private bulkAccountLoader;
|
|
11
|
+
private decode;
|
|
12
|
+
private syncConfig;
|
|
13
|
+
private syncPromise?;
|
|
14
|
+
private syncPromiseResolver;
|
|
11
15
|
/**
|
|
12
16
|
* Creates a new UserStatsMap instance.
|
|
13
17
|
*
|
|
14
18
|
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
15
19
|
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
16
20
|
*/
|
|
17
|
-
constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader);
|
|
21
|
+
constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader, syncConfig?: SyncConfig);
|
|
18
22
|
subscribe(authorities: PublicKey[]): Promise<void>;
|
|
19
23
|
/**
|
|
20
24
|
*
|
|
@@ -42,5 +46,16 @@ export declare class UserStatsMap {
|
|
|
42
46
|
* You may want to get this list from UserMap in order to filter out idle users
|
|
43
47
|
*/
|
|
44
48
|
sync(authorities: PublicKey[]): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Sync the UserStatsMap using the default sync method, which loads individual users into the bulkAccountLoader and
|
|
51
|
+
* loads them. (bulkAccountLoader uses batch getMultipleAccounts)
|
|
52
|
+
* @param authorities
|
|
53
|
+
*/
|
|
54
|
+
private defaultSync;
|
|
55
|
+
/**
|
|
56
|
+
* Sync the UserStatsMap using the paginated sync method, which uses multiple getMultipleAccounts calls (without RPC batching), and limits concurrency.
|
|
57
|
+
* @param authorities
|
|
58
|
+
*/
|
|
59
|
+
private paginatedSync;
|
|
45
60
|
unsubscribe(): Promise<void>;
|
|
46
61
|
}
|
|
@@ -10,7 +10,7 @@ class UserStatsMap {
|
|
|
10
10
|
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
11
11
|
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
12
12
|
*/
|
|
13
|
-
constructor(driftClient, bulkAccountLoader) {
|
|
13
|
+
constructor(driftClient, bulkAccountLoader, syncConfig) {
|
|
14
14
|
/**
|
|
15
15
|
* map from authority pubkey to UserStats
|
|
16
16
|
*/
|
|
@@ -20,6 +20,11 @@ class UserStatsMap {
|
|
|
20
20
|
bulkAccountLoader = new __1.BulkAccountLoader(driftClient.connection, driftClient.opts.commitment, 0);
|
|
21
21
|
}
|
|
22
22
|
this.bulkAccountLoader = bulkAccountLoader;
|
|
23
|
+
this.syncConfig = syncConfig !== null && syncConfig !== void 0 ? syncConfig : {
|
|
24
|
+
type: 'default',
|
|
25
|
+
};
|
|
26
|
+
this.decode =
|
|
27
|
+
this.driftClient.program.account.userStats.coder.accounts.decodeUnchecked.bind(this.driftClient.program.account.userStats.coder.accounts);
|
|
23
28
|
}
|
|
24
29
|
async subscribe(authorities) {
|
|
25
30
|
if (this.size() > 0) {
|
|
@@ -152,9 +157,111 @@ class UserStatsMap {
|
|
|
152
157
|
* You may want to get this list from UserMap in order to filter out idle users
|
|
153
158
|
*/
|
|
154
159
|
async sync(authorities) {
|
|
160
|
+
if (this.syncConfig.type === 'default') {
|
|
161
|
+
return this.defaultSync(authorities);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
return this.paginatedSync(authorities);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Sync the UserStatsMap using the default sync method, which loads individual users into the bulkAccountLoader and
|
|
169
|
+
* loads them. (bulkAccountLoader uses batch getMultipleAccounts)
|
|
170
|
+
* @param authorities
|
|
171
|
+
*/
|
|
172
|
+
async defaultSync(authorities) {
|
|
155
173
|
await Promise.all(authorities.map((authority) => this.addUserStat(authority, undefined, true)));
|
|
156
174
|
await this.bulkAccountLoader.load();
|
|
157
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Sync the UserStatsMap using the paginated sync method, which uses multiple getMultipleAccounts calls (without RPC batching), and limits concurrency.
|
|
178
|
+
* @param authorities
|
|
179
|
+
*/
|
|
180
|
+
async paginatedSync(authorities) {
|
|
181
|
+
var _a, _b;
|
|
182
|
+
if (this.syncPromise) {
|
|
183
|
+
return this.syncPromise;
|
|
184
|
+
}
|
|
185
|
+
this.syncPromise = new Promise((resolve) => {
|
|
186
|
+
this.syncPromiseResolver = resolve;
|
|
187
|
+
});
|
|
188
|
+
try {
|
|
189
|
+
let accountsToLoad = authorities;
|
|
190
|
+
if (authorities.length === 0) {
|
|
191
|
+
const accountsPrefetch = await this.driftClient.connection.getProgramAccounts(this.driftClient.program.programId, {
|
|
192
|
+
dataSlice: { offset: 0, length: 0 },
|
|
193
|
+
filters: [(0, __1.getUserStatsFilter)()],
|
|
194
|
+
});
|
|
195
|
+
accountsToLoad = accountsPrefetch.map((account) => account.pubkey);
|
|
196
|
+
}
|
|
197
|
+
const limitConcurrency = async (tasks, limit) => {
|
|
198
|
+
const executing = [];
|
|
199
|
+
const results = [];
|
|
200
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
201
|
+
const executor = Promise.resolve().then(tasks[i]);
|
|
202
|
+
results.push(executor);
|
|
203
|
+
if (executing.length < limit) {
|
|
204
|
+
executing.push(executor);
|
|
205
|
+
executor.finally(() => {
|
|
206
|
+
const index = executing.indexOf(executor);
|
|
207
|
+
if (index > -1) {
|
|
208
|
+
executing.splice(index, 1);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
await Promise.race(executing);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return Promise.all(results);
|
|
217
|
+
};
|
|
218
|
+
const programAccountBufferMap = new Set();
|
|
219
|
+
// @ts-ignore
|
|
220
|
+
const chunkSize = (_a = this.syncConfig.chunkSize) !== null && _a !== void 0 ? _a : 100;
|
|
221
|
+
const tasks = [];
|
|
222
|
+
for (let i = 0; i < accountsToLoad.length; i += chunkSize) {
|
|
223
|
+
const chunk = accountsToLoad.slice(i, i + chunkSize);
|
|
224
|
+
tasks.push(async () => {
|
|
225
|
+
const accountInfos = await this.driftClient.connection.getMultipleAccountsInfoAndContext(chunk, {
|
|
226
|
+
commitment: this.driftClient.opts.commitment,
|
|
227
|
+
});
|
|
228
|
+
for (let j = 0; j < accountInfos.value.length; j += 1) {
|
|
229
|
+
const accountInfo = accountInfos.value[j];
|
|
230
|
+
if (accountInfo === null)
|
|
231
|
+
continue;
|
|
232
|
+
const publicKeyString = chunk[j].toString();
|
|
233
|
+
if (!this.has(publicKeyString)) {
|
|
234
|
+
const buffer = Buffer.from(accountInfo.data);
|
|
235
|
+
const decodedUserStats = this.decode('UserStats', buffer);
|
|
236
|
+
programAccountBufferMap.add(decodedUserStats.authority.toBase58());
|
|
237
|
+
this.addUserStat(decodedUserStats.authority, decodedUserStats, false);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
// @ts-ignore
|
|
243
|
+
const concurrencyLimit = (_b = this.syncConfig.concurrencyLimit) !== null && _b !== void 0 ? _b : 10;
|
|
244
|
+
await limitConcurrency(tasks, concurrencyLimit);
|
|
245
|
+
for (const [key] of this.userStatsMap.entries()) {
|
|
246
|
+
if (!programAccountBufferMap.has(key)) {
|
|
247
|
+
const user = this.get(key);
|
|
248
|
+
if (user) {
|
|
249
|
+
await user.unsubscribe();
|
|
250
|
+
this.userStatsMap.delete(key);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
console.error(`Error in UserStatsMap.paginatedSync():`, err);
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
if (this.syncPromiseResolver) {
|
|
260
|
+
this.syncPromiseResolver();
|
|
261
|
+
}
|
|
262
|
+
this.syncPromise = undefined;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
158
265
|
async unsubscribe() {
|
|
159
266
|
for (const [key, userStats] of this.userStatsMap.entries()) {
|
|
160
267
|
await userStats.unsubscribe();
|
|
@@ -888,8 +888,8 @@ export declare class DriftClient {
|
|
|
888
888
|
disableUserHighLeverageMode(user: PublicKey, userAccount?: UserAccount, txParams?: TxParams): Promise<TransactionSignature>;
|
|
889
889
|
getDisableHighLeverageModeIx(user: PublicKey, userAccount?: UserAccount): Promise<TransactionInstruction>;
|
|
890
890
|
fetchHighLeverageModeConfig(): Promise<HighLeverageModeConfig>;
|
|
891
|
-
updateUserProtectedMakerOrders(subAccountId: number, protectedOrders: boolean, txParams?: TxParams): Promise<TransactionSignature>;
|
|
892
|
-
getUpdateUserProtectedMakerOrdersIx(subAccountId: number, protectedOrders: boolean): Promise<TransactionInstruction>;
|
|
891
|
+
updateUserProtectedMakerOrders(subAccountId: number, protectedOrders: boolean, authority?: PublicKey, txParams?: TxParams): Promise<TransactionSignature>;
|
|
892
|
+
getUpdateUserProtectedMakerOrdersIx(subAccountId: number, protectedOrders: boolean, authority?: PublicKey): Promise<TransactionInstruction>;
|
|
893
893
|
getPauseSpotMarketDepositWithdrawIx(spotMarketIndex: number): Promise<TransactionInstruction>;
|
|
894
894
|
pauseSpotMarketDepositWithdraw(spotMarketIndex: number, txParams?: TxParams): Promise<TransactionSignature>;
|
|
895
895
|
private handleSignedTransaction;
|
package/lib/node/driftClient.js
CHANGED
|
@@ -4847,15 +4847,15 @@ class DriftClient {
|
|
|
4847
4847
|
const config = await this.program.account.highLeverageModeConfig.fetch((0, pda_1.getHighLeverageModeConfigPublicKey)(this.program.programId));
|
|
4848
4848
|
return config;
|
|
4849
4849
|
}
|
|
4850
|
-
async updateUserProtectedMakerOrders(subAccountId, protectedOrders, txParams) {
|
|
4851
|
-
const { txSig } = await this.sendTransaction(await this.buildTransaction(await this.getUpdateUserProtectedMakerOrdersIx(subAccountId, protectedOrders), txParams), [], this.opts);
|
|
4850
|
+
async updateUserProtectedMakerOrders(subAccountId, protectedOrders, authority, txParams) {
|
|
4851
|
+
const { txSig } = await this.sendTransaction(await this.buildTransaction(await this.getUpdateUserProtectedMakerOrdersIx(subAccountId, protectedOrders, authority), txParams), [], this.opts);
|
|
4852
4852
|
return txSig;
|
|
4853
4853
|
}
|
|
4854
|
-
async getUpdateUserProtectedMakerOrdersIx(subAccountId, protectedOrders) {
|
|
4854
|
+
async getUpdateUserProtectedMakerOrdersIx(subAccountId, protectedOrders, authority) {
|
|
4855
4855
|
const ix = await this.program.instruction.updateUserProtectedMakerOrders(subAccountId, protectedOrders, {
|
|
4856
4856
|
accounts: {
|
|
4857
4857
|
state: await this.getStatePublicKey(),
|
|
4858
|
-
user: (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, this.
|
|
4858
|
+
user: (0, pda_1.getUserAccountPublicKeySync)(this.program.programId, authority !== null && authority !== void 0 ? authority : this.authority, subAccountId),
|
|
4859
4859
|
authority: this.wallet.publicKey,
|
|
4860
4860
|
protectedMakerModeConfig: (0, pda_1.getProtectedMakerModeConfigPublicKey)(this.program.programId),
|
|
4861
4861
|
},
|
|
@@ -81,7 +81,17 @@ export declare class UserMap implements UserMapInterface {
|
|
|
81
81
|
*/
|
|
82
82
|
getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[];
|
|
83
83
|
sync(): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Syncs the UserMap using the default sync method (single getProgramAccounts call with filters).
|
|
86
|
+
* This method may fail when drift has too many users. (nodejs response size limits)
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
84
89
|
private defaultSync;
|
|
90
|
+
/**
|
|
91
|
+
* Syncs the UserMap using the paginated sync method (multiple getMultipleAccounts calls with filters).
|
|
92
|
+
* This method is more reliable when drift has many users.
|
|
93
|
+
* @returns
|
|
94
|
+
*/
|
|
85
95
|
private paginatedSync;
|
|
86
96
|
unsubscribe(): Promise<void>;
|
|
87
97
|
updateUserAccount(key: string, userAccount: UserAccount, slot: number): Promise<void>;
|
|
@@ -259,6 +259,11 @@ class UserMap {
|
|
|
259
259
|
return this.paginatedSync();
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Syncs the UserMap using the default sync method (single getProgramAccounts call with filters).
|
|
264
|
+
* This method may fail when drift has too many users. (nodejs response size limits)
|
|
265
|
+
* @returns
|
|
266
|
+
*/
|
|
262
267
|
async defaultSync() {
|
|
263
268
|
var _a;
|
|
264
269
|
if (this.syncPromise) {
|
|
@@ -334,6 +339,11 @@ class UserMap {
|
|
|
334
339
|
this.syncPromise = undefined;
|
|
335
340
|
}
|
|
336
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Syncs the UserMap using the paginated sync method (multiple getMultipleAccounts calls with filters).
|
|
344
|
+
* This method is more reliable when drift has many users.
|
|
345
|
+
* @returns
|
|
346
|
+
*/
|
|
337
347
|
async paginatedSync() {
|
|
338
348
|
var _a, _b;
|
|
339
349
|
if (this.syncPromise) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DriftClient, OrderRecord, UserStatsAccount, UserStats, WrappedEvent, BulkAccountLoader } from '..';
|
|
1
|
+
import { DriftClient, OrderRecord, UserStatsAccount, UserStats, WrappedEvent, BulkAccountLoader, SyncConfig } from '..';
|
|
2
2
|
import { PublicKey } from '@solana/web3.js';
|
|
3
3
|
import { UserMap } from './userMap';
|
|
4
4
|
export declare class UserStatsMap {
|
|
@@ -8,13 +8,17 @@ export declare class UserStatsMap {
|
|
|
8
8
|
private userStatsMap;
|
|
9
9
|
private driftClient;
|
|
10
10
|
private bulkAccountLoader;
|
|
11
|
+
private decode;
|
|
12
|
+
private syncConfig;
|
|
13
|
+
private syncPromise?;
|
|
14
|
+
private syncPromiseResolver;
|
|
11
15
|
/**
|
|
12
16
|
* Creates a new UserStatsMap instance.
|
|
13
17
|
*
|
|
14
18
|
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
15
19
|
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
16
20
|
*/
|
|
17
|
-
constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader);
|
|
21
|
+
constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader, syncConfig?: SyncConfig);
|
|
18
22
|
subscribe(authorities: PublicKey[]): Promise<void>;
|
|
19
23
|
/**
|
|
20
24
|
*
|
|
@@ -42,5 +46,16 @@ export declare class UserStatsMap {
|
|
|
42
46
|
* You may want to get this list from UserMap in order to filter out idle users
|
|
43
47
|
*/
|
|
44
48
|
sync(authorities: PublicKey[]): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Sync the UserStatsMap using the default sync method, which loads individual users into the bulkAccountLoader and
|
|
51
|
+
* loads them. (bulkAccountLoader uses batch getMultipleAccounts)
|
|
52
|
+
* @param authorities
|
|
53
|
+
*/
|
|
54
|
+
private defaultSync;
|
|
55
|
+
/**
|
|
56
|
+
* Sync the UserStatsMap using the paginated sync method, which uses multiple getMultipleAccounts calls (without RPC batching), and limits concurrency.
|
|
57
|
+
* @param authorities
|
|
58
|
+
*/
|
|
59
|
+
private paginatedSync;
|
|
45
60
|
unsubscribe(): Promise<void>;
|
|
46
61
|
}
|
|
@@ -10,7 +10,7 @@ class UserStatsMap {
|
|
|
10
10
|
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
11
11
|
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
12
12
|
*/
|
|
13
|
-
constructor(driftClient, bulkAccountLoader) {
|
|
13
|
+
constructor(driftClient, bulkAccountLoader, syncConfig) {
|
|
14
14
|
/**
|
|
15
15
|
* map from authority pubkey to UserStats
|
|
16
16
|
*/
|
|
@@ -20,6 +20,11 @@ class UserStatsMap {
|
|
|
20
20
|
bulkAccountLoader = new __1.BulkAccountLoader(driftClient.connection, driftClient.opts.commitment, 0);
|
|
21
21
|
}
|
|
22
22
|
this.bulkAccountLoader = bulkAccountLoader;
|
|
23
|
+
this.syncConfig = syncConfig !== null && syncConfig !== void 0 ? syncConfig : {
|
|
24
|
+
type: 'default',
|
|
25
|
+
};
|
|
26
|
+
this.decode =
|
|
27
|
+
this.driftClient.program.account.userStats.coder.accounts.decodeUnchecked.bind(this.driftClient.program.account.userStats.coder.accounts);
|
|
23
28
|
}
|
|
24
29
|
async subscribe(authorities) {
|
|
25
30
|
if (this.size() > 0) {
|
|
@@ -152,9 +157,111 @@ class UserStatsMap {
|
|
|
152
157
|
* You may want to get this list from UserMap in order to filter out idle users
|
|
153
158
|
*/
|
|
154
159
|
async sync(authorities) {
|
|
160
|
+
if (this.syncConfig.type === 'default') {
|
|
161
|
+
return this.defaultSync(authorities);
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
return this.paginatedSync(authorities);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Sync the UserStatsMap using the default sync method, which loads individual users into the bulkAccountLoader and
|
|
169
|
+
* loads them. (bulkAccountLoader uses batch getMultipleAccounts)
|
|
170
|
+
* @param authorities
|
|
171
|
+
*/
|
|
172
|
+
async defaultSync(authorities) {
|
|
155
173
|
await Promise.all(authorities.map((authority) => this.addUserStat(authority, undefined, true)));
|
|
156
174
|
await this.bulkAccountLoader.load();
|
|
157
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Sync the UserStatsMap using the paginated sync method, which uses multiple getMultipleAccounts calls (without RPC batching), and limits concurrency.
|
|
178
|
+
* @param authorities
|
|
179
|
+
*/
|
|
180
|
+
async paginatedSync(authorities) {
|
|
181
|
+
var _a, _b;
|
|
182
|
+
if (this.syncPromise) {
|
|
183
|
+
return this.syncPromise;
|
|
184
|
+
}
|
|
185
|
+
this.syncPromise = new Promise((resolve) => {
|
|
186
|
+
this.syncPromiseResolver = resolve;
|
|
187
|
+
});
|
|
188
|
+
try {
|
|
189
|
+
let accountsToLoad = authorities;
|
|
190
|
+
if (authorities.length === 0) {
|
|
191
|
+
const accountsPrefetch = await this.driftClient.connection.getProgramAccounts(this.driftClient.program.programId, {
|
|
192
|
+
dataSlice: { offset: 0, length: 0 },
|
|
193
|
+
filters: [(0, __1.getUserStatsFilter)()],
|
|
194
|
+
});
|
|
195
|
+
accountsToLoad = accountsPrefetch.map((account) => account.pubkey);
|
|
196
|
+
}
|
|
197
|
+
const limitConcurrency = async (tasks, limit) => {
|
|
198
|
+
const executing = [];
|
|
199
|
+
const results = [];
|
|
200
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
201
|
+
const executor = Promise.resolve().then(tasks[i]);
|
|
202
|
+
results.push(executor);
|
|
203
|
+
if (executing.length < limit) {
|
|
204
|
+
executing.push(executor);
|
|
205
|
+
executor.finally(() => {
|
|
206
|
+
const index = executing.indexOf(executor);
|
|
207
|
+
if (index > -1) {
|
|
208
|
+
executing.splice(index, 1);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
await Promise.race(executing);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return Promise.all(results);
|
|
217
|
+
};
|
|
218
|
+
const programAccountBufferMap = new Set();
|
|
219
|
+
// @ts-ignore
|
|
220
|
+
const chunkSize = (_a = this.syncConfig.chunkSize) !== null && _a !== void 0 ? _a : 100;
|
|
221
|
+
const tasks = [];
|
|
222
|
+
for (let i = 0; i < accountsToLoad.length; i += chunkSize) {
|
|
223
|
+
const chunk = accountsToLoad.slice(i, i + chunkSize);
|
|
224
|
+
tasks.push(async () => {
|
|
225
|
+
const accountInfos = await this.driftClient.connection.getMultipleAccountsInfoAndContext(chunk, {
|
|
226
|
+
commitment: this.driftClient.opts.commitment,
|
|
227
|
+
});
|
|
228
|
+
for (let j = 0; j < accountInfos.value.length; j += 1) {
|
|
229
|
+
const accountInfo = accountInfos.value[j];
|
|
230
|
+
if (accountInfo === null)
|
|
231
|
+
continue;
|
|
232
|
+
const publicKeyString = chunk[j].toString();
|
|
233
|
+
if (!this.has(publicKeyString)) {
|
|
234
|
+
const buffer = Buffer.from(accountInfo.data);
|
|
235
|
+
const decodedUserStats = this.decode('UserStats', buffer);
|
|
236
|
+
programAccountBufferMap.add(decodedUserStats.authority.toBase58());
|
|
237
|
+
this.addUserStat(decodedUserStats.authority, decodedUserStats, false);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
// @ts-ignore
|
|
243
|
+
const concurrencyLimit = (_b = this.syncConfig.concurrencyLimit) !== null && _b !== void 0 ? _b : 10;
|
|
244
|
+
await limitConcurrency(tasks, concurrencyLimit);
|
|
245
|
+
for (const [key] of this.userStatsMap.entries()) {
|
|
246
|
+
if (!programAccountBufferMap.has(key)) {
|
|
247
|
+
const user = this.get(key);
|
|
248
|
+
if (user) {
|
|
249
|
+
await user.unsubscribe();
|
|
250
|
+
this.userStatsMap.delete(key);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
console.error(`Error in UserStatsMap.paginatedSync():`, err);
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
if (this.syncPromiseResolver) {
|
|
260
|
+
this.syncPromiseResolver();
|
|
261
|
+
}
|
|
262
|
+
this.syncPromise = undefined;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
158
265
|
async unsubscribe() {
|
|
159
266
|
for (const [key, userStats] of this.userStatsMap.entries()) {
|
|
160
267
|
await userStats.unsubscribe();
|
package/package.json
CHANGED
package/src/driftClient.ts
CHANGED
|
@@ -9209,13 +9209,15 @@ export class DriftClient {
|
|
|
9209
9209
|
public async updateUserProtectedMakerOrders(
|
|
9210
9210
|
subAccountId: number,
|
|
9211
9211
|
protectedOrders: boolean,
|
|
9212
|
+
authority?: PublicKey,
|
|
9212
9213
|
txParams?: TxParams
|
|
9213
9214
|
): Promise<TransactionSignature> {
|
|
9214
9215
|
const { txSig } = await this.sendTransaction(
|
|
9215
9216
|
await this.buildTransaction(
|
|
9216
9217
|
await this.getUpdateUserProtectedMakerOrdersIx(
|
|
9217
9218
|
subAccountId,
|
|
9218
|
-
protectedOrders
|
|
9219
|
+
protectedOrders,
|
|
9220
|
+
authority
|
|
9219
9221
|
),
|
|
9220
9222
|
txParams
|
|
9221
9223
|
),
|
|
@@ -9227,7 +9229,8 @@ export class DriftClient {
|
|
|
9227
9229
|
|
|
9228
9230
|
public async getUpdateUserProtectedMakerOrdersIx(
|
|
9229
9231
|
subAccountId: number,
|
|
9230
|
-
protectedOrders: boolean
|
|
9232
|
+
protectedOrders: boolean,
|
|
9233
|
+
authority?: PublicKey
|
|
9231
9234
|
): Promise<TransactionInstruction> {
|
|
9232
9235
|
const ix = await this.program.instruction.updateUserProtectedMakerOrders(
|
|
9233
9236
|
subAccountId,
|
|
@@ -9237,7 +9240,7 @@ export class DriftClient {
|
|
|
9237
9240
|
state: await this.getStatePublicKey(),
|
|
9238
9241
|
user: getUserAccountPublicKeySync(
|
|
9239
9242
|
this.program.programId,
|
|
9240
|
-
this.
|
|
9243
|
+
authority ?? this.authority,
|
|
9241
9244
|
subAccountId
|
|
9242
9245
|
),
|
|
9243
9246
|
authority: this.wallet.publicKey,
|
package/src/userMap/userMap.ts
CHANGED
|
@@ -388,6 +388,11 @@ export class UserMap implements UserMapInterface {
|
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
390
|
|
|
391
|
+
/**
|
|
392
|
+
* Syncs the UserMap using the default sync method (single getProgramAccounts call with filters).
|
|
393
|
+
* This method may fail when drift has too many users. (nodejs response size limits)
|
|
394
|
+
* @returns
|
|
395
|
+
*/
|
|
391
396
|
private async defaultSync() {
|
|
392
397
|
if (this.syncPromise) {
|
|
393
398
|
return this.syncPromise;
|
|
@@ -487,6 +492,11 @@ export class UserMap implements UserMapInterface {
|
|
|
487
492
|
}
|
|
488
493
|
}
|
|
489
494
|
|
|
495
|
+
/**
|
|
496
|
+
* Syncs the UserMap using the paginated sync method (multiple getMultipleAccounts calls with filters).
|
|
497
|
+
* This method is more reliable when drift has many users.
|
|
498
|
+
* @returns
|
|
499
|
+
*/
|
|
490
500
|
private async paginatedSync() {
|
|
491
501
|
if (this.syncPromise) {
|
|
492
502
|
return this.syncPromise;
|
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
InsuranceFundStakeRecord,
|
|
16
16
|
BulkAccountLoader,
|
|
17
17
|
PollingUserStatsAccountSubscriber,
|
|
18
|
+
SyncConfig,
|
|
19
|
+
getUserStatsFilter,
|
|
18
20
|
} from '..';
|
|
19
21
|
import { PublicKey } from '@solana/web3.js';
|
|
20
22
|
|
|
@@ -27,6 +29,11 @@ export class UserStatsMap {
|
|
|
27
29
|
private userStatsMap = new Map<string, UserStats>();
|
|
28
30
|
private driftClient: DriftClient;
|
|
29
31
|
private bulkAccountLoader: BulkAccountLoader;
|
|
32
|
+
private decode;
|
|
33
|
+
private syncConfig: SyncConfig;
|
|
34
|
+
|
|
35
|
+
private syncPromise?: Promise<void>;
|
|
36
|
+
private syncPromiseResolver: () => void;
|
|
30
37
|
|
|
31
38
|
/**
|
|
32
39
|
* Creates a new UserStatsMap instance.
|
|
@@ -34,7 +41,11 @@ export class UserStatsMap {
|
|
|
34
41
|
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
35
42
|
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
36
43
|
*/
|
|
37
|
-
constructor(
|
|
44
|
+
constructor(
|
|
45
|
+
driftClient: DriftClient,
|
|
46
|
+
bulkAccountLoader?: BulkAccountLoader,
|
|
47
|
+
syncConfig?: SyncConfig
|
|
48
|
+
) {
|
|
38
49
|
this.driftClient = driftClient;
|
|
39
50
|
if (!bulkAccountLoader) {
|
|
40
51
|
bulkAccountLoader = new BulkAccountLoader(
|
|
@@ -44,6 +55,15 @@ export class UserStatsMap {
|
|
|
44
55
|
);
|
|
45
56
|
}
|
|
46
57
|
this.bulkAccountLoader = bulkAccountLoader;
|
|
58
|
+
|
|
59
|
+
this.syncConfig = syncConfig ?? {
|
|
60
|
+
type: 'default',
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
this.decode =
|
|
64
|
+
this.driftClient.program.account.userStats.coder.accounts.decodeUnchecked.bind(
|
|
65
|
+
this.driftClient.program.account.userStats.coder.accounts
|
|
66
|
+
);
|
|
47
67
|
}
|
|
48
68
|
|
|
49
69
|
public async subscribe(authorities: PublicKey[]) {
|
|
@@ -201,6 +221,19 @@ export class UserStatsMap {
|
|
|
201
221
|
* You may want to get this list from UserMap in order to filter out idle users
|
|
202
222
|
*/
|
|
203
223
|
public async sync(authorities: PublicKey[]) {
|
|
224
|
+
if (this.syncConfig.type === 'default') {
|
|
225
|
+
return this.defaultSync(authorities);
|
|
226
|
+
} else {
|
|
227
|
+
return this.paginatedSync(authorities);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Sync the UserStatsMap using the default sync method, which loads individual users into the bulkAccountLoader and
|
|
233
|
+
* loads them. (bulkAccountLoader uses batch getMultipleAccounts)
|
|
234
|
+
* @param authorities
|
|
235
|
+
*/
|
|
236
|
+
private async defaultSync(authorities: PublicKey[]) {
|
|
204
237
|
await Promise.all(
|
|
205
238
|
authorities.map((authority) =>
|
|
206
239
|
this.addUserStat(authority, undefined, true)
|
|
@@ -209,6 +242,120 @@ export class UserStatsMap {
|
|
|
209
242
|
await this.bulkAccountLoader.load();
|
|
210
243
|
}
|
|
211
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Sync the UserStatsMap using the paginated sync method, which uses multiple getMultipleAccounts calls (without RPC batching), and limits concurrency.
|
|
247
|
+
* @param authorities
|
|
248
|
+
*/
|
|
249
|
+
private async paginatedSync(authorities: PublicKey[]) {
|
|
250
|
+
if (this.syncPromise) {
|
|
251
|
+
return this.syncPromise;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
this.syncPromise = new Promise<void>((resolve) => {
|
|
255
|
+
this.syncPromiseResolver = resolve;
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
let accountsToLoad = authorities;
|
|
260
|
+
if (authorities.length === 0) {
|
|
261
|
+
const accountsPrefetch =
|
|
262
|
+
await this.driftClient.connection.getProgramAccounts(
|
|
263
|
+
this.driftClient.program.programId,
|
|
264
|
+
{
|
|
265
|
+
dataSlice: { offset: 0, length: 0 },
|
|
266
|
+
filters: [getUserStatsFilter()],
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
accountsToLoad = accountsPrefetch.map((account) => account.pubkey);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const limitConcurrency = async (tasks, limit) => {
|
|
273
|
+
const executing = [];
|
|
274
|
+
const results = [];
|
|
275
|
+
|
|
276
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
277
|
+
const executor = Promise.resolve().then(tasks[i]);
|
|
278
|
+
results.push(executor);
|
|
279
|
+
|
|
280
|
+
if (executing.length < limit) {
|
|
281
|
+
executing.push(executor);
|
|
282
|
+
executor.finally(() => {
|
|
283
|
+
const index = executing.indexOf(executor);
|
|
284
|
+
if (index > -1) {
|
|
285
|
+
executing.splice(index, 1);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
} else {
|
|
289
|
+
await Promise.race(executing);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return Promise.all(results);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const programAccountBufferMap = new Set<string>();
|
|
297
|
+
|
|
298
|
+
// @ts-ignore
|
|
299
|
+
const chunkSize = this.syncConfig.chunkSize ?? 100;
|
|
300
|
+
const tasks = [];
|
|
301
|
+
for (let i = 0; i < accountsToLoad.length; i += chunkSize) {
|
|
302
|
+
const chunk = accountsToLoad.slice(i, i + chunkSize);
|
|
303
|
+
tasks.push(async () => {
|
|
304
|
+
const accountInfos =
|
|
305
|
+
await this.driftClient.connection.getMultipleAccountsInfoAndContext(
|
|
306
|
+
chunk,
|
|
307
|
+
{
|
|
308
|
+
commitment: this.driftClient.opts.commitment,
|
|
309
|
+
}
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
for (let j = 0; j < accountInfos.value.length; j += 1) {
|
|
313
|
+
const accountInfo = accountInfos.value[j];
|
|
314
|
+
if (accountInfo === null) continue;
|
|
315
|
+
|
|
316
|
+
const publicKeyString = chunk[j].toString();
|
|
317
|
+
if (!this.has(publicKeyString)) {
|
|
318
|
+
const buffer = Buffer.from(accountInfo.data);
|
|
319
|
+
const decodedUserStats = this.decode(
|
|
320
|
+
'UserStats',
|
|
321
|
+
buffer
|
|
322
|
+
) as UserStatsAccount;
|
|
323
|
+
programAccountBufferMap.add(
|
|
324
|
+
decodedUserStats.authority.toBase58()
|
|
325
|
+
);
|
|
326
|
+
this.addUserStat(
|
|
327
|
+
decodedUserStats.authority,
|
|
328
|
+
decodedUserStats,
|
|
329
|
+
false
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// @ts-ignore
|
|
337
|
+
const concurrencyLimit = this.syncConfig.concurrencyLimit ?? 10;
|
|
338
|
+
await limitConcurrency(tasks, concurrencyLimit);
|
|
339
|
+
|
|
340
|
+
for (const [key] of this.userStatsMap.entries()) {
|
|
341
|
+
if (!programAccountBufferMap.has(key)) {
|
|
342
|
+
const user = this.get(key);
|
|
343
|
+
if (user) {
|
|
344
|
+
await user.unsubscribe();
|
|
345
|
+
this.userStatsMap.delete(key);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} catch (err) {
|
|
350
|
+
console.error(`Error in UserStatsMap.paginatedSync():`, err);
|
|
351
|
+
} finally {
|
|
352
|
+
if (this.syncPromiseResolver) {
|
|
353
|
+
this.syncPromiseResolver();
|
|
354
|
+
}
|
|
355
|
+
this.syncPromise = undefined;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
212
359
|
public async unsubscribe() {
|
|
213
360
|
for (const [key, userStats] of this.userStatsMap.entries()) {
|
|
214
361
|
await userStats.unsubscribe();
|