@drift-labs/sdk 2.48.0-beta.2 → 2.48.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/lib/accounts/bulkAccountLoader.js +1 -1
- package/lib/accounts/pollingUserAccountSubscriber.js +14 -7
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +14 -7
- package/lib/accounts/webSocketAccountSubscriber.js +1 -1
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +3 -2
- package/lib/accounts/webSocketDriftClientAccountSubscriber.js +6 -5
- package/lib/accounts/webSocketProgramAccountSubscriber.js +1 -1
- package/lib/accounts/webSocketUserAccountSubscriber.js +1 -1
- package/lib/constants/spotMarkets.js +10 -0
- package/lib/dlob/orderBookLevels.js +1 -1
- package/lib/driftClient.js +4 -4
- package/lib/events/eventSubscriber.js +1 -1
- package/lib/events/types.d.ts +1 -0
- package/lib/events/webSocketLogProvider.d.ts +7 -1
- package/lib/events/webSocketLogProvider.js +45 -4
- package/lib/orderSubscriber/OrderSubscriber.d.ts +5 -1
- package/lib/orderSubscriber/OrderSubscriber.js +24 -7
- package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -1
- package/lib/orderSubscriber/WebsocketSubscription.js +4 -3
- package/lib/orderSubscriber/types.d.ts +3 -1
- package/lib/slot/SlotSubscriber.js +4 -2
- package/lib/user.js +3 -3
- package/lib/userMap/userMap.d.ts +16 -4
- package/lib/userMap/userMap.js +83 -41
- package/lib/userMap/userStatsMap.d.ts +29 -8
- package/lib/userMap/userStatsMap.js +46 -41
- package/package.json +1 -1
- package/src/accounts/bulkAccountLoader.ts +1 -1
- package/src/accounts/pollingUserAccountSubscriber.ts +19 -11
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +20 -12
- package/src/accounts/webSocketAccountSubscriber.ts +1 -1
- package/src/accounts/webSocketDriftClientAccountSubscriber.ts +13 -6
- package/src/accounts/webSocketProgramAccountSubscriber.ts +1 -1
- package/src/accounts/webSocketUserAccountSubscriber.ts +1 -1
- package/src/constants/spotMarkets.ts +10 -0
- package/src/dlob/orderBookLevels.ts +1 -1
- package/src/driftClient.ts +3 -2
- package/src/events/eventSubscriber.ts +2 -1
- package/src/events/types.ts +1 -0
- package/src/events/webSocketLogProvider.ts +51 -4
- package/src/orderSubscriber/OrderSubscriber.ts +39 -15
- package/src/orderSubscriber/WebsocketSubscription.ts +7 -2
- package/src/orderSubscriber/types.ts +3 -1
- package/src/slot/SlotSubscriber.ts +4 -2
- package/src/user.ts +3 -3
- package/src/userMap/userMap.ts +139 -66
- package/src/userMap/userStatsMap.ts +64 -69
- package/tests/amm/test.ts +3 -2
- package/tests/dlob/test.ts +8 -5
|
@@ -3,10 +3,15 @@ import { Commitment, Connection, PublicKey } from '@solana/web3.js';
|
|
|
3
3
|
|
|
4
4
|
export class WebSocketLogProvider implements LogProvider {
|
|
5
5
|
private subscriptionId: number;
|
|
6
|
+
private isUnsubscribing = false;
|
|
7
|
+
private receivingData = false;
|
|
8
|
+
private timeoutId?: NodeJS.Timeout;
|
|
9
|
+
private callback?: logProviderCallback;
|
|
6
10
|
public constructor(
|
|
7
11
|
private connection: Connection,
|
|
8
12
|
private address: PublicKey,
|
|
9
|
-
private commitment: Commitment
|
|
13
|
+
private commitment: Commitment,
|
|
14
|
+
private resubTimeoutMs?: number
|
|
10
15
|
) {}
|
|
11
16
|
|
|
12
17
|
public subscribe(callback: logProviderCallback): boolean {
|
|
@@ -14,13 +19,24 @@ export class WebSocketLogProvider implements LogProvider {
|
|
|
14
19
|
return true;
|
|
15
20
|
}
|
|
16
21
|
|
|
22
|
+
this.callback = callback;
|
|
17
23
|
this.subscriptionId = this.connection.onLogs(
|
|
18
24
|
this.address,
|
|
19
25
|
(logs, ctx) => {
|
|
26
|
+
if (this.resubTimeoutMs && !this.isUnsubscribing) {
|
|
27
|
+
this.receivingData = true;
|
|
28
|
+
clearTimeout(this.timeoutId);
|
|
29
|
+
this.setTimeout();
|
|
30
|
+
}
|
|
20
31
|
callback(logs.signature, ctx.slot, logs.logs, undefined);
|
|
21
32
|
},
|
|
22
33
|
this.commitment
|
|
23
34
|
);
|
|
35
|
+
|
|
36
|
+
if (this.resubTimeoutMs) {
|
|
37
|
+
this.setTimeout();
|
|
38
|
+
}
|
|
39
|
+
|
|
24
40
|
return true;
|
|
25
41
|
}
|
|
26
42
|
|
|
@@ -29,10 +45,41 @@ export class WebSocketLogProvider implements LogProvider {
|
|
|
29
45
|
}
|
|
30
46
|
|
|
31
47
|
public async unsubscribe(): Promise<boolean> {
|
|
48
|
+
this.isUnsubscribing = true;
|
|
49
|
+
clearTimeout(this.timeoutId);
|
|
50
|
+
|
|
32
51
|
if (this.subscriptionId !== undefined) {
|
|
33
|
-
|
|
34
|
-
|
|
52
|
+
this.connection
|
|
53
|
+
.removeOnLogsListener(this.subscriptionId)
|
|
54
|
+
.then(() => {
|
|
55
|
+
this.subscriptionId = undefined;
|
|
56
|
+
this.isUnsubscribing = false;
|
|
57
|
+
return true;
|
|
58
|
+
})
|
|
59
|
+
.catch((err) => {
|
|
60
|
+
console.log('Error unsubscribing from logs: ', err);
|
|
61
|
+
this.isUnsubscribing = false;
|
|
62
|
+
return false;
|
|
63
|
+
});
|
|
64
|
+
} else {
|
|
65
|
+
this.isUnsubscribing = false;
|
|
66
|
+
return true;
|
|
35
67
|
}
|
|
36
|
-
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private setTimeout(): void {
|
|
71
|
+
this.timeoutId = setTimeout(async () => {
|
|
72
|
+
if (this.isUnsubscribing) {
|
|
73
|
+
// If we are in the process of unsubscribing, do not attempt to resubscribe
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (this.receivingData) {
|
|
78
|
+
console.log(`No log data in ${this.resubTimeoutMs}ms, resubscribing`);
|
|
79
|
+
await this.unsubscribe();
|
|
80
|
+
this.receivingData = false;
|
|
81
|
+
this.subscribe(this.callback);
|
|
82
|
+
}
|
|
83
|
+
}, this.resubTimeoutMs);
|
|
37
84
|
}
|
|
38
85
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DriftClient } from '../driftClient';
|
|
2
2
|
import { UserAccount } from '../types';
|
|
3
3
|
import { getUserFilter, getUserWithOrderFilter } from '../memcmp';
|
|
4
|
-
import { PublicKey, RpcResponseAndContext } from '@solana/web3.js';
|
|
4
|
+
import { Commitment, PublicKey, RpcResponseAndContext } from '@solana/web3.js';
|
|
5
5
|
import { Buffer } from 'buffer';
|
|
6
6
|
import { DLOB } from '../dlob/DLOB';
|
|
7
7
|
import { OrderSubscriberConfig, OrderSubscriberEvents } from './types';
|
|
@@ -14,13 +14,17 @@ export class OrderSubscriber {
|
|
|
14
14
|
driftClient: DriftClient;
|
|
15
15
|
usersAccounts = new Map<string, { slot: number; userAccount: UserAccount }>();
|
|
16
16
|
subscription: PollingSubscription | WebsocketSubscription;
|
|
17
|
+
commitment: Commitment;
|
|
17
18
|
eventEmitter: StrictEventEmitter<EventEmitter, OrderSubscriberEvents>;
|
|
18
19
|
|
|
19
20
|
fetchPromise?: Promise<void>;
|
|
20
21
|
fetchPromiseResolver: () => void;
|
|
21
22
|
|
|
23
|
+
mostRecentSlot: number;
|
|
24
|
+
|
|
22
25
|
constructor(config: OrderSubscriberConfig) {
|
|
23
26
|
this.driftClient = config.driftClient;
|
|
27
|
+
this.commitment = config.subscriptionConfig.commitment || 'processed';
|
|
24
28
|
if (config.subscriptionConfig.type === 'polling') {
|
|
25
29
|
this.subscription = new PollingSubscription({
|
|
26
30
|
orderSubscriber: this,
|
|
@@ -29,6 +33,7 @@ export class OrderSubscriber {
|
|
|
29
33
|
} else {
|
|
30
34
|
this.subscription = new WebsocketSubscription({
|
|
31
35
|
orderSubscriber: this,
|
|
36
|
+
commitment: this.commitment,
|
|
32
37
|
skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
|
|
33
38
|
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
34
39
|
});
|
|
@@ -53,7 +58,7 @@ export class OrderSubscriber {
|
|
|
53
58
|
const rpcRequestArgs = [
|
|
54
59
|
this.driftClient.program.programId.toBase58(),
|
|
55
60
|
{
|
|
56
|
-
commitment: this.
|
|
61
|
+
commitment: this.commitment,
|
|
57
62
|
filters: [getUserFilter(), getUserWithOrderFilter()],
|
|
58
63
|
encoding: 'base64',
|
|
59
64
|
withContext: true,
|
|
@@ -81,18 +86,13 @@ export class OrderSubscriber {
|
|
|
81
86
|
const programAccountSet = new Set<string>();
|
|
82
87
|
for (const programAccount of rpcResponseAndContext.value) {
|
|
83
88
|
const key = programAccount.pubkey.toString();
|
|
84
|
-
// @ts-ignore
|
|
85
|
-
const buffer = Buffer.from(
|
|
86
|
-
programAccount.account.data[0],
|
|
87
|
-
programAccount.account.data[1]
|
|
88
|
-
);
|
|
89
89
|
programAccountSet.add(key);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
this.tryUpdateUserAccount(
|
|
91
|
+
key,
|
|
92
|
+
'raw',
|
|
93
|
+
programAccount.account.data,
|
|
94
|
+
slot
|
|
95
|
+
);
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
for (const key of this.usersAccounts.keys()) {
|
|
@@ -110,11 +110,31 @@ export class OrderSubscriber {
|
|
|
110
110
|
|
|
111
111
|
tryUpdateUserAccount(
|
|
112
112
|
key: string,
|
|
113
|
-
|
|
113
|
+
dataType: 'raw' | 'decoded',
|
|
114
|
+
data: string[] | UserAccount,
|
|
114
115
|
slot: number
|
|
115
116
|
): void {
|
|
117
|
+
if (!this.mostRecentSlot || slot > this.mostRecentSlot) {
|
|
118
|
+
this.mostRecentSlot = slot;
|
|
119
|
+
}
|
|
120
|
+
|
|
116
121
|
const slotAndUserAccount = this.usersAccounts.get(key);
|
|
117
|
-
if (!slotAndUserAccount || slotAndUserAccount.slot
|
|
122
|
+
if (!slotAndUserAccount || slotAndUserAccount.slot <= slot) {
|
|
123
|
+
let userAccount: UserAccount;
|
|
124
|
+
// Polling leads to a lot of redundant decoding, so we only decode if data is from a fresh slot
|
|
125
|
+
if (dataType === 'raw') {
|
|
126
|
+
// @ts-ignore
|
|
127
|
+
const buffer = Buffer.from(data[0], data[1]);
|
|
128
|
+
|
|
129
|
+
userAccount =
|
|
130
|
+
this.driftClient.program.account.user.coder.accounts.decodeUnchecked(
|
|
131
|
+
'User',
|
|
132
|
+
buffer
|
|
133
|
+
) as UserAccount;
|
|
134
|
+
} else {
|
|
135
|
+
userAccount = data as UserAccount;
|
|
136
|
+
}
|
|
137
|
+
|
|
118
138
|
const newOrders = userAccount.orders.filter(
|
|
119
139
|
(order) =>
|
|
120
140
|
order.slot.toNumber() > (slotAndUserAccount?.slot ?? 0) &&
|
|
@@ -148,6 +168,10 @@ export class OrderSubscriber {
|
|
|
148
168
|
return dlob;
|
|
149
169
|
}
|
|
150
170
|
|
|
171
|
+
public getSlot(): number {
|
|
172
|
+
return this.mostRecentSlot ?? 0;
|
|
173
|
+
}
|
|
174
|
+
|
|
151
175
|
public async unsubscribe(): Promise<void> {
|
|
152
176
|
await this.subscription.unsubscribe();
|
|
153
177
|
}
|
|
@@ -2,10 +2,11 @@ import { OrderSubscriber } from './OrderSubscriber';
|
|
|
2
2
|
import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
|
|
3
3
|
import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber';
|
|
4
4
|
import { UserAccount } from '../types';
|
|
5
|
-
import { Context, PublicKey } from '@solana/web3.js';
|
|
5
|
+
import { Commitment, Context, PublicKey } from '@solana/web3.js';
|
|
6
6
|
|
|
7
7
|
export class WebsocketSubscription {
|
|
8
8
|
private orderSubscriber: OrderSubscriber;
|
|
9
|
+
private commitment: Commitment;
|
|
9
10
|
private skipInitialLoad: boolean;
|
|
10
11
|
private resubTimeoutMs?: number;
|
|
11
12
|
|
|
@@ -13,14 +14,17 @@ export class WebsocketSubscription {
|
|
|
13
14
|
|
|
14
15
|
constructor({
|
|
15
16
|
orderSubscriber,
|
|
17
|
+
commitment,
|
|
16
18
|
skipInitialLoad = false,
|
|
17
19
|
resubTimeoutMs,
|
|
18
20
|
}: {
|
|
19
21
|
orderSubscriber: OrderSubscriber;
|
|
22
|
+
commitment: Commitment;
|
|
20
23
|
skipInitialLoad?: boolean;
|
|
21
24
|
resubTimeoutMs?: number;
|
|
22
25
|
}) {
|
|
23
26
|
this.orderSubscriber = orderSubscriber;
|
|
27
|
+
this.commitment = commitment;
|
|
24
28
|
this.skipInitialLoad = skipInitialLoad;
|
|
25
29
|
this.resubTimeoutMs = resubTimeoutMs;
|
|
26
30
|
}
|
|
@@ -36,7 +40,7 @@ export class WebsocketSubscription {
|
|
|
36
40
|
),
|
|
37
41
|
{
|
|
38
42
|
filters: [getUserFilter(), getNonIdleUserFilter()],
|
|
39
|
-
commitment: this.
|
|
43
|
+
commitment: this.commitment,
|
|
40
44
|
},
|
|
41
45
|
this.resubTimeoutMs
|
|
42
46
|
);
|
|
@@ -47,6 +51,7 @@ export class WebsocketSubscription {
|
|
|
47
51
|
const userKey = accountId.toBase58();
|
|
48
52
|
this.orderSubscriber.tryUpdateUserAccount(
|
|
49
53
|
userKey,
|
|
54
|
+
'decoded',
|
|
50
55
|
account,
|
|
51
56
|
context.slot
|
|
52
57
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PublicKey } from '@solana/web3.js';
|
|
1
|
+
import { Commitment, PublicKey } from '@solana/web3.js';
|
|
2
2
|
import { Order, UserAccount } from '../types';
|
|
3
3
|
import { DriftClient } from '../driftClient';
|
|
4
4
|
|
|
@@ -8,11 +8,13 @@ export type OrderSubscriberConfig = {
|
|
|
8
8
|
| {
|
|
9
9
|
type: 'polling';
|
|
10
10
|
frequency: number;
|
|
11
|
+
commitment?: Commitment;
|
|
11
12
|
}
|
|
12
13
|
| {
|
|
13
14
|
type: 'websocket';
|
|
14
15
|
skipInitialLoad?: boolean;
|
|
15
16
|
resubTimeoutMs?: number;
|
|
17
|
+
commitment?: Commitment;
|
|
16
18
|
};
|
|
17
19
|
};
|
|
18
20
|
|
|
@@ -29,8 +29,10 @@ export class SlotSubscriber {
|
|
|
29
29
|
this.currentSlot = await this.connection.getSlot('confirmed');
|
|
30
30
|
|
|
31
31
|
this.subscriptionId = this.connection.onSlotChange((slotInfo) => {
|
|
32
|
-
this.currentSlot
|
|
33
|
-
|
|
32
|
+
if (!this.currentSlot || this.currentSlot < slotInfo.slot) {
|
|
33
|
+
this.currentSlot = slotInfo.slot;
|
|
34
|
+
this.eventEmitter.emit('newSlot', slotInfo.slot);
|
|
35
|
+
}
|
|
34
36
|
});
|
|
35
37
|
}
|
|
36
38
|
|
package/src/user.ts
CHANGED
|
@@ -109,12 +109,12 @@ export class User {
|
|
|
109
109
|
);
|
|
110
110
|
} else if (config.accountSubscription?.type === 'custom') {
|
|
111
111
|
this.accountSubscriber = config.accountSubscription.userAccountSubscriber;
|
|
112
|
-
} else
|
|
112
|
+
} else {
|
|
113
113
|
this.accountSubscriber = new WebSocketUserAccountSubscriber(
|
|
114
114
|
config.driftClient.program,
|
|
115
115
|
config.userAccountPublicKey,
|
|
116
|
-
config.accountSubscription
|
|
117
|
-
config.accountSubscription
|
|
116
|
+
config.accountSubscription?.resubTimeoutMs,
|
|
117
|
+
config.accountSubscription?.commitment
|
|
118
118
|
);
|
|
119
119
|
}
|
|
120
120
|
this.eventEmitter = this.accountSubscriber.eventEmitter;
|
package/src/userMap/userMap.ts
CHANGED
|
@@ -32,33 +32,59 @@ 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;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public addSyncCallback(
|
|
83
|
+
syncCallback?: (authorities: PublicKey[]) => Promise<void>,
|
|
84
|
+
syncCallbackCriteria: SyncCallbackCriteria = { hasOpenOrders: false }
|
|
85
|
+
) {
|
|
86
|
+
this.syncCallback = syncCallback;
|
|
87
|
+
this.syncCallbackCriteria = syncCallbackCriteria;
|
|
62
88
|
}
|
|
63
89
|
|
|
64
90
|
public async subscribe() {
|
|
@@ -69,7 +95,10 @@ export class UserMap implements UserMapInterface {
|
|
|
69
95
|
await this.driftClient.subscribe();
|
|
70
96
|
this.lastNumberOfSubAccounts =
|
|
71
97
|
this.driftClient.getStateAccount().numberOfSubAccounts;
|
|
72
|
-
this.driftClient.eventEmitter.on(
|
|
98
|
+
this.driftClient.eventEmitter.on(
|
|
99
|
+
'stateAccountUpdate',
|
|
100
|
+
this.stateAccountUpdateCallback
|
|
101
|
+
);
|
|
73
102
|
|
|
74
103
|
await this.sync();
|
|
75
104
|
}
|
|
@@ -187,74 +216,118 @@ export class UserMap implements UserMapInterface {
|
|
|
187
216
|
return this.userMap.size;
|
|
188
217
|
}
|
|
189
218
|
|
|
219
|
+
public getUniqueAuthorities(useSyncCallbackCriteria = true): PublicKey[] {
|
|
220
|
+
const usersMeetingCriteria = Array.from(this.userMap.values()).filter(
|
|
221
|
+
(user) => {
|
|
222
|
+
let pass = true;
|
|
223
|
+
if (
|
|
224
|
+
useSyncCallbackCriteria &&
|
|
225
|
+
this.syncCallbackCriteria.hasOpenOrders
|
|
226
|
+
) {
|
|
227
|
+
pass = pass && user.getUserAccount().hasOpenOrder;
|
|
228
|
+
}
|
|
229
|
+
return pass;
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
const userAuths = new Set(
|
|
233
|
+
usersMeetingCriteria.map((user) =>
|
|
234
|
+
user.getUserAccount().authority.toBase58()
|
|
235
|
+
)
|
|
236
|
+
);
|
|
237
|
+
const userAuthKeys = Array.from(userAuths).map(
|
|
238
|
+
(userAuth) => new PublicKey(userAuth)
|
|
239
|
+
);
|
|
240
|
+
return userAuthKeys;
|
|
241
|
+
}
|
|
242
|
+
|
|
190
243
|
public async sync() {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
filters.push(getNonIdleUserFilter());
|
|
244
|
+
if (this.syncPromise) {
|
|
245
|
+
return this.syncPromise;
|
|
194
246
|
}
|
|
247
|
+
this.syncPromise = new Promise((resolver) => {
|
|
248
|
+
this.syncPromiseResolver = resolver;
|
|
249
|
+
});
|
|
195
250
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
{
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
encoding: 'base64',
|
|
202
|
-
withContext: true,
|
|
203
|
-
},
|
|
204
|
-
];
|
|
205
|
-
|
|
206
|
-
// @ts-ignore
|
|
207
|
-
const rpcJSONResponse: any = await this.driftClient.connection._rpcRequest(
|
|
208
|
-
'getProgramAccounts',
|
|
209
|
-
rpcRequestArgs
|
|
210
|
-
);
|
|
251
|
+
try {
|
|
252
|
+
const filters = [getUserFilter()];
|
|
253
|
+
if (!this.includeIdle) {
|
|
254
|
+
filters.push(getNonIdleUserFilter());
|
|
255
|
+
}
|
|
211
256
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const programAccountBufferMap = new Map<string, Buffer>();
|
|
224
|
-
for (const programAccount of rpcResponseAndContext.value) {
|
|
225
|
-
programAccountBufferMap.set(
|
|
226
|
-
programAccount.pubkey.toString(),
|
|
257
|
+
const rpcRequestArgs = [
|
|
258
|
+
this.driftClient.program.programId.toBase58(),
|
|
259
|
+
{
|
|
260
|
+
commitment: this.driftClient.connection.commitment,
|
|
261
|
+
filters,
|
|
262
|
+
encoding: 'base64',
|
|
263
|
+
withContext: true,
|
|
264
|
+
},
|
|
265
|
+
];
|
|
266
|
+
|
|
267
|
+
const rpcJSONResponse: any =
|
|
227
268
|
// @ts-ignore
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
)
|
|
232
|
-
|
|
233
|
-
|
|
269
|
+
await this.driftClient.connection._rpcRequest(
|
|
270
|
+
'getProgramAccounts',
|
|
271
|
+
rpcRequestArgs
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
const rpcResponseAndContext: RpcResponseAndContext<
|
|
275
|
+
Array<{
|
|
276
|
+
pubkey: PublicKey;
|
|
277
|
+
account: {
|
|
278
|
+
data: [string, string];
|
|
279
|
+
};
|
|
280
|
+
}>
|
|
281
|
+
> = rpcJSONResponse.result;
|
|
282
|
+
|
|
283
|
+
const slot = rpcResponseAndContext.context.slot;
|
|
234
284
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
285
|
+
const programAccountBufferMap = new Map<string, Buffer>();
|
|
286
|
+
for (const programAccount of rpcResponseAndContext.value) {
|
|
287
|
+
programAccountBufferMap.set(
|
|
288
|
+
programAccount.pubkey.toString(),
|
|
289
|
+
// @ts-ignore
|
|
290
|
+
Buffer.from(
|
|
291
|
+
programAccount.account.data[0],
|
|
292
|
+
programAccount.account.data[1]
|
|
293
|
+
)
|
|
294
|
+
);
|
|
243
295
|
}
|
|
244
|
-
}
|
|
245
296
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
297
|
+
for (const [key, buffer] of programAccountBufferMap.entries()) {
|
|
298
|
+
if (!this.has(key)) {
|
|
299
|
+
const userAccount =
|
|
300
|
+
this.driftClient.program.account.user.coder.accounts.decode(
|
|
301
|
+
'User',
|
|
302
|
+
buffer
|
|
303
|
+
);
|
|
304
|
+
await this.addPubkey(new PublicKey(key), userAccount);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
for (const [key, user] of this.userMap.entries()) {
|
|
309
|
+
if (!programAccountBufferMap.has(key)) {
|
|
310
|
+
await user.unsubscribe();
|
|
311
|
+
this.userMap.delete(key);
|
|
312
|
+
} else {
|
|
313
|
+
const userAccount =
|
|
314
|
+
this.driftClient.program.account.user.coder.accounts.decode(
|
|
315
|
+
'User',
|
|
316
|
+
programAccountBufferMap.get(key)
|
|
317
|
+
);
|
|
318
|
+
user.accountSubscriber.updateData(userAccount, slot);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (this.syncCallback) {
|
|
323
|
+
await this.syncCallback(this.getUniqueAuthorities());
|
|
257
324
|
}
|
|
325
|
+
} catch (e) {
|
|
326
|
+
console.error(`Error in UserMap.sync()`);
|
|
327
|
+
console.error(e);
|
|
328
|
+
} finally {
|
|
329
|
+
this.syncPromiseResolver();
|
|
330
|
+
this.syncPromise = undefined;
|
|
258
331
|
}
|
|
259
332
|
}
|
|
260
333
|
|
|
@@ -267,7 +340,7 @@ export class UserMap implements UserMapInterface {
|
|
|
267
340
|
if (this.lastNumberOfSubAccounts) {
|
|
268
341
|
this.driftClient.eventEmitter.removeListener(
|
|
269
342
|
'stateAccountUpdate',
|
|
270
|
-
this.
|
|
343
|
+
this.stateAccountUpdateCallback
|
|
271
344
|
);
|
|
272
345
|
this.lastNumberOfSubAccounts = undefined;
|
|
273
346
|
}
|