@drift-labs/sdk 2.41.0-beta.0 → 2.41.0-beta.1
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/README.md +0 -1
- package/VERSION +1 -1
- package/bun.lockb +0 -0
- 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/addresses/pda.d.ts +1 -0
- package/lib/addresses/pda.js +5 -1
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +20 -0
- 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/dlob/DLOB.d.ts +2 -6
- package/lib/dlob/DLOB.js +9 -11
- package/lib/dlob/DLOBSubscriber.js +4 -7
- package/lib/dlob/orderBookLevels.d.ts +4 -2
- package/lib/dlob/orderBookLevels.js +79 -16
- package/lib/driftClient.d.ts +2 -1
- package/lib/driftClient.js +5 -4
- package/lib/driftClientConfig.d.ts +1 -0
- package/lib/factory/bigNum.js +4 -2
- package/lib/idl/drift.json +222 -2
- package/lib/jupiter/jupiterClient.d.ts +4 -1
- package/lib/jupiter/jupiterClient.js +4 -1
- package/lib/math/amm.js +7 -2
- package/lib/math/auction.d.ts +12 -1
- package/lib/math/auction.js +22 -1
- package/lib/math/market.js +2 -4
- package/lib/math/superStake.d.ts +51 -4
- package/lib/math/superStake.js +173 -21
- package/lib/math/trade.js +2 -4
- package/lib/math/utils.d.ts +1 -0
- package/lib/math/utils.js +10 -1
- 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 +15 -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 +2 -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/addresses/pda.ts +9 -0
- package/src/adminClient.ts +32 -0
- package/src/auctionSubscriber/auctionSubscriber.ts +30 -21
- package/src/auctionSubscriber/types.ts +1 -0
- package/src/dlob/DLOB.ts +9 -32
- package/src/dlob/DLOBSubscriber.ts +8 -7
- package/src/dlob/orderBookLevels.ts +133 -32
- package/src/driftClient.ts +5 -1
- package/src/driftClientConfig.ts +1 -0
- package/src/factory/bigNum.ts +2 -0
- package/src/idl/drift.json +222 -2
- package/src/jupiter/jupiterClient.ts +6 -0
- package/src/math/amm.ts +9 -2
- package/src/math/auction.ts +36 -2
- package/src/math/market.ts +4 -9
- package/src/math/superStake.ts +247 -23
- package/src/math/trade.ts +3 -11
- package/src/math/utils.ts +12 -0
- package/src/orderSubscriber/OrderSubscriber.ts +12 -7
- package/src/orderSubscriber/WebsocketSubscription.ts +34 -23
- package/src/orderSubscriber/types.ts +1 -0
- package/src/user.ts +7 -5
- package/tests/amm/test.ts +402 -0
- package/tests/auctions/test.ts +66 -0
- package/tests/dlob/test.ts +1 -73
|
@@ -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) {
|
package/src/addresses/pda.ts
CHANGED
|
@@ -214,3 +214,12 @@ export function getReferrerNamePublicKeySync(
|
|
|
214
214
|
programId
|
|
215
215
|
)[0];
|
|
216
216
|
}
|
|
217
|
+
|
|
218
|
+
export function getProtocolIfSharesTransferConfigPublicKey(
|
|
219
|
+
programId: PublicKey
|
|
220
|
+
): PublicKey {
|
|
221
|
+
return PublicKey.findProgramAddressSync(
|
|
222
|
+
[Buffer.from(anchor.utils.bytes.utf8.encode('if_shares_transfer_config'))],
|
|
223
|
+
programId
|
|
224
|
+
)[0];
|
|
225
|
+
}
|
package/src/adminClient.ts
CHANGED
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
getSerumOpenOrdersPublicKey,
|
|
26
26
|
getSerumFulfillmentConfigPublicKey,
|
|
27
27
|
getPhoenixFulfillmentConfigPublicKey,
|
|
28
|
+
getProtocolIfSharesTransferConfigPublicKey,
|
|
28
29
|
} from './addresses/pda';
|
|
29
30
|
import { squareRootBN } from './math/utils';
|
|
30
31
|
import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
@@ -1516,4 +1517,35 @@ export class AdminClient extends DriftClient {
|
|
|
1516
1517
|
}
|
|
1517
1518
|
);
|
|
1518
1519
|
}
|
|
1520
|
+
|
|
1521
|
+
public async initializeProtocolIfSharesTransferConfig(): Promise<TransactionSignature> {
|
|
1522
|
+
return await this.program.rpc.initializeProtocolIfSharesTransferConfig({
|
|
1523
|
+
accounts: {
|
|
1524
|
+
admin: this.wallet.publicKey,
|
|
1525
|
+
state: await this.getStatePublicKey(),
|
|
1526
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
1527
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
1528
|
+
protocolIfSharesTransferConfig:
|
|
1529
|
+
getProtocolIfSharesTransferConfigPublicKey(this.program.programId),
|
|
1530
|
+
},
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
public async updateProtocolIfSharesTransferConfig(
|
|
1535
|
+
whitelistedSigners?: PublicKey[],
|
|
1536
|
+
maxTransferPerEpoch?: BN
|
|
1537
|
+
): Promise<TransactionSignature> {
|
|
1538
|
+
return await this.program.rpc.updateProtocolIfSharesTransferConfig(
|
|
1539
|
+
whitelistedSigners || null,
|
|
1540
|
+
maxTransferPerEpoch,
|
|
1541
|
+
{
|
|
1542
|
+
accounts: {
|
|
1543
|
+
admin: this.wallet.publicKey,
|
|
1544
|
+
state: await this.getStatePublicKey(),
|
|
1545
|
+
protocolIfSharesTransferConfig:
|
|
1546
|
+
getProtocolIfSharesTransferConfigPublicKey(this.program.programId),
|
|
1547
|
+
},
|
|
1548
|
+
}
|
|
1549
|
+
);
|
|
1550
|
+
}
|
|
1519
1551
|
}
|
|
@@ -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/dlob/DLOB.ts
CHANGED
|
@@ -675,7 +675,7 @@ export class DLOB {
|
|
|
675
675
|
marketType,
|
|
676
676
|
oraclePriceData,
|
|
677
677
|
takingOrderGenerator,
|
|
678
|
-
this.
|
|
678
|
+
this.getRestingLimitBids.bind(this),
|
|
679
679
|
(takerPrice, makerPrice) => {
|
|
680
680
|
if (isVariant(marketType, 'spot')) {
|
|
681
681
|
if (takerPrice === undefined) {
|
|
@@ -687,8 +687,7 @@ export class DLOB {
|
|
|
687
687
|
}
|
|
688
688
|
}
|
|
689
689
|
return takerPrice === undefined || takerPrice.lte(makerPrice);
|
|
690
|
-
}
|
|
691
|
-
fallbackAsk
|
|
690
|
+
}
|
|
692
691
|
);
|
|
693
692
|
for (const takingAskCrossingBid of takingAsksCrossingBids) {
|
|
694
693
|
nodesToFill.push(takingAskCrossingBid);
|
|
@@ -732,7 +731,7 @@ export class DLOB {
|
|
|
732
731
|
marketType,
|
|
733
732
|
oraclePriceData,
|
|
734
733
|
takingOrderGenerator,
|
|
735
|
-
this.
|
|
734
|
+
this.getRestingLimitAsks.bind(this),
|
|
736
735
|
(takerPrice, makerPrice) => {
|
|
737
736
|
if (isVariant(marketType, 'spot')) {
|
|
738
737
|
if (takerPrice === undefined) {
|
|
@@ -745,8 +744,7 @@ export class DLOB {
|
|
|
745
744
|
}
|
|
746
745
|
|
|
747
746
|
return takerPrice === undefined || takerPrice.gte(makerPrice);
|
|
748
|
-
}
|
|
749
|
-
fallbackBid
|
|
747
|
+
}
|
|
750
748
|
);
|
|
751
749
|
|
|
752
750
|
for (const takingBidToFill of takingBidsToFill) {
|
|
@@ -790,11 +788,9 @@ export class DLOB {
|
|
|
790
788
|
marketIndex: number,
|
|
791
789
|
slot: number,
|
|
792
790
|
marketType: MarketType,
|
|
793
|
-
oraclePriceData: OraclePriceData
|
|
794
|
-
fallbackPrice?: BN
|
|
791
|
+
oraclePriceData: OraclePriceData
|
|
795
792
|
) => Generator<DLOBNode>,
|
|
796
|
-
doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean
|
|
797
|
-
fallbackPrice?: BN
|
|
793
|
+
doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean
|
|
798
794
|
): NodeToFill[] {
|
|
799
795
|
const nodesToFill = new Array<NodeToFill>();
|
|
800
796
|
|
|
@@ -803,8 +799,7 @@ export class DLOB {
|
|
|
803
799
|
marketIndex,
|
|
804
800
|
slot,
|
|
805
801
|
marketType,
|
|
806
|
-
oraclePriceData
|
|
807
|
-
fallbackPrice
|
|
802
|
+
oraclePriceData
|
|
808
803
|
);
|
|
809
804
|
|
|
810
805
|
for (const makerNode of makerNodeGenerator) {
|
|
@@ -1714,8 +1709,6 @@ export class DLOB {
|
|
|
1714
1709
|
* @param slot
|
|
1715
1710
|
* @param oraclePriceData
|
|
1716
1711
|
* @param depth how many levels of the order book to return
|
|
1717
|
-
* @param fallbackAsk best ask for fallback liquidity, only relevant for perps
|
|
1718
|
-
* @param fallbackBid best bid for fallback liquidity, only relevant for perps
|
|
1719
1712
|
* @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber}
|
|
1720
1713
|
*/
|
|
1721
1714
|
public getL2({
|
|
@@ -1724,8 +1717,6 @@ export class DLOB {
|
|
|
1724
1717
|
slot,
|
|
1725
1718
|
oraclePriceData,
|
|
1726
1719
|
depth,
|
|
1727
|
-
fallbackAsk,
|
|
1728
|
-
fallbackBid,
|
|
1729
1720
|
fallbackL2Generators = [],
|
|
1730
1721
|
}: {
|
|
1731
1722
|
marketIndex: number;
|
|
@@ -1733,18 +1724,10 @@ export class DLOB {
|
|
|
1733
1724
|
slot: number;
|
|
1734
1725
|
oraclePriceData: OraclePriceData;
|
|
1735
1726
|
depth: number;
|
|
1736
|
-
fallbackAsk?: BN;
|
|
1737
|
-
fallbackBid?: BN;
|
|
1738
1727
|
fallbackL2Generators?: L2OrderBookGenerator[];
|
|
1739
1728
|
}): L2OrderBook {
|
|
1740
1729
|
const makerAskL2LevelGenerator = getL2GeneratorFromDLOBNodes(
|
|
1741
|
-
this.
|
|
1742
|
-
marketIndex,
|
|
1743
|
-
slot,
|
|
1744
|
-
marketType,
|
|
1745
|
-
oraclePriceData,
|
|
1746
|
-
fallbackBid
|
|
1747
|
-
),
|
|
1730
|
+
this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData),
|
|
1748
1731
|
oraclePriceData,
|
|
1749
1732
|
slot
|
|
1750
1733
|
);
|
|
@@ -1765,13 +1748,7 @@ export class DLOB {
|
|
|
1765
1748
|
const asks = createL2Levels(askL2LevelGenerator, depth);
|
|
1766
1749
|
|
|
1767
1750
|
const makerBidGenerator = getL2GeneratorFromDLOBNodes(
|
|
1768
|
-
this.
|
|
1769
|
-
marketIndex,
|
|
1770
|
-
slot,
|
|
1771
|
-
marketType,
|
|
1772
|
-
oraclePriceData,
|
|
1773
|
-
fallbackAsk
|
|
1774
|
-
),
|
|
1751
|
+
this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData),
|
|
1775
1752
|
oraclePriceData,
|
|
1776
1753
|
slot
|
|
1777
1754
|
);
|
|
@@ -10,12 +10,12 @@ import {
|
|
|
10
10
|
import { DriftClient } from '../driftClient';
|
|
11
11
|
import { isVariant, MarketType } from '../types';
|
|
12
12
|
import {
|
|
13
|
+
DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS,
|
|
13
14
|
getVammL2Generator,
|
|
14
15
|
L2OrderBook,
|
|
15
16
|
L2OrderBookGenerator,
|
|
16
17
|
L3OrderBook,
|
|
17
18
|
} from './orderBookLevels';
|
|
18
|
-
import { calculateAskPrice, calculateBidPrice } from '../math/market';
|
|
19
19
|
|
|
20
20
|
export class DLOBSubscriber {
|
|
21
21
|
driftClient: DriftClient;
|
|
@@ -103,8 +103,6 @@ export class DLOBSubscriber {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
let oraclePriceData;
|
|
106
|
-
let fallbackBid;
|
|
107
|
-
let fallbackAsk;
|
|
108
106
|
const isPerp = isVariant(marketType, 'perp');
|
|
109
107
|
if (isPerp) {
|
|
110
108
|
const perpMarketAccount =
|
|
@@ -112,19 +110,24 @@ export class DLOBSubscriber {
|
|
|
112
110
|
oraclePriceData = this.driftClient.getOracleDataForPerpMarket(
|
|
113
111
|
perpMarketAccount.marketIndex
|
|
114
112
|
);
|
|
115
|
-
fallbackBid = calculateBidPrice(perpMarketAccount, oraclePriceData);
|
|
116
|
-
fallbackAsk = calculateAskPrice(perpMarketAccount, oraclePriceData);
|
|
117
113
|
} else {
|
|
118
114
|
oraclePriceData =
|
|
119
115
|
this.driftClient.getOracleDataForSpotMarket(marketIndex);
|
|
120
116
|
}
|
|
121
117
|
|
|
122
118
|
if (isPerp && includeVamm) {
|
|
119
|
+
if (fallbackL2Generators.length > 0) {
|
|
120
|
+
throw new Error(
|
|
121
|
+
'includeVamm can only be used if fallbackL2Generators is empty'
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
123
125
|
fallbackL2Generators = [
|
|
124
126
|
getVammL2Generator({
|
|
125
127
|
marketAccount: this.driftClient.getPerpMarketAccount(marketIndex),
|
|
126
128
|
oraclePriceData,
|
|
127
129
|
numOrders: numVammOrders ?? depth,
|
|
130
|
+
topOfBookQuoteAmounts: DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS,
|
|
128
131
|
}),
|
|
129
132
|
];
|
|
130
133
|
}
|
|
@@ -135,8 +138,6 @@ export class DLOBSubscriber {
|
|
|
135
138
|
depth,
|
|
136
139
|
oraclePriceData,
|
|
137
140
|
slot: this.slotSource.getSlot(),
|
|
138
|
-
fallbackBid,
|
|
139
|
-
fallbackAsk,
|
|
140
141
|
fallbackL2Generators: fallbackL2Generators,
|
|
141
142
|
});
|
|
142
143
|
}
|