@drift-labs/sdk 2.40.0-beta.6 → 2.40.0-beta.8
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/types.d.ts +5 -1
- package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -1
- package/lib/accounts/webSocketAccountSubscriber.js +28 -2
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +2 -1
- package/lib/accounts/webSocketDriftClientAccountSubscriber.js +6 -5
- package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +32 -0
- package/lib/accounts/webSocketProgramAccountSubscriber.js +99 -0
- package/lib/accounts/webSocketUserAccountSubscriber.d.ts +2 -1
- package/lib/accounts/webSocketUserAccountSubscriber.js +3 -2
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +2 -1
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +3 -2
- package/lib/auctionSubscriber/auctionSubscriber.d.ts +3 -2
- package/lib/auctionSubscriber/auctionSubscriber.js +15 -7
- package/lib/auctionSubscriber/types.d.ts +1 -0
- package/lib/driftClient.js +3 -3
- package/lib/driftClientConfig.d.ts +1 -0
- package/lib/orderSubscriber/OrderSubscriber.d.ts +1 -3
- package/lib/orderSubscriber/OrderSubscriber.js +4 -3
- package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -2
- package/lib/orderSubscriber/WebsocketSubscription.js +13 -12
- package/lib/orderSubscriber/types.d.ts +1 -0
- package/lib/user.d.ts +2 -2
- package/lib/user.js +5 -5
- package/package.json +1 -1
- package/src/accounts/types.ts +8 -1
- package/src/accounts/webSocketAccountSubscriber.ts +36 -2
- package/src/accounts/webSocketDriftClientAccountSubscriber.ts +15 -5
- package/src/accounts/webSocketProgramAccountSubscriber.ts +152 -0
- package/src/accounts/webSocketUserAccountSubscriber.ts +10 -2
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +10 -2
- package/src/auctionSubscriber/auctionSubscriber.ts +30 -21
- package/src/auctionSubscriber/types.ts +1 -0
- package/src/driftClient.ts +2 -1
- package/src/driftClientConfig.ts +1 -0
- package/src/orderSubscriber/OrderSubscriber.ts +12 -7
- package/src/orderSubscriber/WebsocketSubscription.ts +33 -23
- package/src/orderSubscriber/types.ts +1 -0
- package/src/user.ts +7 -5
package/src/accounts/types.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from '../types';
|
|
9
9
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
10
10
|
import { EventEmitter } from 'events';
|
|
11
|
-
import { PublicKey } from '@solana/web3.js';
|
|
11
|
+
import { Context, PublicKey } from '@solana/web3.js';
|
|
12
12
|
import { Account } from '@solana/spl-token';
|
|
13
13
|
import { OracleInfo, OraclePriceData } from '..';
|
|
14
14
|
|
|
@@ -21,6 +21,13 @@ export interface AccountSubscriber<T> {
|
|
|
21
21
|
setData(userAccount: T, slot?: number): void;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
export interface ProgramAccountSubscriber<T> {
|
|
25
|
+
subscribe(
|
|
26
|
+
onChange: (accountId: PublicKey, data: T, context: Context) => void
|
|
27
|
+
): Promise<void>;
|
|
28
|
+
unsubscribe(): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
24
31
|
export class NotSubscribedError extends Error {
|
|
25
32
|
name = 'NotSubscribedError';
|
|
26
33
|
}
|
|
@@ -13,17 +13,24 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
|
|
|
13
13
|
decodeBufferFn: (buffer: Buffer) => T;
|
|
14
14
|
onChange: (data: T) => void;
|
|
15
15
|
listenerId?: number;
|
|
16
|
+
resubTimeoutMs?: number;
|
|
17
|
+
timeoutId?: NodeJS.Timeout;
|
|
18
|
+
|
|
19
|
+
receivingData: boolean;
|
|
16
20
|
|
|
17
21
|
public constructor(
|
|
18
22
|
accountName: string,
|
|
19
23
|
program: Program,
|
|
20
24
|
accountPublicKey: PublicKey,
|
|
21
|
-
decodeBuffer?: (buffer: Buffer) => T
|
|
25
|
+
decodeBuffer?: (buffer: Buffer) => T,
|
|
26
|
+
resubTimeoutMs?: number
|
|
22
27
|
) {
|
|
23
28
|
this.accountName = accountName;
|
|
24
29
|
this.program = program;
|
|
25
30
|
this.accountPublicKey = accountPublicKey;
|
|
26
31
|
this.decodeBufferFn = decodeBuffer;
|
|
32
|
+
this.resubTimeoutMs = resubTimeoutMs;
|
|
33
|
+
this.receivingData = false;
|
|
27
34
|
}
|
|
28
35
|
|
|
29
36
|
async subscribe(onChange: (data: T) => void): Promise<void> {
|
|
@@ -39,10 +46,21 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
|
|
|
39
46
|
this.listenerId = this.program.provider.connection.onAccountChange(
|
|
40
47
|
this.accountPublicKey,
|
|
41
48
|
(accountInfo, context) => {
|
|
42
|
-
this.
|
|
49
|
+
if (this.resubTimeoutMs) {
|
|
50
|
+
this.receivingData = true;
|
|
51
|
+
clearTimeout(this.timeoutId);
|
|
52
|
+
this.handleRpcResponse(context, accountInfo);
|
|
53
|
+
this.setTimeout();
|
|
54
|
+
} else {
|
|
55
|
+
this.handleRpcResponse(context, accountInfo);
|
|
56
|
+
}
|
|
43
57
|
},
|
|
44
58
|
(this.program.provider as AnchorProvider).opts.commitment
|
|
45
59
|
);
|
|
60
|
+
|
|
61
|
+
if (this.resubTimeoutMs) {
|
|
62
|
+
this.setTimeout();
|
|
63
|
+
}
|
|
46
64
|
}
|
|
47
65
|
|
|
48
66
|
setData(data: T, slot?: number): void {
|
|
@@ -57,6 +75,22 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
|
|
|
57
75
|
};
|
|
58
76
|
}
|
|
59
77
|
|
|
78
|
+
private setTimeout(): void {
|
|
79
|
+
if (!this.onChange) {
|
|
80
|
+
throw new Error('onChange callback function must be set');
|
|
81
|
+
}
|
|
82
|
+
this.timeoutId = setTimeout(async () => {
|
|
83
|
+
if (this.receivingData) {
|
|
84
|
+
console.log(
|
|
85
|
+
`No ws data from ${this.accountName} in ${this.resubTimeoutMs}ms, resubscribing`
|
|
86
|
+
);
|
|
87
|
+
await this.unsubscribe();
|
|
88
|
+
this.receivingData = false;
|
|
89
|
+
await this.subscribe(this.onChange);
|
|
90
|
+
}
|
|
91
|
+
}, this.resubTimeoutMs);
|
|
92
|
+
}
|
|
93
|
+
|
|
60
94
|
async fetch(): Promise<void> {
|
|
61
95
|
const rpcResponse =
|
|
62
96
|
await this.program.provider.connection.getAccountInfoAndContext(
|
|
@@ -31,6 +31,7 @@ export class WebSocketDriftClientAccountSubscriber
|
|
|
31
31
|
oracleInfos: OracleInfo[];
|
|
32
32
|
oracleClientCache = new OracleClientCache();
|
|
33
33
|
|
|
34
|
+
resubTimeoutMs?: number;
|
|
34
35
|
shouldFindAllMarketsAndOracles: boolean;
|
|
35
36
|
|
|
36
37
|
eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
|
|
@@ -54,7 +55,8 @@ export class WebSocketDriftClientAccountSubscriber
|
|
|
54
55
|
perpMarketIndexes: number[],
|
|
55
56
|
spotMarketIndexes: number[],
|
|
56
57
|
oracleInfos: OracleInfo[],
|
|
57
|
-
shouldFindAllMarketsAndOracles: boolean
|
|
58
|
+
shouldFindAllMarketsAndOracles: boolean,
|
|
59
|
+
resubTimeoutMs?: number
|
|
58
60
|
) {
|
|
59
61
|
this.isSubscribed = false;
|
|
60
62
|
this.program = program;
|
|
@@ -63,6 +65,7 @@ export class WebSocketDriftClientAccountSubscriber
|
|
|
63
65
|
this.spotMarketIndexes = spotMarketIndexes;
|
|
64
66
|
this.oracleInfos = oracleInfos;
|
|
65
67
|
this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
|
|
68
|
+
this.resubTimeoutMs = resubTimeoutMs;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
public async subscribe(): Promise<boolean> {
|
|
@@ -96,7 +99,9 @@ export class WebSocketDriftClientAccountSubscriber
|
|
|
96
99
|
this.stateAccountSubscriber = new WebSocketAccountSubscriber(
|
|
97
100
|
'state',
|
|
98
101
|
this.program,
|
|
99
|
-
statePublicKey
|
|
102
|
+
statePublicKey,
|
|
103
|
+
undefined,
|
|
104
|
+
this.resubTimeoutMs
|
|
100
105
|
);
|
|
101
106
|
await this.stateAccountSubscriber.subscribe((data: StateAccount) => {
|
|
102
107
|
this.eventEmitter.emit('stateAccountUpdate', data);
|
|
@@ -136,7 +141,9 @@ export class WebSocketDriftClientAccountSubscriber
|
|
|
136
141
|
const accountSubscriber = new WebSocketAccountSubscriber<PerpMarketAccount>(
|
|
137
142
|
'perpMarket',
|
|
138
143
|
this.program,
|
|
139
|
-
perpMarketPublicKey
|
|
144
|
+
perpMarketPublicKey,
|
|
145
|
+
undefined,
|
|
146
|
+
this.resubTimeoutMs
|
|
140
147
|
);
|
|
141
148
|
await accountSubscriber.subscribe((data: PerpMarketAccount) => {
|
|
142
149
|
this.eventEmitter.emit('perpMarketAccountUpdate', data);
|
|
@@ -161,7 +168,9 @@ export class WebSocketDriftClientAccountSubscriber
|
|
|
161
168
|
const accountSubscriber = new WebSocketAccountSubscriber<SpotMarketAccount>(
|
|
162
169
|
'spotMarket',
|
|
163
170
|
this.program,
|
|
164
|
-
marketPublicKey
|
|
171
|
+
marketPublicKey,
|
|
172
|
+
undefined,
|
|
173
|
+
this.resubTimeoutMs
|
|
165
174
|
);
|
|
166
175
|
await accountSubscriber.subscribe((data: SpotMarketAccount) => {
|
|
167
176
|
this.eventEmitter.emit('spotMarketAccountUpdate', data);
|
|
@@ -192,7 +201,8 @@ export class WebSocketDriftClientAccountSubscriber
|
|
|
192
201
|
oracleInfo.publicKey,
|
|
193
202
|
(buffer: Buffer) => {
|
|
194
203
|
return client.getOraclePriceDataFromBuffer(buffer);
|
|
195
|
-
}
|
|
204
|
+
},
|
|
205
|
+
this.resubTimeoutMs
|
|
196
206
|
);
|
|
197
207
|
|
|
198
208
|
await accountSubscriber.subscribe((data: OraclePriceData) => {
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { DataAndSlot, BufferAndSlot, ProgramAccountSubscriber } from './types';
|
|
2
|
+
import { AnchorProvider, Program } from '@coral-xyz/anchor';
|
|
3
|
+
import {
|
|
4
|
+
Commitment,
|
|
5
|
+
Context,
|
|
6
|
+
KeyedAccountInfo,
|
|
7
|
+
MemcmpFilter,
|
|
8
|
+
PublicKey,
|
|
9
|
+
} from '@solana/web3.js';
|
|
10
|
+
import * as Buffer from 'buffer';
|
|
11
|
+
|
|
12
|
+
export class WebSocketProgramAccountSubscriber<T>
|
|
13
|
+
implements ProgramAccountSubscriber<T>
|
|
14
|
+
{
|
|
15
|
+
subscriptionName: string;
|
|
16
|
+
accountDiscriminator: string;
|
|
17
|
+
dataAndSlot?: DataAndSlot<T> & { accountId: PublicKey };
|
|
18
|
+
bufferAndSlot?: BufferAndSlot;
|
|
19
|
+
program: Program;
|
|
20
|
+
decodeBuffer: (accountName: string, ix: Buffer) => T;
|
|
21
|
+
onChange: (accountId: PublicKey, data: T, context: Context) => void;
|
|
22
|
+
listenerId?: number;
|
|
23
|
+
resubTimeoutMs?: number;
|
|
24
|
+
timeoutId?: NodeJS.Timeout;
|
|
25
|
+
options: { filters: MemcmpFilter[]; commitment?: Commitment };
|
|
26
|
+
|
|
27
|
+
receivingData = false;
|
|
28
|
+
|
|
29
|
+
public constructor(
|
|
30
|
+
subscriptionName: string,
|
|
31
|
+
accountDiscriminator: string,
|
|
32
|
+
program: Program,
|
|
33
|
+
decodeBufferFn: (accountName: string, ix: Buffer) => T,
|
|
34
|
+
options: { filters: MemcmpFilter[]; commitment?: Commitment } = {
|
|
35
|
+
filters: [],
|
|
36
|
+
},
|
|
37
|
+
resubTimeoutMs?: number
|
|
38
|
+
) {
|
|
39
|
+
this.subscriptionName = subscriptionName;
|
|
40
|
+
this.accountDiscriminator = accountDiscriminator;
|
|
41
|
+
this.program = program;
|
|
42
|
+
this.decodeBuffer = decodeBufferFn;
|
|
43
|
+
this.resubTimeoutMs = resubTimeoutMs;
|
|
44
|
+
this.options = options;
|
|
45
|
+
this.receivingData = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async subscribe(
|
|
49
|
+
onChange: (accountId: PublicKey, data: T, context: Context) => void
|
|
50
|
+
): Promise<void> {
|
|
51
|
+
if (this.listenerId) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.onChange = onChange;
|
|
56
|
+
|
|
57
|
+
this.listenerId = this.program.provider.connection.onProgramAccountChange(
|
|
58
|
+
this.program.programId,
|
|
59
|
+
(keyedAccountInfo, context) => {
|
|
60
|
+
if (this.resubTimeoutMs) {
|
|
61
|
+
this.receivingData = true;
|
|
62
|
+
clearTimeout(this.timeoutId);
|
|
63
|
+
this.handleRpcResponse(context, keyedAccountInfo);
|
|
64
|
+
this.setTimeout();
|
|
65
|
+
} else {
|
|
66
|
+
this.handleRpcResponse(context, keyedAccountInfo);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
this.options.commitment ??
|
|
70
|
+
(this.program.provider as AnchorProvider).opts.commitment,
|
|
71
|
+
this.options.filters
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (this.resubTimeoutMs) {
|
|
75
|
+
this.setTimeout();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private setTimeout(): void {
|
|
80
|
+
if (!this.onChange) {
|
|
81
|
+
throw new Error('onChange callback function must be set');
|
|
82
|
+
}
|
|
83
|
+
this.timeoutId = setTimeout(async () => {
|
|
84
|
+
if (this.receivingData) {
|
|
85
|
+
console.log(
|
|
86
|
+
`No ws data from ${this.subscriptionName} in ${this.resubTimeoutMs}ms, resubscribing`
|
|
87
|
+
);
|
|
88
|
+
await this.unsubscribe();
|
|
89
|
+
this.receivingData = false;
|
|
90
|
+
await this.subscribe(this.onChange);
|
|
91
|
+
}
|
|
92
|
+
}, this.resubTimeoutMs);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
handleRpcResponse(
|
|
96
|
+
context: Context,
|
|
97
|
+
keyedAccountInfo: KeyedAccountInfo
|
|
98
|
+
): void {
|
|
99
|
+
const newSlot = context.slot;
|
|
100
|
+
let newBuffer: Buffer | undefined = undefined;
|
|
101
|
+
if (keyedAccountInfo) {
|
|
102
|
+
newBuffer = keyedAccountInfo.accountInfo.data;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!this.bufferAndSlot) {
|
|
106
|
+
this.bufferAndSlot = {
|
|
107
|
+
buffer: newBuffer,
|
|
108
|
+
slot: newSlot,
|
|
109
|
+
};
|
|
110
|
+
if (newBuffer) {
|
|
111
|
+
const account = this.decodeBuffer(this.accountDiscriminator, newBuffer);
|
|
112
|
+
this.dataAndSlot = {
|
|
113
|
+
data: account,
|
|
114
|
+
slot: newSlot,
|
|
115
|
+
accountId: keyedAccountInfo.accountId,
|
|
116
|
+
};
|
|
117
|
+
this.onChange(keyedAccountInfo.accountId, account, context);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (newSlot <= this.bufferAndSlot.slot) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const oldBuffer = this.bufferAndSlot.buffer;
|
|
127
|
+
if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) {
|
|
128
|
+
this.bufferAndSlot = {
|
|
129
|
+
buffer: newBuffer,
|
|
130
|
+
slot: newSlot,
|
|
131
|
+
};
|
|
132
|
+
const account = this.decodeBuffer(this.accountDiscriminator, newBuffer);
|
|
133
|
+
this.dataAndSlot = {
|
|
134
|
+
data: account,
|
|
135
|
+
slot: newSlot,
|
|
136
|
+
accountId: keyedAccountInfo.accountId,
|
|
137
|
+
};
|
|
138
|
+
this.onChange(keyedAccountInfo.accountId, account, context);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
unsubscribe(): Promise<void> {
|
|
143
|
+
if (this.listenerId) {
|
|
144
|
+
const promise =
|
|
145
|
+
this.program.provider.connection.removeAccountChangeListener(
|
|
146
|
+
this.listenerId
|
|
147
|
+
);
|
|
148
|
+
this.listenerId = undefined;
|
|
149
|
+
return promise;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -14,17 +14,23 @@ import { UserAccount } from '../types';
|
|
|
14
14
|
|
|
15
15
|
export class WebSocketUserAccountSubscriber implements UserAccountSubscriber {
|
|
16
16
|
isSubscribed: boolean;
|
|
17
|
+
reconnectTimeoutMs?: number;
|
|
17
18
|
program: Program;
|
|
18
19
|
eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
|
|
19
20
|
userAccountPublicKey: PublicKey;
|
|
20
21
|
|
|
21
22
|
userDataAccountSubscriber: AccountSubscriber<UserAccount>;
|
|
22
23
|
|
|
23
|
-
public constructor(
|
|
24
|
+
public constructor(
|
|
25
|
+
program: Program,
|
|
26
|
+
userAccountPublicKey: PublicKey,
|
|
27
|
+
reconnectTimeoutMs?: number
|
|
28
|
+
) {
|
|
24
29
|
this.isSubscribed = false;
|
|
25
30
|
this.program = program;
|
|
26
31
|
this.userAccountPublicKey = userAccountPublicKey;
|
|
27
32
|
this.eventEmitter = new EventEmitter();
|
|
33
|
+
this.reconnectTimeoutMs = reconnectTimeoutMs;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
async subscribe(userAccount?: UserAccount): Promise<boolean> {
|
|
@@ -35,7 +41,9 @@ export class WebSocketUserAccountSubscriber implements UserAccountSubscriber {
|
|
|
35
41
|
this.userDataAccountSubscriber = new WebSocketAccountSubscriber(
|
|
36
42
|
'user',
|
|
37
43
|
this.program,
|
|
38
|
-
this.userAccountPublicKey
|
|
44
|
+
this.userAccountPublicKey,
|
|
45
|
+
undefined,
|
|
46
|
+
this.reconnectTimeoutMs
|
|
39
47
|
);
|
|
40
48
|
|
|
41
49
|
if (userAccount) {
|
|
@@ -16,17 +16,23 @@ export class WebSocketUserStatsAccountSubscriber
|
|
|
16
16
|
implements UserStatsAccountSubscriber
|
|
17
17
|
{
|
|
18
18
|
isSubscribed: boolean;
|
|
19
|
+
reconnectTimeoutMs?: number;
|
|
19
20
|
program: Program;
|
|
20
21
|
eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
|
|
21
22
|
userStatsAccountPublicKey: PublicKey;
|
|
22
23
|
|
|
23
24
|
userStatsAccountSubscriber: AccountSubscriber<UserStatsAccount>;
|
|
24
25
|
|
|
25
|
-
public constructor(
|
|
26
|
+
public constructor(
|
|
27
|
+
program: Program,
|
|
28
|
+
userStatsAccountPublicKey: PublicKey,
|
|
29
|
+
reconnectTimeoutMs?: number
|
|
30
|
+
) {
|
|
26
31
|
this.isSubscribed = false;
|
|
27
32
|
this.program = program;
|
|
28
33
|
this.userStatsAccountPublicKey = userStatsAccountPublicKey;
|
|
29
34
|
this.eventEmitter = new EventEmitter();
|
|
35
|
+
this.reconnectTimeoutMs = reconnectTimeoutMs;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
async subscribe(userStatsAccount?: UserStatsAccount): Promise<boolean> {
|
|
@@ -37,7 +43,9 @@ export class WebSocketUserStatsAccountSubscriber
|
|
|
37
43
|
this.userStatsAccountSubscriber = new WebSocketAccountSubscriber(
|
|
38
44
|
'userStats',
|
|
39
45
|
this.program,
|
|
40
|
-
this.userStatsAccountPublicKey
|
|
46
|
+
this.userStatsAccountPublicKey,
|
|
47
|
+
undefined,
|
|
48
|
+
this.reconnectTimeoutMs
|
|
41
49
|
);
|
|
42
50
|
|
|
43
51
|
if (userStatsAccount) {
|
|
@@ -4,48 +4,57 @@ import { getUserFilter, getUserWithAuctionFilter } from '../memcmp';
|
|
|
4
4
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
6
6
|
import { UserAccount } from '../types';
|
|
7
|
-
import { ConfirmOptions } from '@solana/web3.js';
|
|
7
|
+
import { ConfirmOptions, Context, PublicKey } from '@solana/web3.js';
|
|
8
|
+
import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber';
|
|
8
9
|
|
|
9
10
|
export class AuctionSubscriber {
|
|
10
11
|
private driftClient: DriftClient;
|
|
11
12
|
private opts: ConfirmOptions;
|
|
13
|
+
private resubTimeoutMs?: number;
|
|
12
14
|
|
|
13
15
|
eventEmitter: StrictEventEmitter<EventEmitter, AuctionSubscriberEvents>;
|
|
16
|
+
private subscriber: WebSocketProgramAccountSubscriber<UserAccount>;
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
constructor({ driftClient, opts }: AuctionSubscriberConfig) {
|
|
18
|
+
constructor({ driftClient, opts, resubTimeoutMs }: AuctionSubscriberConfig) {
|
|
18
19
|
this.driftClient = driftClient;
|
|
19
20
|
this.opts = opts || this.driftClient.opts;
|
|
20
21
|
this.eventEmitter = new EventEmitter();
|
|
22
|
+
this.resubTimeoutMs = resubTimeoutMs;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
public async subscribe() {
|
|
24
|
-
this.
|
|
25
|
-
this.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
if (!this.subscriber) {
|
|
27
|
+
this.subscriber = new WebSocketProgramAccountSubscriber<UserAccount>(
|
|
28
|
+
'AuctionSubscriber',
|
|
29
|
+
'User',
|
|
30
|
+
this.driftClient.program,
|
|
31
|
+
this.driftClient.program.account.user.coder.accounts.decode.bind(
|
|
32
|
+
this.driftClient.program.account.user.coder.accounts
|
|
33
|
+
),
|
|
34
|
+
{
|
|
35
|
+
filters: [getUserFilter(), getUserWithAuctionFilter()],
|
|
36
|
+
commitment: this.driftClient.opts.commitment,
|
|
37
|
+
},
|
|
38
|
+
this.resubTimeoutMs
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await this.subscriber.subscribe(
|
|
43
|
+
(accountId: PublicKey, data: UserAccount, context: Context) => {
|
|
32
44
|
this.eventEmitter.emit(
|
|
33
45
|
'onAccountUpdate',
|
|
34
|
-
|
|
35
|
-
|
|
46
|
+
data,
|
|
47
|
+
accountId,
|
|
36
48
|
context.slot
|
|
37
49
|
);
|
|
38
|
-
}
|
|
39
|
-
this.driftClient.opts.commitment,
|
|
40
|
-
[getUserFilter(), getUserWithAuctionFilter()]
|
|
50
|
+
}
|
|
41
51
|
);
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
public async unsubscribe() {
|
|
45
|
-
if (this.
|
|
46
|
-
|
|
47
|
-
this.websocketId
|
|
48
|
-
);
|
|
55
|
+
if (!this.subscriber) {
|
|
56
|
+
return;
|
|
49
57
|
}
|
|
58
|
+
this.subscriber.unsubscribe();
|
|
50
59
|
}
|
|
51
60
|
}
|
package/src/driftClient.ts
CHANGED
|
@@ -278,7 +278,8 @@ export class DriftClient {
|
|
|
278
278
|
config.perpMarketIndexes ?? [],
|
|
279
279
|
config.spotMarketIndexes ?? [],
|
|
280
280
|
config.oracleInfos ?? [],
|
|
281
|
-
noMarketsAndOraclesSpecified
|
|
281
|
+
noMarketsAndOraclesSpecified,
|
|
282
|
+
config.accountSubscription?.resubTimeoutMs
|
|
282
283
|
);
|
|
283
284
|
}
|
|
284
285
|
this.eventEmitter = this.accountSubscriber.eventEmitter;
|
package/src/driftClientConfig.ts
CHANGED
|
@@ -30,6 +30,7 @@ export class OrderSubscriber {
|
|
|
30
30
|
this.subscription = new WebsocketSubscription({
|
|
31
31
|
orderSubscriber: this,
|
|
32
32
|
skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
|
|
33
|
+
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
this.eventEmitter = new EventEmitter();
|
|
@@ -86,7 +87,12 @@ export class OrderSubscriber {
|
|
|
86
87
|
programAccount.account.data[1]
|
|
87
88
|
);
|
|
88
89
|
programAccountSet.add(key);
|
|
89
|
-
|
|
90
|
+
const userAccount =
|
|
91
|
+
this.driftClient.program.account.user.coder.accounts.decode(
|
|
92
|
+
'User',
|
|
93
|
+
buffer
|
|
94
|
+
) as UserAccount;
|
|
95
|
+
this.tryUpdateUserAccount(key, userAccount, slot);
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
for (const key of this.usersAccounts.keys()) {
|
|
@@ -102,14 +108,13 @@ export class OrderSubscriber {
|
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
|
|
105
|
-
tryUpdateUserAccount(
|
|
111
|
+
tryUpdateUserAccount(
|
|
112
|
+
key: string,
|
|
113
|
+
userAccount: UserAccount,
|
|
114
|
+
slot: number
|
|
115
|
+
): void {
|
|
106
116
|
const slotAndUserAccount = this.usersAccounts.get(key);
|
|
107
117
|
if (!slotAndUserAccount || slotAndUserAccount.slot < slot) {
|
|
108
|
-
const userAccount =
|
|
109
|
-
this.driftClient.program.account.user.coder.accounts.decode(
|
|
110
|
-
'User',
|
|
111
|
-
buffer
|
|
112
|
-
) as UserAccount;
|
|
113
118
|
const newOrders = userAccount.orders.filter(
|
|
114
119
|
(order) =>
|
|
115
120
|
order.slot.toNumber() > (slotAndUserAccount?.slot ?? 0) &&
|
|
@@ -1,42 +1,57 @@
|
|
|
1
1
|
import { OrderSubscriber } from './OrderSubscriber';
|
|
2
2
|
import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
|
|
3
|
+
import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber';
|
|
4
|
+
import { UserAccount } from '../types';
|
|
5
|
+
import { Context, PublicKey } from '@solana/web3.js';
|
|
3
6
|
|
|
4
7
|
export class WebsocketSubscription {
|
|
5
8
|
private orderSubscriber: OrderSubscriber;
|
|
6
9
|
private skipInitialLoad: boolean;
|
|
10
|
+
private resubTimeoutMs?: number;
|
|
7
11
|
|
|
8
|
-
private
|
|
12
|
+
private subscriber: WebSocketProgramAccountSubscriber<UserAccount>;
|
|
9
13
|
|
|
10
14
|
constructor({
|
|
11
15
|
orderSubscriber,
|
|
12
16
|
skipInitialLoad = false,
|
|
17
|
+
resubTimeoutMs,
|
|
13
18
|
}: {
|
|
14
19
|
orderSubscriber: OrderSubscriber;
|
|
15
20
|
skipInitialLoad?: boolean;
|
|
21
|
+
resubTimeoutMs?: number;
|
|
16
22
|
}) {
|
|
17
23
|
this.orderSubscriber = orderSubscriber;
|
|
18
24
|
this.skipInitialLoad = skipInitialLoad;
|
|
25
|
+
this.resubTimeoutMs = resubTimeoutMs;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
public async subscribe(): Promise<void> {
|
|
22
|
-
if (this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
keyAccountInfo.accountInfo.data,
|
|
34
|
-
context.slot
|
|
35
|
-
);
|
|
29
|
+
if (!this.subscriber) {
|
|
30
|
+
this.subscriber = new WebSocketProgramAccountSubscriber<UserAccount>(
|
|
31
|
+
'OrderSubscriber',
|
|
32
|
+
'User',
|
|
33
|
+
this.orderSubscriber.driftClient.program,
|
|
34
|
+
this.orderSubscriber.driftClient.program.account.user.coder.accounts.decode.bind(
|
|
35
|
+
this.orderSubscriber.driftClient.program.account.user.coder.accounts
|
|
36
|
+
),
|
|
37
|
+
{
|
|
38
|
+
filters: [getUserFilter(), getNonIdleUserFilter()],
|
|
39
|
+
commitment: this.orderSubscriber.driftClient.opts.commitment,
|
|
36
40
|
},
|
|
37
|
-
this.
|
|
38
|
-
[getUserFilter(), getNonIdleUserFilter()]
|
|
41
|
+
this.resubTimeoutMs
|
|
39
42
|
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
await this.subscriber.subscribe(
|
|
46
|
+
(accountId: PublicKey, account: UserAccount, context: Context) => {
|
|
47
|
+
const userKey = accountId.toBase58();
|
|
48
|
+
this.orderSubscriber.tryUpdateUserAccount(
|
|
49
|
+
userKey,
|
|
50
|
+
account,
|
|
51
|
+
context.slot
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
40
55
|
|
|
41
56
|
if (!this.skipInitialLoad) {
|
|
42
57
|
await this.orderSubscriber.fetch();
|
|
@@ -44,11 +59,6 @@ export class WebsocketSubscription {
|
|
|
44
59
|
}
|
|
45
60
|
|
|
46
61
|
public async unsubscribe(): Promise<void> {
|
|
47
|
-
|
|
48
|
-
await this.orderSubscriber.driftClient.connection.removeProgramAccountChangeListener(
|
|
49
|
-
this.websocketId
|
|
50
|
-
);
|
|
51
|
-
this.websocketId = undefined;
|
|
52
|
-
}
|
|
62
|
+
this.subscriber.unsubscribe();
|
|
53
63
|
}
|
|
54
64
|
}
|
package/src/user.ts
CHANGED
|
@@ -1465,8 +1465,10 @@ export class User {
|
|
|
1465
1465
|
* calculates current user leverage which is (total liability size) / (net asset value)
|
|
1466
1466
|
* @returns : Precision TEN_THOUSAND
|
|
1467
1467
|
*/
|
|
1468
|
-
public getLeverage(): BN {
|
|
1469
|
-
return this.calculateLeverageFromComponents(
|
|
1468
|
+
public getLeverage(includeOpenOrders = true): BN {
|
|
1469
|
+
return this.calculateLeverageFromComponents(
|
|
1470
|
+
this.getLeverageComponents(includeOpenOrders)
|
|
1471
|
+
);
|
|
1470
1472
|
}
|
|
1471
1473
|
|
|
1472
1474
|
calculateLeverageFromComponents({
|
|
@@ -1491,7 +1493,7 @@ export class User {
|
|
|
1491
1493
|
return totalLiabilityValue.mul(TEN_THOUSAND).div(netAssetValue);
|
|
1492
1494
|
}
|
|
1493
1495
|
|
|
1494
|
-
getLeverageComponents(): {
|
|
1496
|
+
getLeverageComponents(includeOpenOrders = true): {
|
|
1495
1497
|
perpLiabilityValue: BN;
|
|
1496
1498
|
perpPnl: BN;
|
|
1497
1499
|
spotAssetValue: BN;
|
|
@@ -1500,7 +1502,7 @@ export class User {
|
|
|
1500
1502
|
const perpLiability = this.getTotalPerpPositionValue(
|
|
1501
1503
|
undefined,
|
|
1502
1504
|
undefined,
|
|
1503
|
-
|
|
1505
|
+
includeOpenOrders
|
|
1504
1506
|
);
|
|
1505
1507
|
const perpPnl = this.getUnrealizedPNL(true);
|
|
1506
1508
|
|
|
@@ -1511,7 +1513,7 @@ export class User {
|
|
|
1511
1513
|
undefined,
|
|
1512
1514
|
undefined,
|
|
1513
1515
|
undefined,
|
|
1514
|
-
|
|
1516
|
+
includeOpenOrders
|
|
1515
1517
|
);
|
|
1516
1518
|
|
|
1517
1519
|
return {
|