@drift-labs/sdk 2.48.0-beta.1 → 2.48.0-beta.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/VERSION +1 -1
- package/lib/accounts/pollingUserAccountSubscriber.js +9 -7
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +9 -7
- package/lib/driftClient.js +6 -6
- package/lib/userMap/userMap.d.ts +14 -4
- package/lib/userMap/userMap.js +75 -41
- package/lib/userMap/userStatsMap.d.ts +29 -8
- package/lib/userMap/userStatsMap.js +47 -41
- package/package.json +1 -1
- package/src/accounts/pollingUserAccountSubscriber.ts +13 -11
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +13 -12
- package/src/driftClient.ts +7 -6
- package/src/userMap/userMap.ts +124 -66
- package/src/userMap/userStatsMap.ts +65 -69
- package/tests/amm/test.ts +3 -2
- package/tests/dlob/test.ts +8 -5
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.48.0-beta.
|
|
1
|
+
2.48.0-beta.3
|
|
@@ -53,12 +53,12 @@ class PollingUserAccountSubscriber {
|
|
|
53
53
|
}
|
|
54
54
|
async fetch() {
|
|
55
55
|
var _a, _b;
|
|
56
|
-
await this.accountLoader.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
const dataAndContext = await this.program.account.user.fetchAndContext(this.userAccountPublicKey, this.accountLoader.commitment);
|
|
57
|
+
if ((_b = dataAndContext.context.slot > ((_a = this.user) === null || _a === void 0 ? void 0 : _a.slot)) !== null && _b !== void 0 ? _b : 0) {
|
|
58
|
+
this.user = {
|
|
59
|
+
data: dataAndContext.data,
|
|
60
|
+
slot: dataAndContext.context.slot,
|
|
61
|
+
};
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
doesAccountExist() {
|
|
@@ -80,7 +80,9 @@ class PollingUserAccountSubscriber {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
getUserAccountAndSlot() {
|
|
83
|
-
this.
|
|
83
|
+
if (!this.doesAccountExist()) {
|
|
84
|
+
throw new types_1.NotSubscribedError('You must call `subscribe` or `fetch` before using this function');
|
|
85
|
+
}
|
|
84
86
|
return this.user;
|
|
85
87
|
}
|
|
86
88
|
updateData(userAccount, slot) {
|
|
@@ -53,12 +53,12 @@ class PollingUserStatsAccountSubscriber {
|
|
|
53
53
|
}
|
|
54
54
|
async fetch() {
|
|
55
55
|
var _a, _b;
|
|
56
|
-
await this.accountLoader.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
const dataAndContext = await this.program.account.userStats.fetchAndContext(this.userStatsAccountPublicKey, this.accountLoader.commitment);
|
|
57
|
+
if ((_b = dataAndContext.context.slot > ((_a = this.userStats) === null || _a === void 0 ? void 0 : _a.slot)) !== null && _b !== void 0 ? _b : 0) {
|
|
58
|
+
this.userStats = {
|
|
59
|
+
data: dataAndContext.data,
|
|
60
|
+
slot: dataAndContext.context.slot,
|
|
61
|
+
};
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
doesAccountExist() {
|
|
@@ -80,7 +80,9 @@ class PollingUserStatsAccountSubscriber {
|
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
getUserStatsAccountAndSlot() {
|
|
83
|
-
this.
|
|
83
|
+
if (!this.doesAccountExist()) {
|
|
84
|
+
throw new types_1.NotSubscribedError('You must call `subscribe` or `fetch` before using this function');
|
|
85
|
+
}
|
|
84
86
|
return this.userStats;
|
|
85
87
|
}
|
|
86
88
|
}
|
package/lib/driftClient.js
CHANGED
|
@@ -2647,9 +2647,9 @@ class DriftClient {
|
|
|
2647
2647
|
auctionDuration: auctionDuration || 0,
|
|
2648
2648
|
auctionStartPrice: auctionStartPrice || numericConstants_1.ZERO,
|
|
2649
2649
|
auctionEndPrice: auctionEndPrice || numericConstants_1.ZERO,
|
|
2650
|
-
reduceOnly: reduceOnly
|
|
2651
|
-
postOnly: postOnly
|
|
2652
|
-
immediateOrCancel: immediateOrCancel
|
|
2650
|
+
reduceOnly: reduceOnly != undefined ? reduceOnly : null,
|
|
2651
|
+
postOnly: postOnly != undefined ? postOnly : null,
|
|
2652
|
+
immediateOrCancel: immediateOrCancel != undefined ? immediateOrCancel : null,
|
|
2653
2653
|
policy: policy || null,
|
|
2654
2654
|
maxTs: maxTs || null,
|
|
2655
2655
|
};
|
|
@@ -2698,9 +2698,9 @@ class DriftClient {
|
|
|
2698
2698
|
oraclePriceOffset: newOraclePriceOffset || null,
|
|
2699
2699
|
triggerPrice: newTriggerPrice || null,
|
|
2700
2700
|
triggerCondition: newTriggerCondition || null,
|
|
2701
|
-
auctionDuration: auctionDuration ||
|
|
2702
|
-
auctionStartPrice: auctionStartPrice ||
|
|
2703
|
-
auctionEndPrice: auctionEndPrice ||
|
|
2701
|
+
auctionDuration: auctionDuration || null,
|
|
2702
|
+
auctionStartPrice: auctionStartPrice || null,
|
|
2703
|
+
auctionEndPrice: auctionEndPrice || null,
|
|
2704
2704
|
reduceOnly: reduceOnly || false,
|
|
2705
2705
|
postOnly: postOnly || null,
|
|
2706
2706
|
immediateOrCancel: immediateOrCancel || false,
|
package/lib/userMap/userMap.d.ts
CHANGED
|
@@ -11,20 +11,30 @@ export interface UserMapInterface {
|
|
|
11
11
|
updateWithOrderRecord(record: OrderRecord): Promise<void>;
|
|
12
12
|
values(): IterableIterator<User>;
|
|
13
13
|
}
|
|
14
|
+
export type SyncCallbackCriteria = {
|
|
15
|
+
hasOpenOrders: boolean;
|
|
16
|
+
};
|
|
14
17
|
export declare class UserMap implements UserMapInterface {
|
|
15
18
|
private userMap;
|
|
16
19
|
private driftClient;
|
|
17
20
|
private accountSubscription;
|
|
18
21
|
private includeIdle;
|
|
19
22
|
private lastNumberOfSubAccounts;
|
|
23
|
+
private stateAccountUpdateCallback;
|
|
20
24
|
private syncCallback;
|
|
25
|
+
private syncCallbackCriteria;
|
|
26
|
+
private syncPromise?;
|
|
27
|
+
private syncPromiseResolver;
|
|
21
28
|
/**
|
|
29
|
+
* Constructs a new UserMap instance.
|
|
22
30
|
*
|
|
23
|
-
* @param driftClient
|
|
24
|
-
* @param accountSubscription
|
|
25
|
-
* @param includeIdle
|
|
31
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
32
|
+
* @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
|
|
33
|
+
* @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
|
|
34
|
+
* @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
|
|
35
|
+
* @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
|
|
26
36
|
*/
|
|
27
|
-
constructor(driftClient: DriftClient, accountSubscription: UserSubscriptionConfig, includeIdle?: boolean);
|
|
37
|
+
constructor(driftClient: DriftClient, accountSubscription: UserSubscriptionConfig, includeIdle?: boolean, syncCallback?: (authorities: PublicKey[]) => Promise<void>, syncCallbackCriteria?: SyncCallbackCriteria);
|
|
28
38
|
subscribe(): Promise<void>;
|
|
29
39
|
addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount): Promise<void>;
|
|
30
40
|
has(key: string): boolean;
|
package/lib/userMap/userMap.js
CHANGED
|
@@ -7,14 +7,17 @@ const buffer_1 = require("buffer");
|
|
|
7
7
|
const memcmp_1 = require("../memcmp");
|
|
8
8
|
class UserMap {
|
|
9
9
|
/**
|
|
10
|
+
* Constructs a new UserMap instance.
|
|
10
11
|
*
|
|
11
|
-
* @param driftClient
|
|
12
|
-
* @param accountSubscription
|
|
13
|
-
* @param includeIdle
|
|
12
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
13
|
+
* @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
|
|
14
|
+
* @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
|
|
15
|
+
* @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
|
|
16
|
+
* @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
|
|
14
17
|
*/
|
|
15
|
-
constructor(driftClient, accountSubscription, includeIdle = false) {
|
|
18
|
+
constructor(driftClient, accountSubscription, includeIdle = false, syncCallback, syncCallbackCriteria = { hasOpenOrders: false }) {
|
|
16
19
|
this.userMap = new Map();
|
|
17
|
-
this.
|
|
20
|
+
this.stateAccountUpdateCallback = async (state) => {
|
|
18
21
|
if (state.numberOfSubAccounts !== this.lastNumberOfSubAccounts) {
|
|
19
22
|
await this.sync();
|
|
20
23
|
this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
|
|
@@ -23,6 +26,8 @@ class UserMap {
|
|
|
23
26
|
this.driftClient = driftClient;
|
|
24
27
|
this.accountSubscription = accountSubscription;
|
|
25
28
|
this.includeIdle = includeIdle;
|
|
29
|
+
this.syncCallback = syncCallback;
|
|
30
|
+
this.syncCallbackCriteria = syncCallbackCriteria;
|
|
26
31
|
}
|
|
27
32
|
async subscribe() {
|
|
28
33
|
if (this.size() > 0) {
|
|
@@ -31,7 +36,7 @@ class UserMap {
|
|
|
31
36
|
await this.driftClient.subscribe();
|
|
32
37
|
this.lastNumberOfSubAccounts =
|
|
33
38
|
this.driftClient.getStateAccount().numberOfSubAccounts;
|
|
34
|
-
this.driftClient.eventEmitter.on('stateAccountUpdate', this.
|
|
39
|
+
this.driftClient.eventEmitter.on('stateAccountUpdate', this.stateAccountUpdateCallback);
|
|
35
40
|
await this.sync();
|
|
36
41
|
}
|
|
37
42
|
async addPubkey(userAccountPublicKey, userAccount) {
|
|
@@ -140,44 +145,73 @@ class UserMap {
|
|
|
140
145
|
return this.userMap.size;
|
|
141
146
|
}
|
|
142
147
|
async sync() {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
filters.push((0, memcmp_1.getNonIdleUserFilter)());
|
|
146
|
-
}
|
|
147
|
-
const rpcRequestArgs = [
|
|
148
|
-
this.driftClient.program.programId.toBase58(),
|
|
149
|
-
{
|
|
150
|
-
commitment: this.driftClient.connection.commitment,
|
|
151
|
-
filters,
|
|
152
|
-
encoding: 'base64',
|
|
153
|
-
withContext: true,
|
|
154
|
-
},
|
|
155
|
-
];
|
|
156
|
-
// @ts-ignore
|
|
157
|
-
const rpcJSONResponse = await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
158
|
-
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
159
|
-
const slot = rpcResponseAndContext.context.slot;
|
|
160
|
-
const programAccountBufferMap = new Map();
|
|
161
|
-
for (const programAccount of rpcResponseAndContext.value) {
|
|
162
|
-
programAccountBufferMap.set(programAccount.pubkey.toString(),
|
|
163
|
-
// @ts-ignore
|
|
164
|
-
buffer_1.Buffer.from(programAccount.account.data[0], programAccount.account.data[1]));
|
|
148
|
+
if (this.syncPromise) {
|
|
149
|
+
return this.syncPromise;
|
|
165
150
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
151
|
+
this.syncPromise = new Promise((resolver) => {
|
|
152
|
+
this.syncPromiseResolver = resolver;
|
|
153
|
+
});
|
|
154
|
+
try {
|
|
155
|
+
const filters = [(0, memcmp_1.getUserFilter)()];
|
|
156
|
+
if (!this.includeIdle) {
|
|
157
|
+
filters.push((0, memcmp_1.getNonIdleUserFilter)());
|
|
170
158
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
159
|
+
const rpcRequestArgs = [
|
|
160
|
+
this.driftClient.program.programId.toBase58(),
|
|
161
|
+
{
|
|
162
|
+
commitment: this.driftClient.connection.commitment,
|
|
163
|
+
filters,
|
|
164
|
+
encoding: 'base64',
|
|
165
|
+
withContext: true,
|
|
166
|
+
},
|
|
167
|
+
];
|
|
168
|
+
const rpcJSONResponse =
|
|
169
|
+
// @ts-ignore
|
|
170
|
+
await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
171
|
+
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
172
|
+
const slot = rpcResponseAndContext.context.slot;
|
|
173
|
+
const programAccountBufferMap = new Map();
|
|
174
|
+
for (const programAccount of rpcResponseAndContext.value) {
|
|
175
|
+
programAccountBufferMap.set(programAccount.pubkey.toString(),
|
|
176
|
+
// @ts-ignore
|
|
177
|
+
buffer_1.Buffer.from(programAccount.account.data[0], programAccount.account.data[1]));
|
|
176
178
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
179
|
+
for (const [key, buffer] of programAccountBufferMap.entries()) {
|
|
180
|
+
if (!this.has(key)) {
|
|
181
|
+
const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', buffer);
|
|
182
|
+
await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
|
|
183
|
+
}
|
|
180
184
|
}
|
|
185
|
+
for (const [key, user] of this.userMap.entries()) {
|
|
186
|
+
if (!programAccountBufferMap.has(key)) {
|
|
187
|
+
await user.unsubscribe();
|
|
188
|
+
this.userMap.delete(key);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', programAccountBufferMap.get(key));
|
|
192
|
+
user.accountSubscriber.updateData(userAccount, slot);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (this.syncCallback) {
|
|
196
|
+
const usersMeetingCriteria = Array.from(this.userMap.values()).filter((user) => {
|
|
197
|
+
let pass = true;
|
|
198
|
+
if (this.syncCallbackCriteria.hasOpenOrders) {
|
|
199
|
+
pass = pass && user.getUserAccount().hasOpenOrder;
|
|
200
|
+
}
|
|
201
|
+
return pass;
|
|
202
|
+
});
|
|
203
|
+
const userAuths = new Set(usersMeetingCriteria.map((user) => user.getUserAccount().authority.toBase58()));
|
|
204
|
+
const userAuthKeys = Array.from(userAuths).map((userAuth) => new web3_js_1.PublicKey(userAuth));
|
|
205
|
+
await this.syncCallback(userAuthKeys);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
catch (e) {
|
|
209
|
+
console.error(`Error in UserMap.sync()`);
|
|
210
|
+
console.error(e);
|
|
211
|
+
}
|
|
212
|
+
finally {
|
|
213
|
+
this.syncPromiseResolver();
|
|
214
|
+
this.syncPromise = undefined;
|
|
181
215
|
}
|
|
182
216
|
}
|
|
183
217
|
async unsubscribe() {
|
|
@@ -186,7 +220,7 @@ class UserMap {
|
|
|
186
220
|
this.userMap.delete(key);
|
|
187
221
|
}
|
|
188
222
|
if (this.lastNumberOfSubAccounts) {
|
|
189
|
-
this.driftClient.eventEmitter.removeListener('stateAccountUpdate', this.
|
|
223
|
+
this.driftClient.eventEmitter.removeListener('stateAccountUpdate', this.stateAccountUpdateCallback);
|
|
190
224
|
this.lastNumberOfSubAccounts = undefined;
|
|
191
225
|
}
|
|
192
226
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DriftClient, OrderRecord, UserStatsAccount, UserStats,
|
|
1
|
+
import { DriftClient, OrderRecord, UserStatsAccount, UserStats, WrappedEvent, BulkAccountLoader } from '..';
|
|
2
2
|
import { PublicKey } from '@solana/web3.js';
|
|
3
3
|
import { UserMap } from './userMap';
|
|
4
4
|
export declare class UserStatsMap {
|
|
@@ -7,19 +7,40 @@ export declare class UserStatsMap {
|
|
|
7
7
|
*/
|
|
8
8
|
private userStatsMap;
|
|
9
9
|
private driftClient;
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
private bulkAccountLoader;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new UserStatsMap instance.
|
|
13
|
+
*
|
|
14
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
15
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
16
|
+
*/
|
|
17
|
+
constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader);
|
|
18
|
+
subscribe(authorities: PublicKey[]): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @param authority that owns the UserStatsAccount
|
|
22
|
+
* @param userStatsAccount optional UserStatsAccount to subscribe to, if undefined will be fetched later
|
|
23
|
+
* @param skipFetch if true, will not immediately fetch the UserStatsAccount
|
|
24
|
+
*/
|
|
25
|
+
addUserStat(authority: PublicKey, userStatsAccount?: UserStatsAccount, skipFetch?: boolean): Promise<void>;
|
|
16
26
|
updateWithOrderRecord(record: OrderRecord, userMap: UserMap): Promise<void>;
|
|
17
27
|
updateWithEventRecord(record: WrappedEvent<any>, userMap?: UserMap): Promise<void>;
|
|
18
28
|
has(authorityPublicKey: string): boolean;
|
|
19
29
|
get(authorityPublicKey: string): UserStats;
|
|
30
|
+
/**
|
|
31
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
32
|
+
* reading one from the blockchain if necessary.
|
|
33
|
+
* @param authorityPublicKey
|
|
34
|
+
* @returns
|
|
35
|
+
*/
|
|
20
36
|
mustGet(authorityPublicKey: string): Promise<UserStats>;
|
|
21
37
|
values(): IterableIterator<UserStats>;
|
|
22
38
|
size(): number;
|
|
23
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Sync the UserStatsMap
|
|
41
|
+
* @param authorities list of authorities to derive UserStatsAccount public keys from.
|
|
42
|
+
* You may want to get this list from UserMap in order to filter out idle users
|
|
43
|
+
*/
|
|
44
|
+
sync(authorities: PublicKey[]): Promise<void>;
|
|
24
45
|
unsubscribe(): Promise<void>;
|
|
25
46
|
}
|
|
@@ -4,43 +4,57 @@ exports.UserStatsMap = void 0;
|
|
|
4
4
|
const __1 = require("..");
|
|
5
5
|
const web3_js_1 = require("@solana/web3.js");
|
|
6
6
|
class UserStatsMap {
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new UserStatsMap instance.
|
|
9
|
+
*
|
|
10
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
11
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
12
|
+
*/
|
|
13
|
+
constructor(driftClient, bulkAccountLoader) {
|
|
8
14
|
/**
|
|
9
15
|
* map from authority pubkey to UserStats
|
|
10
16
|
*/
|
|
11
17
|
this.userStatsMap = new Map();
|
|
12
|
-
this.syncCallback = async (state) => {
|
|
13
|
-
if (state.numberOfAuthorities !== this.lastNumberOfAuthorities) {
|
|
14
|
-
await this.sync();
|
|
15
|
-
this.lastNumberOfAuthorities = state.numberOfAuthorities;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
18
|
this.driftClient = driftClient;
|
|
19
|
-
|
|
19
|
+
if (!bulkAccountLoader) {
|
|
20
|
+
bulkAccountLoader = new __1.BulkAccountLoader(driftClient.connection, driftClient.opts.commitment, 0);
|
|
21
|
+
}
|
|
22
|
+
this.bulkAccountLoader = bulkAccountLoader;
|
|
20
23
|
}
|
|
21
|
-
async subscribe() {
|
|
24
|
+
async subscribe(authorities) {
|
|
22
25
|
if (this.size() > 0) {
|
|
23
26
|
return;
|
|
24
27
|
}
|
|
25
28
|
await this.driftClient.subscribe();
|
|
26
|
-
this.
|
|
27
|
-
this.driftClient.getStateAccount().numberOfAuthorities;
|
|
28
|
-
this.driftClient.eventEmitter.on('stateAccountUpdate', this.syncCallback);
|
|
29
|
-
await this.sync();
|
|
29
|
+
await this.sync(authorities);
|
|
30
30
|
}
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
* @param authority that owns the UserStatsAccount
|
|
34
|
+
* @param userStatsAccount optional UserStatsAccount to subscribe to, if undefined will be fetched later
|
|
35
|
+
* @param skipFetch if true, will not immediately fetch the UserStatsAccount
|
|
36
|
+
*/
|
|
37
|
+
async addUserStat(authority, userStatsAccount, skipFetch) {
|
|
32
38
|
const userStat = new __1.UserStats({
|
|
33
39
|
driftClient: this.driftClient,
|
|
34
40
|
userStatsAccountPublicKey: (0, __1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, authority),
|
|
35
|
-
accountSubscription:
|
|
41
|
+
accountSubscription: {
|
|
42
|
+
type: 'polling',
|
|
43
|
+
accountLoader: this.bulkAccountLoader,
|
|
44
|
+
},
|
|
36
45
|
});
|
|
37
|
-
|
|
46
|
+
if (skipFetch) {
|
|
47
|
+
await userStat.accountSubscriber.addToAccountLoader();
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
await userStat.subscribe(userStatsAccount);
|
|
51
|
+
}
|
|
38
52
|
this.userStatsMap.set(authority.toString(), userStat);
|
|
39
53
|
}
|
|
40
54
|
async updateWithOrderRecord(record, userMap) {
|
|
41
55
|
const user = await userMap.mustGet(record.user.toString());
|
|
42
56
|
if (!this.has(user.getUserAccount().authority.toString())) {
|
|
43
|
-
await this.addUserStat(user.getUserAccount().authority);
|
|
57
|
+
await this.addUserStat(user.getUserAccount().authority, undefined, false);
|
|
44
58
|
}
|
|
45
59
|
}
|
|
46
60
|
async updateWithEventRecord(record, userMap) {
|
|
@@ -114,9 +128,15 @@ class UserStatsMap {
|
|
|
114
128
|
get(authorityPublicKey) {
|
|
115
129
|
return this.userStatsMap.get(authorityPublicKey);
|
|
116
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
133
|
+
* reading one from the blockchain if necessary.
|
|
134
|
+
* @param authorityPublicKey
|
|
135
|
+
* @returns
|
|
136
|
+
*/
|
|
117
137
|
async mustGet(authorityPublicKey) {
|
|
118
138
|
if (!this.has(authorityPublicKey)) {
|
|
119
|
-
await this.addUserStat(new web3_js_1.PublicKey(authorityPublicKey));
|
|
139
|
+
await this.addUserStat(new web3_js_1.PublicKey(authorityPublicKey), undefined, false);
|
|
120
140
|
}
|
|
121
141
|
return this.get(authorityPublicKey);
|
|
122
142
|
}
|
|
@@ -126,35 +146,21 @@ class UserStatsMap {
|
|
|
126
146
|
size() {
|
|
127
147
|
return this.userStatsMap.size;
|
|
128
148
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
const programAccountMap = new Map();
|
|
139
|
-
for (const programAccount of programAccounts) {
|
|
140
|
-
programAccountMap.set(new web3_js_1.PublicKey(programAccount.account.data.slice(8, 40)).toString(), programAccount.account);
|
|
141
|
-
}
|
|
142
|
-
for (const key of programAccountMap.keys()) {
|
|
143
|
-
if (!this.has(key)) {
|
|
144
|
-
const userStatsAccount = this.driftClient.program.account.userStats.coder.accounts.decode('UserStats', programAccountMap.get(key).data);
|
|
145
|
-
await this.addUserStat(new web3_js_1.PublicKey(key), userStatsAccount);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
149
|
+
/**
|
|
150
|
+
* Sync the UserStatsMap
|
|
151
|
+
* @param authorities list of authorities to derive UserStatsAccount public keys from.
|
|
152
|
+
* You may want to get this list from UserMap in order to filter out idle users
|
|
153
|
+
*/
|
|
154
|
+
async sync(authorities) {
|
|
155
|
+
console.log(`USER MAP SIYCING AUTHS: ${authorities.length}`);
|
|
156
|
+
await Promise.all(authorities.map((authority) => this.addUserStat(authority, undefined, true)));
|
|
157
|
+
await this.bulkAccountLoader.load();
|
|
148
158
|
}
|
|
149
159
|
async unsubscribe() {
|
|
150
160
|
for (const [key, userStats] of this.userStatsMap.entries()) {
|
|
151
161
|
await userStats.unsubscribe();
|
|
152
162
|
this.userStatsMap.delete(key);
|
|
153
163
|
}
|
|
154
|
-
if (this.lastNumberOfAuthorities) {
|
|
155
|
-
this.driftClient.eventEmitter.removeListener('stateAccountUpdate', this.syncCallback);
|
|
156
|
-
this.lastNumberOfAuthorities = undefined;
|
|
157
|
-
}
|
|
158
164
|
}
|
|
159
165
|
}
|
|
160
166
|
exports.UserStatsMap = UserStatsMap;
|
package/package.json
CHANGED
|
@@ -93,17 +93,15 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
|
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
async fetch(): Promise<void> {
|
|
96
|
-
await this.
|
|
97
|
-
|
|
98
|
-
this.
|
|
96
|
+
const dataAndContext = await this.program.account.user.fetchAndContext(
|
|
97
|
+
this.userAccountPublicKey,
|
|
98
|
+
this.accountLoader.commitment
|
|
99
99
|
);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
);
|
|
106
|
-
this.user = { data: account, slot };
|
|
100
|
+
if (dataAndContext.context.slot > this.user?.slot ?? 0) {
|
|
101
|
+
this.user = {
|
|
102
|
+
data: dataAndContext.data as UserAccount,
|
|
103
|
+
slot: dataAndContext.context.slot,
|
|
104
|
+
};
|
|
107
105
|
}
|
|
108
106
|
}
|
|
109
107
|
|
|
@@ -137,7 +135,11 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
|
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
public getUserAccountAndSlot(): DataAndSlot<UserAccount> {
|
|
140
|
-
this.
|
|
138
|
+
if (!this.doesAccountExist()) {
|
|
139
|
+
throw new NotSubscribedError(
|
|
140
|
+
'You must call `subscribe` or `fetch` before using this function'
|
|
141
|
+
);
|
|
142
|
+
}
|
|
141
143
|
return this.user;
|
|
142
144
|
}
|
|
143
145
|
|
|
@@ -97,18 +97,15 @@ export class PollingUserStatsAccountSubscriber
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
async fetch(): Promise<void> {
|
|
100
|
-
await this.
|
|
101
|
-
|
|
102
|
-
this.
|
|
100
|
+
const dataAndContext = await this.program.account.userStats.fetchAndContext(
|
|
101
|
+
this.userStatsAccountPublicKey,
|
|
102
|
+
this.accountLoader.commitment
|
|
103
103
|
);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
buffer
|
|
110
|
-
);
|
|
111
|
-
this.userStats = { data: account, slot };
|
|
104
|
+
if (dataAndContext.context.slot > this.userStats?.slot ?? 0) {
|
|
105
|
+
this.userStats = {
|
|
106
|
+
data: dataAndContext.data as UserStatsAccount,
|
|
107
|
+
slot: dataAndContext.context.slot,
|
|
108
|
+
};
|
|
112
109
|
}
|
|
113
110
|
}
|
|
114
111
|
|
|
@@ -142,7 +139,11 @@ export class PollingUserStatsAccountSubscriber
|
|
|
142
139
|
}
|
|
143
140
|
|
|
144
141
|
public getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount> {
|
|
145
|
-
this.
|
|
142
|
+
if (!this.doesAccountExist()) {
|
|
143
|
+
throw new NotSubscribedError(
|
|
144
|
+
'You must call `subscribe` or `fetch` before using this function'
|
|
145
|
+
);
|
|
146
|
+
}
|
|
146
147
|
return this.userStats;
|
|
147
148
|
}
|
|
148
149
|
}
|
package/src/driftClient.ts
CHANGED
|
@@ -4654,9 +4654,10 @@ export class DriftClient {
|
|
|
4654
4654
|
auctionDuration: auctionDuration || 0,
|
|
4655
4655
|
auctionStartPrice: auctionStartPrice || ZERO,
|
|
4656
4656
|
auctionEndPrice: auctionEndPrice || ZERO,
|
|
4657
|
-
reduceOnly: reduceOnly
|
|
4658
|
-
postOnly: postOnly
|
|
4659
|
-
immediateOrCancel:
|
|
4657
|
+
reduceOnly: reduceOnly != undefined ? reduceOnly : null,
|
|
4658
|
+
postOnly: postOnly != undefined ? postOnly : null,
|
|
4659
|
+
immediateOrCancel:
|
|
4660
|
+
immediateOrCancel != undefined ? immediateOrCancel : null,
|
|
4660
4661
|
policy: policy || null,
|
|
4661
4662
|
maxTs: maxTs || null,
|
|
4662
4663
|
};
|
|
@@ -4769,9 +4770,9 @@ export class DriftClient {
|
|
|
4769
4770
|
oraclePriceOffset: newOraclePriceOffset || null,
|
|
4770
4771
|
triggerPrice: newTriggerPrice || null,
|
|
4771
4772
|
triggerCondition: newTriggerCondition || null,
|
|
4772
|
-
auctionDuration: auctionDuration ||
|
|
4773
|
-
auctionStartPrice: auctionStartPrice ||
|
|
4774
|
-
auctionEndPrice: auctionEndPrice ||
|
|
4773
|
+
auctionDuration: auctionDuration || null,
|
|
4774
|
+
auctionStartPrice: auctionStartPrice || null,
|
|
4775
|
+
auctionEndPrice: auctionEndPrice || null,
|
|
4775
4776
|
reduceOnly: reduceOnly || false,
|
|
4776
4777
|
postOnly: postOnly || null,
|
|
4777
4778
|
immediateOrCancel: immediateOrCancel || false,
|
package/src/userMap/userMap.ts
CHANGED
|
@@ -32,33 +32,51 @@ export interface UserMapInterface {
|
|
|
32
32
|
values(): IterableIterator<User>;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
// filter users that meet these criteria when passing into syncCallback
|
|
36
|
+
export type SyncCallbackCriteria = {
|
|
37
|
+
// only sync users that have open orders
|
|
38
|
+
hasOpenOrders: boolean;
|
|
39
|
+
};
|
|
40
|
+
|
|
35
41
|
export class UserMap implements UserMapInterface {
|
|
36
42
|
private userMap = new Map<string, User>();
|
|
37
43
|
private driftClient: DriftClient;
|
|
38
44
|
private accountSubscription: UserSubscriptionConfig;
|
|
39
45
|
private includeIdle: boolean;
|
|
40
46
|
private lastNumberOfSubAccounts;
|
|
41
|
-
private
|
|
47
|
+
private stateAccountUpdateCallback = async (state: StateAccount) => {
|
|
42
48
|
if (state.numberOfSubAccounts !== this.lastNumberOfSubAccounts) {
|
|
43
49
|
await this.sync();
|
|
44
50
|
this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
|
|
45
51
|
}
|
|
46
52
|
};
|
|
53
|
+
private syncCallback: (authorities: PublicKey[]) => Promise<void>;
|
|
54
|
+
private syncCallbackCriteria: SyncCallbackCriteria;
|
|
55
|
+
|
|
56
|
+
private syncPromise?: Promise<void>;
|
|
57
|
+
private syncPromiseResolver: () => void;
|
|
47
58
|
|
|
48
59
|
/**
|
|
60
|
+
* Constructs a new UserMap instance.
|
|
49
61
|
*
|
|
50
|
-
* @param driftClient
|
|
51
|
-
* @param accountSubscription
|
|
52
|
-
* @param includeIdle
|
|
62
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
63
|
+
* @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
|
|
64
|
+
* @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
|
|
65
|
+
* @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
|
|
66
|
+
* @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
|
|
53
67
|
*/
|
|
54
68
|
constructor(
|
|
55
69
|
driftClient: DriftClient,
|
|
56
70
|
accountSubscription: UserSubscriptionConfig,
|
|
57
|
-
includeIdle = false
|
|
71
|
+
includeIdle = false,
|
|
72
|
+
syncCallback?: (authorities: PublicKey[]) => Promise<void>,
|
|
73
|
+
syncCallbackCriteria: SyncCallbackCriteria = { hasOpenOrders: false }
|
|
58
74
|
) {
|
|
59
75
|
this.driftClient = driftClient;
|
|
60
76
|
this.accountSubscription = accountSubscription;
|
|
61
77
|
this.includeIdle = includeIdle;
|
|
78
|
+
this.syncCallback = syncCallback;
|
|
79
|
+
this.syncCallbackCriteria = syncCallbackCriteria;
|
|
62
80
|
}
|
|
63
81
|
|
|
64
82
|
public async subscribe() {
|
|
@@ -69,7 +87,10 @@ export class UserMap implements UserMapInterface {
|
|
|
69
87
|
await this.driftClient.subscribe();
|
|
70
88
|
this.lastNumberOfSubAccounts =
|
|
71
89
|
this.driftClient.getStateAccount().numberOfSubAccounts;
|
|
72
|
-
this.driftClient.eventEmitter.on(
|
|
90
|
+
this.driftClient.eventEmitter.on(
|
|
91
|
+
'stateAccountUpdate',
|
|
92
|
+
this.stateAccountUpdateCallback
|
|
93
|
+
);
|
|
73
94
|
|
|
74
95
|
await this.sync();
|
|
75
96
|
}
|
|
@@ -188,73 +209,110 @@ export class UserMap implements UserMapInterface {
|
|
|
188
209
|
}
|
|
189
210
|
|
|
190
211
|
public async sync() {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
filters.push(getNonIdleUserFilter());
|
|
212
|
+
if (this.syncPromise) {
|
|
213
|
+
return this.syncPromise;
|
|
194
214
|
}
|
|
215
|
+
this.syncPromise = new Promise((resolver) => {
|
|
216
|
+
this.syncPromiseResolver = resolver;
|
|
217
|
+
});
|
|
195
218
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
{
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
219
|
+
try {
|
|
220
|
+
const filters = [getUserFilter()];
|
|
221
|
+
if (!this.includeIdle) {
|
|
222
|
+
filters.push(getNonIdleUserFilter());
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const rpcRequestArgs = [
|
|
226
|
+
this.driftClient.program.programId.toBase58(),
|
|
227
|
+
{
|
|
228
|
+
commitment: this.driftClient.connection.commitment,
|
|
229
|
+
filters,
|
|
230
|
+
encoding: 'base64',
|
|
231
|
+
withContext: true,
|
|
232
|
+
},
|
|
233
|
+
];
|
|
211
234
|
|
|
212
|
-
|
|
213
|
-
Array<{
|
|
214
|
-
pubkey: PublicKey;
|
|
215
|
-
account: {
|
|
216
|
-
data: [string, string];
|
|
217
|
-
};
|
|
218
|
-
}>
|
|
219
|
-
> = rpcJSONResponse.result;
|
|
220
|
-
|
|
221
|
-
const slot = rpcResponseAndContext.context.slot;
|
|
222
|
-
|
|
223
|
-
const programAccountBufferMap = new Map<string, Buffer>();
|
|
224
|
-
for (const programAccount of rpcResponseAndContext.value) {
|
|
225
|
-
programAccountBufferMap.set(
|
|
226
|
-
programAccount.pubkey.toString(),
|
|
235
|
+
const rpcJSONResponse: any =
|
|
227
236
|
// @ts-ignore
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)
|
|
232
|
-
);
|
|
233
|
-
}
|
|
237
|
+
await this.driftClient.connection._rpcRequest(
|
|
238
|
+
'getProgramAccounts',
|
|
239
|
+
rpcRequestArgs
|
|
240
|
+
);
|
|
234
241
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
242
|
+
const rpcResponseAndContext: RpcResponseAndContext<
|
|
243
|
+
Array<{
|
|
244
|
+
pubkey: PublicKey;
|
|
245
|
+
account: {
|
|
246
|
+
data: [string, string];
|
|
247
|
+
};
|
|
248
|
+
}>
|
|
249
|
+
> = rpcJSONResponse.result;
|
|
250
|
+
|
|
251
|
+
const slot = rpcResponseAndContext.context.slot;
|
|
252
|
+
|
|
253
|
+
const programAccountBufferMap = new Map<string, Buffer>();
|
|
254
|
+
for (const programAccount of rpcResponseAndContext.value) {
|
|
255
|
+
programAccountBufferMap.set(
|
|
256
|
+
programAccount.pubkey.toString(),
|
|
257
|
+
// @ts-ignore
|
|
258
|
+
Buffer.from(
|
|
259
|
+
programAccount.account.data[0],
|
|
260
|
+
programAccount.account.data[1]
|
|
261
|
+
)
|
|
262
|
+
);
|
|
243
263
|
}
|
|
244
|
-
}
|
|
245
264
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
265
|
+
for (const [key, buffer] of programAccountBufferMap.entries()) {
|
|
266
|
+
if (!this.has(key)) {
|
|
267
|
+
const userAccount =
|
|
268
|
+
this.driftClient.program.account.user.coder.accounts.decode(
|
|
269
|
+
'User',
|
|
270
|
+
buffer
|
|
271
|
+
);
|
|
272
|
+
await this.addPubkey(new PublicKey(key), userAccount);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
for (const [key, user] of this.userMap.entries()) {
|
|
277
|
+
if (!programAccountBufferMap.has(key)) {
|
|
278
|
+
await user.unsubscribe();
|
|
279
|
+
this.userMap.delete(key);
|
|
280
|
+
} else {
|
|
281
|
+
const userAccount =
|
|
282
|
+
this.driftClient.program.account.user.coder.accounts.decode(
|
|
283
|
+
'User',
|
|
284
|
+
programAccountBufferMap.get(key)
|
|
285
|
+
);
|
|
286
|
+
user.accountSubscriber.updateData(userAccount, slot);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (this.syncCallback) {
|
|
291
|
+
const usersMeetingCriteria = Array.from(this.userMap.values()).filter(
|
|
292
|
+
(user) => {
|
|
293
|
+
let pass = true;
|
|
294
|
+
if (this.syncCallbackCriteria.hasOpenOrders) {
|
|
295
|
+
pass = pass && user.getUserAccount().hasOpenOrder;
|
|
296
|
+
}
|
|
297
|
+
return pass;
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
const userAuths = new Set(
|
|
301
|
+
usersMeetingCriteria.map((user) =>
|
|
302
|
+
user.getUserAccount().authority.toBase58()
|
|
303
|
+
)
|
|
304
|
+
);
|
|
305
|
+
const userAuthKeys = Array.from(userAuths).map(
|
|
306
|
+
(userAuth) => new PublicKey(userAuth)
|
|
307
|
+
);
|
|
308
|
+
await this.syncCallback(userAuthKeys);
|
|
257
309
|
}
|
|
310
|
+
} catch (e) {
|
|
311
|
+
console.error(`Error in UserMap.sync()`);
|
|
312
|
+
console.error(e);
|
|
313
|
+
} finally {
|
|
314
|
+
this.syncPromiseResolver();
|
|
315
|
+
this.syncPromise = undefined;
|
|
258
316
|
}
|
|
259
317
|
}
|
|
260
318
|
|
|
@@ -267,7 +325,7 @@ export class UserMap implements UserMapInterface {
|
|
|
267
325
|
if (this.lastNumberOfSubAccounts) {
|
|
268
326
|
this.driftClient.eventEmitter.removeListener(
|
|
269
327
|
'stateAccountUpdate',
|
|
270
|
-
this.
|
|
328
|
+
this.stateAccountUpdateCallback
|
|
271
329
|
);
|
|
272
330
|
this.lastNumberOfSubAccounts = undefined;
|
|
273
331
|
}
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
OrderRecord,
|
|
5
5
|
UserStatsAccount,
|
|
6
6
|
UserStats,
|
|
7
|
-
UserStatsSubscriptionConfig,
|
|
8
7
|
WrappedEvent,
|
|
9
8
|
DepositRecord,
|
|
10
9
|
FundingPaymentRecord,
|
|
@@ -14,12 +13,12 @@ import {
|
|
|
14
13
|
NewUserRecord,
|
|
15
14
|
LPRecord,
|
|
16
15
|
InsuranceFundStakeRecord,
|
|
17
|
-
|
|
16
|
+
BulkAccountLoader,
|
|
17
|
+
PollingUserStatsAccountSubscriber,
|
|
18
18
|
} from '..';
|
|
19
|
-
import {
|
|
19
|
+
import { PublicKey } from '@solana/web3.js';
|
|
20
20
|
|
|
21
21
|
import { UserMap } from './userMap';
|
|
22
|
-
import { Buffer } from 'buffer';
|
|
23
22
|
|
|
24
23
|
export class UserStatsMap {
|
|
25
24
|
/**
|
|
@@ -27,39 +26,45 @@ export class UserStatsMap {
|
|
|
27
26
|
*/
|
|
28
27
|
private userStatsMap = new Map<string, UserStats>();
|
|
29
28
|
private driftClient: DriftClient;
|
|
30
|
-
private
|
|
31
|
-
private lastNumberOfAuthorities;
|
|
32
|
-
private syncCallback = async (state: StateAccount) => {
|
|
33
|
-
if (state.numberOfAuthorities !== this.lastNumberOfAuthorities) {
|
|
34
|
-
await this.sync();
|
|
35
|
-
this.lastNumberOfAuthorities = state.numberOfAuthorities;
|
|
36
|
-
}
|
|
37
|
-
};
|
|
29
|
+
private bulkAccountLoader: BulkAccountLoader;
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Creates a new UserStatsMap instance.
|
|
33
|
+
*
|
|
34
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
35
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
36
|
+
*/
|
|
37
|
+
constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader) {
|
|
43
38
|
this.driftClient = driftClient;
|
|
44
|
-
|
|
39
|
+
if (!bulkAccountLoader) {
|
|
40
|
+
bulkAccountLoader = new BulkAccountLoader(
|
|
41
|
+
driftClient.connection,
|
|
42
|
+
driftClient.opts.commitment,
|
|
43
|
+
0
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
this.bulkAccountLoader = bulkAccountLoader;
|
|
45
47
|
}
|
|
46
48
|
|
|
47
|
-
public async subscribe() {
|
|
49
|
+
public async subscribe(authorities: PublicKey[]) {
|
|
48
50
|
if (this.size() > 0) {
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
await this.driftClient.subscribe();
|
|
53
|
-
this.
|
|
54
|
-
this.driftClient.getStateAccount().numberOfAuthorities;
|
|
55
|
-
this.driftClient.eventEmitter.on('stateAccountUpdate', this.syncCallback);
|
|
56
|
-
|
|
57
|
-
await this.sync();
|
|
55
|
+
await this.sync(authorities);
|
|
58
56
|
}
|
|
59
57
|
|
|
58
|
+
/**
|
|
59
|
+
*
|
|
60
|
+
* @param authority that owns the UserStatsAccount
|
|
61
|
+
* @param userStatsAccount optional UserStatsAccount to subscribe to, if undefined will be fetched later
|
|
62
|
+
* @param skipFetch if true, will not immediately fetch the UserStatsAccount
|
|
63
|
+
*/
|
|
60
64
|
public async addUserStat(
|
|
61
65
|
authority: PublicKey,
|
|
62
|
-
userStatsAccount?: UserStatsAccount
|
|
66
|
+
userStatsAccount?: UserStatsAccount,
|
|
67
|
+
skipFetch?: boolean
|
|
63
68
|
) {
|
|
64
69
|
const userStat = new UserStats({
|
|
65
70
|
driftClient: this.driftClient,
|
|
@@ -67,9 +72,18 @@ export class UserStatsMap {
|
|
|
67
72
|
this.driftClient.program.programId,
|
|
68
73
|
authority
|
|
69
74
|
),
|
|
70
|
-
accountSubscription:
|
|
75
|
+
accountSubscription: {
|
|
76
|
+
type: 'polling',
|
|
77
|
+
accountLoader: this.bulkAccountLoader,
|
|
78
|
+
},
|
|
71
79
|
});
|
|
72
|
-
|
|
80
|
+
if (skipFetch) {
|
|
81
|
+
await (
|
|
82
|
+
userStat.accountSubscriber as PollingUserStatsAccountSubscriber
|
|
83
|
+
).addToAccountLoader();
|
|
84
|
+
} else {
|
|
85
|
+
await userStat.subscribe(userStatsAccount);
|
|
86
|
+
}
|
|
73
87
|
|
|
74
88
|
this.userStatsMap.set(authority.toString(), userStat);
|
|
75
89
|
}
|
|
@@ -77,7 +91,7 @@ export class UserStatsMap {
|
|
|
77
91
|
public async updateWithOrderRecord(record: OrderRecord, userMap: UserMap) {
|
|
78
92
|
const user = await userMap.mustGet(record.user.toString());
|
|
79
93
|
if (!this.has(user.getUserAccount().authority.toString())) {
|
|
80
|
-
await this.addUserStat(user.getUserAccount().authority);
|
|
94
|
+
await this.addUserStat(user.getUserAccount().authority, undefined, false);
|
|
81
95
|
}
|
|
82
96
|
}
|
|
83
97
|
|
|
@@ -156,9 +170,19 @@ export class UserStatsMap {
|
|
|
156
170
|
return this.userStatsMap.get(authorityPublicKey);
|
|
157
171
|
}
|
|
158
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
175
|
+
* reading one from the blockchain if necessary.
|
|
176
|
+
* @param authorityPublicKey
|
|
177
|
+
* @returns
|
|
178
|
+
*/
|
|
159
179
|
public async mustGet(authorityPublicKey: string): Promise<UserStats> {
|
|
160
180
|
if (!this.has(authorityPublicKey)) {
|
|
161
|
-
await this.addUserStat(
|
|
181
|
+
await this.addUserStat(
|
|
182
|
+
new PublicKey(authorityPublicKey),
|
|
183
|
+
undefined,
|
|
184
|
+
false
|
|
185
|
+
);
|
|
162
186
|
}
|
|
163
187
|
return this.get(authorityPublicKey);
|
|
164
188
|
}
|
|
@@ -171,39 +195,19 @@ export class UserStatsMap {
|
|
|
171
195
|
return this.userStatsMap.size;
|
|
172
196
|
}
|
|
173
197
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
const programAccountMap = new Map<string, AccountInfo<Buffer>>();
|
|
190
|
-
for (const programAccount of programAccounts) {
|
|
191
|
-
programAccountMap.set(
|
|
192
|
-
new PublicKey(programAccount.account.data.slice(8, 40)).toString(),
|
|
193
|
-
programAccount.account
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
for (const key of programAccountMap.keys()) {
|
|
198
|
-
if (!this.has(key)) {
|
|
199
|
-
const userStatsAccount =
|
|
200
|
-
this.driftClient.program.account.userStats.coder.accounts.decode(
|
|
201
|
-
'UserStats',
|
|
202
|
-
programAccountMap.get(key).data
|
|
203
|
-
);
|
|
204
|
-
await this.addUserStat(new PublicKey(key), userStatsAccount);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
198
|
+
/**
|
|
199
|
+
* Sync the UserStatsMap
|
|
200
|
+
* @param authorities list of authorities to derive UserStatsAccount public keys from.
|
|
201
|
+
* You may want to get this list from UserMap in order to filter out idle users
|
|
202
|
+
*/
|
|
203
|
+
public async sync(authorities: PublicKey[]) {
|
|
204
|
+
console.log(`USER MAP SIYCING AUTHS: ${authorities.length}`);
|
|
205
|
+
await Promise.all(
|
|
206
|
+
authorities.map((authority) =>
|
|
207
|
+
this.addUserStat(authority, undefined, true)
|
|
208
|
+
)
|
|
209
|
+
);
|
|
210
|
+
await this.bulkAccountLoader.load();
|
|
207
211
|
}
|
|
208
212
|
|
|
209
213
|
public async unsubscribe() {
|
|
@@ -211,13 +215,5 @@ export class UserStatsMap {
|
|
|
211
215
|
await userStats.unsubscribe();
|
|
212
216
|
this.userStatsMap.delete(key);
|
|
213
217
|
}
|
|
214
|
-
|
|
215
|
-
if (this.lastNumberOfAuthorities) {
|
|
216
|
-
this.driftClient.eventEmitter.removeListener(
|
|
217
|
-
'stateAccountUpdate',
|
|
218
|
-
this.syncCallback
|
|
219
|
-
);
|
|
220
|
-
this.lastNumberOfAuthorities = undefined;
|
|
221
|
-
}
|
|
222
218
|
}
|
|
223
219
|
}
|
package/tests/amm/test.ts
CHANGED
|
@@ -967,8 +967,9 @@ describe('AMM Tests', () => {
|
|
|
967
967
|
mockMarket1.amm.maxBaseAssetReserve = mockMarket1.amm.baseAssetReserve.add(
|
|
968
968
|
new BN(9)
|
|
969
969
|
);
|
|
970
|
-
mockMarket1.amm.minBaseAssetReserve =
|
|
971
|
-
|
|
970
|
+
mockMarket1.amm.minBaseAssetReserve = mockMarket1.amm.baseAssetReserve.sub(
|
|
971
|
+
new BN(9)
|
|
972
|
+
);
|
|
972
973
|
mockMarket1.amm.quoteAssetReserve = new BN(cc).mul(BASE_PRECISION);
|
|
973
974
|
mockMarket1.amm.pegMultiplier = new BN(18.32 * PEG_PRECISION.toNumber());
|
|
974
975
|
mockMarket1.amm.sqrtK = new BN(cc).mul(BASE_PRECISION);
|
package/tests/dlob/test.ts
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
|
|
28
28
|
import { mockPerpMarkets, mockSpotMarkets, mockStateAccount } from './helpers';
|
|
29
29
|
import { DLOBOrdersCoder } from '../../src/dlob/DLOBOrders';
|
|
30
|
-
import {isAuctionComplete, isRestingLimitOrder} from
|
|
30
|
+
import { isAuctionComplete, isRestingLimitOrder } from '../../lib';
|
|
31
31
|
|
|
32
32
|
function insertOrderToDLOB(
|
|
33
33
|
dlob: DLOB,
|
|
@@ -2537,18 +2537,21 @@ describe('DLOB Perp Tests', () => {
|
|
|
2537
2537
|
OrderTriggerCondition.TRIGGERED_ABOVE, // triggerCondition: OrderTriggerCondition,
|
|
2538
2538
|
vBid,
|
|
2539
2539
|
vAsk,
|
|
2540
|
-
new BN(1)
|
|
2540
|
+
new BN(1) // slot
|
|
2541
2541
|
);
|
|
2542
2542
|
|
|
2543
|
-
const restingLimitBids = Array.from(
|
|
2543
|
+
const restingLimitBids = Array.from(
|
|
2544
|
+
dlob.getRestingLimitBids(marketIndex, slot, MarketType.PERP, oracle)
|
|
2545
|
+
);
|
|
2544
2546
|
expect(restingLimitBids.length).to.equal(0);
|
|
2545
2547
|
|
|
2546
|
-
const takingBids = Array.from(
|
|
2548
|
+
const takingBids = Array.from(
|
|
2549
|
+
dlob.getTakingBids(marketIndex, MarketType.PERP, slot, oracle)
|
|
2550
|
+
);
|
|
2547
2551
|
expect(takingBids.length).to.equal(1);
|
|
2548
2552
|
const triggerLimitBid = takingBids[0];
|
|
2549
2553
|
expect(isAuctionComplete(triggerLimitBid.order, slot)).to.equal(true);
|
|
2550
2554
|
expect(isRestingLimitOrder(triggerLimitBid.order, slot)).to.equal(false);
|
|
2551
|
-
|
|
2552
2555
|
});
|
|
2553
2556
|
|
|
2554
2557
|
it('Test will return expired market orders to fill', () => {
|