@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
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.48.0-beta.
|
|
1
|
+
2.48.0-beta.20
|
|
@@ -139,7 +139,7 @@ class BulkAccountLoader {
|
|
|
139
139
|
accountsToLoad.forEach((accountToLoad, j) => {
|
|
140
140
|
const key = accountToLoad.publicKey.toBase58();
|
|
141
141
|
const oldRPCResponse = this.bufferAndSlotMap.get(key);
|
|
142
|
-
if (oldRPCResponse && newSlot
|
|
142
|
+
if (oldRPCResponse && newSlot < oldRPCResponse.slot) {
|
|
143
143
|
return;
|
|
144
144
|
}
|
|
145
145
|
let newBuffer = undefined;
|
|
@@ -53,12 +53,17 @@ class PollingUserAccountSubscriber {
|
|
|
53
53
|
}
|
|
54
54
|
async fetch() {
|
|
55
55
|
var _a, _b;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
try {
|
|
57
|
+
const dataAndContext = await this.program.account.user.fetchAndContext(this.userAccountPublicKey, this.accountLoader.commitment);
|
|
58
|
+
if (dataAndContext.context.slot > ((_b = (_a = this.user) === null || _a === void 0 ? void 0 : _a.slot) !== null && _b !== void 0 ? _b : 0)) {
|
|
59
|
+
this.user = {
|
|
60
|
+
data: dataAndContext.data,
|
|
61
|
+
slot: dataAndContext.context.slot,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
console.log(`PollingUserAccountSubscriber.fetch() UserAccount does not exist: ${e.message}`);
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
doesAccountExist() {
|
|
@@ -80,7 +85,9 @@ class PollingUserAccountSubscriber {
|
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
getUserAccountAndSlot() {
|
|
83
|
-
this.
|
|
88
|
+
if (!this.doesAccountExist()) {
|
|
89
|
+
throw new types_1.NotSubscribedError('You must call `subscribe` or `fetch` before using this function');
|
|
90
|
+
}
|
|
84
91
|
return this.user;
|
|
85
92
|
}
|
|
86
93
|
updateData(userAccount, slot) {
|
|
@@ -53,12 +53,17 @@ class PollingUserStatsAccountSubscriber {
|
|
|
53
53
|
}
|
|
54
54
|
async fetch() {
|
|
55
55
|
var _a, _b;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
try {
|
|
57
|
+
const dataAndContext = await this.program.account.userStats.fetchAndContext(this.userStatsAccountPublicKey, this.accountLoader.commitment);
|
|
58
|
+
if (dataAndContext.context.slot > ((_b = (_a = this.userStats) === null || _a === void 0 ? void 0 : _a.slot) !== null && _b !== void 0 ? _b : 0)) {
|
|
59
|
+
this.userStats = {
|
|
60
|
+
data: dataAndContext.data,
|
|
61
|
+
slot: dataAndContext.context.slot,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
console.log(`PollingUserStatsAccountSubscriber.fetch() UserStatsAccount does not exist: ${e.message}`);
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
doesAccountExist() {
|
|
@@ -80,7 +85,9 @@ class PollingUserStatsAccountSubscriber {
|
|
|
80
85
|
}
|
|
81
86
|
}
|
|
82
87
|
getUserStatsAccountAndSlot() {
|
|
83
|
-
this.
|
|
88
|
+
if (!this.doesAccountExist()) {
|
|
89
|
+
throw new types_1.NotSubscribedError('You must call `subscribe` or `fetch` before using this function');
|
|
90
|
+
}
|
|
84
91
|
return this.userStats;
|
|
85
92
|
}
|
|
86
93
|
}
|
|
@@ -5,12 +5,13 @@ import { SpotMarketAccount, PerpMarketAccount, StateAccount } from '../types';
|
|
|
5
5
|
import { Program } from '@coral-xyz/anchor';
|
|
6
6
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
7
7
|
import { EventEmitter } from 'events';
|
|
8
|
-
import { PublicKey } from '@solana/web3.js';
|
|
8
|
+
import { Commitment, PublicKey } from '@solana/web3.js';
|
|
9
9
|
import { OracleInfo, OraclePriceData } from '../oracles/types';
|
|
10
10
|
import { OracleClientCache } from '../oracles/oracleClientCache';
|
|
11
11
|
export declare class WebSocketDriftClientAccountSubscriber implements DriftClientAccountSubscriber {
|
|
12
12
|
isSubscribed: boolean;
|
|
13
13
|
program: Program;
|
|
14
|
+
commitment?: Commitment;
|
|
14
15
|
perpMarketIndexes: number[];
|
|
15
16
|
spotMarketIndexes: number[];
|
|
16
17
|
oracleInfos: OracleInfo[];
|
|
@@ -25,7 +26,7 @@ export declare class WebSocketDriftClientAccountSubscriber implements DriftClien
|
|
|
25
26
|
private isSubscribing;
|
|
26
27
|
private subscriptionPromise;
|
|
27
28
|
private subscriptionPromiseResolver;
|
|
28
|
-
constructor(program: Program, perpMarketIndexes: number[], spotMarketIndexes: number[], oracleInfos: OracleInfo[], shouldFindAllMarketsAndOracles: boolean, resubTimeoutMs?: number);
|
|
29
|
+
constructor(program: Program, perpMarketIndexes: number[], spotMarketIndexes: number[], oracleInfos: OracleInfo[], shouldFindAllMarketsAndOracles: boolean, resubTimeoutMs?: number, commitment?: Commitment);
|
|
29
30
|
subscribe(): Promise<boolean>;
|
|
30
31
|
subscribeToPerpMarketAccounts(): Promise<boolean>;
|
|
31
32
|
subscribeToPerpMarketAccount(marketIndex: number): Promise<boolean>;
|
|
@@ -10,7 +10,7 @@ const oracleClientCache_1 = require("../oracles/oracleClientCache");
|
|
|
10
10
|
const quoteAssetOracleClient_1 = require("../oracles/quoteAssetOracleClient");
|
|
11
11
|
const config_1 = require("../config");
|
|
12
12
|
class WebSocketDriftClientAccountSubscriber {
|
|
13
|
-
constructor(program, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles, resubTimeoutMs) {
|
|
13
|
+
constructor(program, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles, resubTimeoutMs, commitment) {
|
|
14
14
|
this.oracleClientCache = new oracleClientCache_1.OracleClientCache();
|
|
15
15
|
this.perpMarketAccountSubscribers = new Map();
|
|
16
16
|
this.spotMarketAccountSubscribers = new Map();
|
|
@@ -24,6 +24,7 @@ class WebSocketDriftClientAccountSubscriber {
|
|
|
24
24
|
this.oracleInfos = oracleInfos;
|
|
25
25
|
this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
|
|
26
26
|
this.resubTimeoutMs = resubTimeoutMs;
|
|
27
|
+
this.commitment = commitment;
|
|
27
28
|
}
|
|
28
29
|
async subscribe() {
|
|
29
30
|
if (this.isSubscribed) {
|
|
@@ -44,7 +45,7 @@ class WebSocketDriftClientAccountSubscriber {
|
|
|
44
45
|
}
|
|
45
46
|
const statePublicKey = await (0, pda_1.getDriftStateAccountPublicKey)(this.program.programId);
|
|
46
47
|
// create and activate main state account subscription
|
|
47
|
-
this.stateAccountSubscriber = new webSocketAccountSubscriber_1.WebSocketAccountSubscriber('state', this.program, statePublicKey, undefined, this.resubTimeoutMs);
|
|
48
|
+
this.stateAccountSubscriber = new webSocketAccountSubscriber_1.WebSocketAccountSubscriber('state', this.program, statePublicKey, undefined, this.resubTimeoutMs, this.commitment);
|
|
48
49
|
await this.stateAccountSubscriber.subscribe((data) => {
|
|
49
50
|
this.eventEmitter.emit('stateAccountUpdate', data);
|
|
50
51
|
this.eventEmitter.emit('update');
|
|
@@ -69,7 +70,7 @@ class WebSocketDriftClientAccountSubscriber {
|
|
|
69
70
|
}
|
|
70
71
|
async subscribeToPerpMarketAccount(marketIndex) {
|
|
71
72
|
const perpMarketPublicKey = await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, marketIndex);
|
|
72
|
-
const accountSubscriber = new webSocketAccountSubscriber_1.WebSocketAccountSubscriber('perpMarket', this.program, perpMarketPublicKey, undefined, this.resubTimeoutMs);
|
|
73
|
+
const accountSubscriber = new webSocketAccountSubscriber_1.WebSocketAccountSubscriber('perpMarket', this.program, perpMarketPublicKey, undefined, this.resubTimeoutMs, this.commitment);
|
|
73
74
|
await accountSubscriber.subscribe((data) => {
|
|
74
75
|
this.eventEmitter.emit('perpMarketAccountUpdate', data);
|
|
75
76
|
this.eventEmitter.emit('update');
|
|
@@ -85,7 +86,7 @@ class WebSocketDriftClientAccountSubscriber {
|
|
|
85
86
|
}
|
|
86
87
|
async subscribeToSpotMarketAccount(marketIndex) {
|
|
87
88
|
const marketPublicKey = await (0, pda_1.getSpotMarketPublicKey)(this.program.programId, marketIndex);
|
|
88
|
-
const accountSubscriber = new webSocketAccountSubscriber_1.WebSocketAccountSubscriber('spotMarket', this.program, marketPublicKey, undefined, this.resubTimeoutMs);
|
|
89
|
+
const accountSubscriber = new webSocketAccountSubscriber_1.WebSocketAccountSubscriber('spotMarket', this.program, marketPublicKey, undefined, this.resubTimeoutMs, this.commitment);
|
|
89
90
|
await accountSubscriber.subscribe((data) => {
|
|
90
91
|
this.eventEmitter.emit('spotMarketAccountUpdate', data);
|
|
91
92
|
this.eventEmitter.emit('update');
|
|
@@ -105,7 +106,7 @@ class WebSocketDriftClientAccountSubscriber {
|
|
|
105
106
|
const client = this.oracleClientCache.get(oracleInfo.source, this.program.provider.connection);
|
|
106
107
|
const accountSubscriber = new webSocketAccountSubscriber_1.WebSocketAccountSubscriber('oracle', this.program, oracleInfo.publicKey, (buffer) => {
|
|
107
108
|
return client.getOraclePriceDataFromBuffer(buffer);
|
|
108
|
-
}, this.resubTimeoutMs);
|
|
109
|
+
}, this.resubTimeoutMs, this.commitment);
|
|
109
110
|
await accountSubscriber.subscribe((data) => {
|
|
110
111
|
this.eventEmitter.emit('oraclePriceUpdate', oracleInfo.publicKey, data);
|
|
111
112
|
this.eventEmitter.emit('update');
|
|
@@ -51,7 +51,7 @@ class WebSocketUserAccountSubscriber {
|
|
|
51
51
|
updateData(userAccount, slot) {
|
|
52
52
|
var _a;
|
|
53
53
|
const currentDataSlot = ((_a = this.userDataAccountSubscriber.dataAndSlot) === null || _a === void 0 ? void 0 : _a.slot) || 0;
|
|
54
|
-
if (currentDataSlot
|
|
54
|
+
if (currentDataSlot <= slot) {
|
|
55
55
|
this.userDataAccountSubscriber.setData(userAccount, slot);
|
|
56
56
|
this.eventEmitter.emit('userAccountUpdate', userAccount);
|
|
57
57
|
this.eventEmitter.emit('update');
|
|
@@ -120,6 +120,16 @@ exports.MainnetSpotMarkets = [
|
|
|
120
120
|
serumMarket: new web3_js_1.PublicKey('4E17F3BxtNVqzVsirxguuqkpYLtFgCR6NfTpccPh82WE'),
|
|
121
121
|
phoenixMarket: new web3_js_1.PublicKey('2sTMN9A1D1qeZLF95XQgJCUPiKe5DiV52jLfZGqMP46m'),
|
|
122
122
|
},
|
|
123
|
+
{
|
|
124
|
+
symbol: 'bSOL',
|
|
125
|
+
marketIndex: 8,
|
|
126
|
+
oracle: new web3_js_1.PublicKey('AFrYBhb5wKQtxRS9UA9YRS4V3dwFm7SqmS6DHKq6YVgo'),
|
|
127
|
+
oracleSource: __1.OracleSource.PYTH,
|
|
128
|
+
mint: new web3_js_1.PublicKey('bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1'),
|
|
129
|
+
precision: new __1.BN(10).pow(numericConstants_1.NINE),
|
|
130
|
+
precisionExp: numericConstants_1.NINE,
|
|
131
|
+
serumMarket: new web3_js_1.PublicKey('ARjaHVxGCQfTvvKjLd7U7srvk6orthZSE6uqWchCczZc'),
|
|
132
|
+
},
|
|
123
133
|
];
|
|
124
134
|
exports.SpotMarkets = {
|
|
125
135
|
devnet: exports.DevnetSpotMarkets,
|
|
@@ -93,7 +93,7 @@ function getVammL2Generator({ marketAccount, oraclePriceData, numOrders, now, to
|
|
|
93
93
|
}
|
|
94
94
|
const updatedAmm = (0, __1.calculateUpdatedAMM)(marketAccount.amm, oraclePriceData);
|
|
95
95
|
let [openBids, openAsks] = (0, __1.calculateMarketOpenBidAsk)(updatedAmm.baseAssetReserve, updatedAmm.minBaseAssetReserve, updatedAmm.maxBaseAssetReserve, updatedAmm.orderStepSize);
|
|
96
|
-
const minOrderSize = marketAccount.amm.
|
|
96
|
+
const minOrderSize = marketAccount.amm.minOrderSize;
|
|
97
97
|
if (openBids.lt(minOrderSize.muln(2))) {
|
|
98
98
|
openBids = __1.ZERO;
|
|
99
99
|
}
|
package/lib/driftClient.js
CHANGED
|
@@ -66,7 +66,7 @@ class DriftClient {
|
|
|
66
66
|
this._isSubscribed = val;
|
|
67
67
|
}
|
|
68
68
|
constructor(config) {
|
|
69
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
|
|
69
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
|
|
70
70
|
this.users = new Map();
|
|
71
71
|
this._isSubscribed = false;
|
|
72
72
|
this.perpMarketLastSlotCache = new Map();
|
|
@@ -143,11 +143,11 @@ class DriftClient {
|
|
|
143
143
|
this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, (_s = config.perpMarketIndexes) !== null && _s !== void 0 ? _s : [], (_t = config.spotMarketIndexes) !== null && _t !== void 0 ? _t : [], (_u = config.oracleInfos) !== null && _u !== void 0 ? _u : [], noMarketsAndOraclesSpecified);
|
|
144
144
|
}
|
|
145
145
|
else {
|
|
146
|
-
this.accountSubscriber = new webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber(this.program, (_v = config.perpMarketIndexes) !== null && _v !== void 0 ? _v : [], (_w = config.spotMarketIndexes) !== null && _w !== void 0 ? _w : [], (_x = config.oracleInfos) !== null && _x !== void 0 ? _x : [], noMarketsAndOraclesSpecified, (_y = config.accountSubscription) === null || _y === void 0 ? void 0 : _y.resubTimeoutMs);
|
|
146
|
+
this.accountSubscriber = new webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber(this.program, (_v = config.perpMarketIndexes) !== null && _v !== void 0 ? _v : [], (_w = config.spotMarketIndexes) !== null && _w !== void 0 ? _w : [], (_x = config.oracleInfos) !== null && _x !== void 0 ? _x : [], noMarketsAndOraclesSpecified, (_y = config.accountSubscription) === null || _y === void 0 ? void 0 : _y.resubTimeoutMs, (_z = config.accountSubscription) === null || _z === void 0 ? void 0 : _z.commitment);
|
|
147
147
|
}
|
|
148
148
|
this.eventEmitter = this.accountSubscriber.eventEmitter;
|
|
149
149
|
this.txSender =
|
|
150
|
-
(
|
|
150
|
+
(_0 = config.txSender) !== null && _0 !== void 0 ? _0 : new retryTxSender_1.RetryTxSender({
|
|
151
151
|
connection: this.connection,
|
|
152
152
|
wallet: this.wallet,
|
|
153
153
|
opts: this.opts,
|
|
@@ -1150,7 +1150,7 @@ class DriftClient {
|
|
|
1150
1150
|
withdrawIxs.push((0, spl_token_1.createCloseAccountInstruction)(associatedTokenAddress, authority, authority, []));
|
|
1151
1151
|
}
|
|
1152
1152
|
const tx = await this.buildTransaction(withdrawIxs, {
|
|
1153
|
-
computeUnits:
|
|
1153
|
+
computeUnits: 1400000,
|
|
1154
1154
|
});
|
|
1155
1155
|
const { txSig, slot } = await this.sendTransaction(tx, additionalSigners, this.opts);
|
|
1156
1156
|
this.spotMarketLastSlotCache.set(marketIndex, slot);
|
|
@@ -27,7 +27,7 @@ class EventSubscriber {
|
|
|
27
27
|
}
|
|
28
28
|
this.eventEmitter = new events_1.EventEmitter();
|
|
29
29
|
if (this.options.logProviderConfig.type === 'websocket') {
|
|
30
|
-
this.logProvider = new webSocketLogProvider_1.WebSocketLogProvider(this.connection, this.address, this.options.commitment);
|
|
30
|
+
this.logProvider = new webSocketLogProvider_1.WebSocketLogProvider(this.connection, this.address, this.options.commitment, this.options.resubTimeoutMs);
|
|
31
31
|
}
|
|
32
32
|
else {
|
|
33
33
|
this.logProvider = new pollingLogProvider_1.PollingLogProvider(this.connection, this.address, options.commitment, this.options.logProviderConfig.frequency, this.options.logProviderConfig.batchSize);
|
package/lib/events/types.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type EventSubscriptionOptions = {
|
|
|
10
10
|
maxTx?: number;
|
|
11
11
|
logProviderConfig?: LogProviderConfig;
|
|
12
12
|
untilTx?: TransactionSignature;
|
|
13
|
+
resubTimeoutMs?: number;
|
|
13
14
|
};
|
|
14
15
|
export declare const DefaultEventSubscriptionOptions: EventSubscriptionOptions;
|
|
15
16
|
export type EventSubscriptionOrderBy = 'blockchain' | 'client';
|
|
@@ -4,9 +4,15 @@ export declare class WebSocketLogProvider implements LogProvider {
|
|
|
4
4
|
private connection;
|
|
5
5
|
private address;
|
|
6
6
|
private commitment;
|
|
7
|
+
private resubTimeoutMs?;
|
|
7
8
|
private subscriptionId;
|
|
8
|
-
|
|
9
|
+
private isUnsubscribing;
|
|
10
|
+
private receivingData;
|
|
11
|
+
private timeoutId?;
|
|
12
|
+
private callback?;
|
|
13
|
+
constructor(connection: Connection, address: PublicKey, commitment: Commitment, resubTimeoutMs?: number);
|
|
9
14
|
subscribe(callback: logProviderCallback): boolean;
|
|
10
15
|
isSubscribed(): boolean;
|
|
11
16
|
unsubscribe(): Promise<boolean>;
|
|
17
|
+
private setTimeout;
|
|
12
18
|
}
|
|
@@ -2,29 +2,70 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.WebSocketLogProvider = void 0;
|
|
4
4
|
class WebSocketLogProvider {
|
|
5
|
-
constructor(connection, address, commitment) {
|
|
5
|
+
constructor(connection, address, commitment, resubTimeoutMs) {
|
|
6
6
|
this.connection = connection;
|
|
7
7
|
this.address = address;
|
|
8
8
|
this.commitment = commitment;
|
|
9
|
+
this.resubTimeoutMs = resubTimeoutMs;
|
|
10
|
+
this.isUnsubscribing = false;
|
|
11
|
+
this.receivingData = false;
|
|
9
12
|
}
|
|
10
13
|
subscribe(callback) {
|
|
11
14
|
if (this.subscriptionId) {
|
|
12
15
|
return true;
|
|
13
16
|
}
|
|
17
|
+
this.callback = callback;
|
|
14
18
|
this.subscriptionId = this.connection.onLogs(this.address, (logs, ctx) => {
|
|
19
|
+
if (this.resubTimeoutMs && !this.isUnsubscribing) {
|
|
20
|
+
this.receivingData = true;
|
|
21
|
+
clearTimeout(this.timeoutId);
|
|
22
|
+
this.setTimeout();
|
|
23
|
+
}
|
|
15
24
|
callback(logs.signature, ctx.slot, logs.logs, undefined);
|
|
16
25
|
}, this.commitment);
|
|
26
|
+
if (this.resubTimeoutMs) {
|
|
27
|
+
this.setTimeout();
|
|
28
|
+
}
|
|
17
29
|
return true;
|
|
18
30
|
}
|
|
19
31
|
isSubscribed() {
|
|
20
32
|
return this.subscriptionId !== undefined;
|
|
21
33
|
}
|
|
22
34
|
async unsubscribe() {
|
|
35
|
+
this.isUnsubscribing = true;
|
|
36
|
+
clearTimeout(this.timeoutId);
|
|
23
37
|
if (this.subscriptionId !== undefined) {
|
|
24
|
-
|
|
25
|
-
|
|
38
|
+
this.connection
|
|
39
|
+
.removeOnLogsListener(this.subscriptionId)
|
|
40
|
+
.then(() => {
|
|
41
|
+
this.subscriptionId = undefined;
|
|
42
|
+
this.isUnsubscribing = false;
|
|
43
|
+
return true;
|
|
44
|
+
})
|
|
45
|
+
.catch((err) => {
|
|
46
|
+
console.log('Error unsubscribing from logs: ', err);
|
|
47
|
+
this.isUnsubscribing = false;
|
|
48
|
+
return false;
|
|
49
|
+
});
|
|
26
50
|
}
|
|
27
|
-
|
|
51
|
+
else {
|
|
52
|
+
this.isUnsubscribing = false;
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
setTimeout() {
|
|
57
|
+
this.timeoutId = setTimeout(async () => {
|
|
58
|
+
if (this.isUnsubscribing) {
|
|
59
|
+
// If we are in the process of unsubscribing, do not attempt to resubscribe
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (this.receivingData) {
|
|
63
|
+
console.log(`No log data in ${this.resubTimeoutMs}ms, resubscribing`);
|
|
64
|
+
await this.unsubscribe();
|
|
65
|
+
this.receivingData = false;
|
|
66
|
+
this.subscribe(this.callback);
|
|
67
|
+
}
|
|
68
|
+
}, this.resubTimeoutMs);
|
|
28
69
|
}
|
|
29
70
|
}
|
|
30
71
|
exports.WebSocketLogProvider = WebSocketLogProvider;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { DriftClient } from '../driftClient';
|
|
3
3
|
import { UserAccount } from '../types';
|
|
4
|
+
import { Commitment } from '@solana/web3.js';
|
|
4
5
|
import { DLOB } from '../dlob/DLOB';
|
|
5
6
|
import { OrderSubscriberConfig, OrderSubscriberEvents } from './types';
|
|
6
7
|
import { PollingSubscription } from './PollingSubscription';
|
|
@@ -14,13 +15,16 @@ export declare class OrderSubscriber {
|
|
|
14
15
|
userAccount: UserAccount;
|
|
15
16
|
}>;
|
|
16
17
|
subscription: PollingSubscription | WebsocketSubscription;
|
|
18
|
+
commitment: Commitment;
|
|
17
19
|
eventEmitter: StrictEventEmitter<EventEmitter, OrderSubscriberEvents>;
|
|
18
20
|
fetchPromise?: Promise<void>;
|
|
19
21
|
fetchPromiseResolver: () => void;
|
|
22
|
+
mostRecentSlot: number;
|
|
20
23
|
constructor(config: OrderSubscriberConfig);
|
|
21
24
|
subscribe(): Promise<void>;
|
|
22
25
|
fetch(): Promise<void>;
|
|
23
|
-
tryUpdateUserAccount(key: string,
|
|
26
|
+
tryUpdateUserAccount(key: string, dataType: 'raw' | 'decoded', data: string[] | UserAccount, slot: number): void;
|
|
24
27
|
getDLOB(slot: number): Promise<DLOB>;
|
|
28
|
+
getSlot(): number;
|
|
25
29
|
unsubscribe(): Promise<void>;
|
|
26
30
|
}
|
|
@@ -12,6 +12,7 @@ class OrderSubscriber {
|
|
|
12
12
|
constructor(config) {
|
|
13
13
|
this.usersAccounts = new Map();
|
|
14
14
|
this.driftClient = config.driftClient;
|
|
15
|
+
this.commitment = config.subscriptionConfig.commitment || 'processed';
|
|
15
16
|
if (config.subscriptionConfig.type === 'polling') {
|
|
16
17
|
this.subscription = new PollingSubscription_1.PollingSubscription({
|
|
17
18
|
orderSubscriber: this,
|
|
@@ -21,6 +22,7 @@ class OrderSubscriber {
|
|
|
21
22
|
else {
|
|
22
23
|
this.subscription = new WebsocketSubscription_1.WebsocketSubscription({
|
|
23
24
|
orderSubscriber: this,
|
|
25
|
+
commitment: this.commitment,
|
|
24
26
|
skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
|
|
25
27
|
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
26
28
|
});
|
|
@@ -41,7 +43,7 @@ class OrderSubscriber {
|
|
|
41
43
|
const rpcRequestArgs = [
|
|
42
44
|
this.driftClient.program.programId.toBase58(),
|
|
43
45
|
{
|
|
44
|
-
commitment: this.
|
|
46
|
+
commitment: this.commitment,
|
|
45
47
|
filters: [(0, memcmp_1.getUserFilter)(), (0, memcmp_1.getUserWithOrderFilter)()],
|
|
46
48
|
encoding: 'base64',
|
|
47
49
|
withContext: true,
|
|
@@ -55,11 +57,8 @@ class OrderSubscriber {
|
|
|
55
57
|
const programAccountSet = new Set();
|
|
56
58
|
for (const programAccount of rpcResponseAndContext.value) {
|
|
57
59
|
const key = programAccount.pubkey.toString();
|
|
58
|
-
// @ts-ignore
|
|
59
|
-
const buffer = buffer_1.Buffer.from(programAccount.account.data[0], programAccount.account.data[1]);
|
|
60
60
|
programAccountSet.add(key);
|
|
61
|
-
|
|
62
|
-
this.tryUpdateUserAccount(key, userAccount, slot);
|
|
61
|
+
this.tryUpdateUserAccount(key, 'raw', programAccount.account.data, slot);
|
|
63
62
|
}
|
|
64
63
|
for (const key of this.usersAccounts.keys()) {
|
|
65
64
|
if (!programAccountSet.has(key)) {
|
|
@@ -75,9 +74,23 @@ class OrderSubscriber {
|
|
|
75
74
|
this.fetchPromise = undefined;
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
|
-
tryUpdateUserAccount(key,
|
|
77
|
+
tryUpdateUserAccount(key, dataType, data, slot) {
|
|
78
|
+
if (!this.mostRecentSlot || slot > this.mostRecentSlot) {
|
|
79
|
+
this.mostRecentSlot = slot;
|
|
80
|
+
}
|
|
79
81
|
const slotAndUserAccount = this.usersAccounts.get(key);
|
|
80
|
-
if (!slotAndUserAccount || slotAndUserAccount.slot
|
|
82
|
+
if (!slotAndUserAccount || slotAndUserAccount.slot <= slot) {
|
|
83
|
+
let userAccount;
|
|
84
|
+
// Polling leads to a lot of redundant decoding, so we only decode if data is from a fresh slot
|
|
85
|
+
if (dataType === 'raw') {
|
|
86
|
+
// @ts-ignore
|
|
87
|
+
const buffer = buffer_1.Buffer.from(data[0], data[1]);
|
|
88
|
+
userAccount =
|
|
89
|
+
this.driftClient.program.account.user.coder.accounts.decodeUnchecked('User', buffer);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
userAccount = data;
|
|
93
|
+
}
|
|
81
94
|
const newOrders = userAccount.orders.filter((order) => {
|
|
82
95
|
var _a;
|
|
83
96
|
return order.slot.toNumber() > ((_a = slotAndUserAccount === null || slotAndUserAccount === void 0 ? void 0 : slotAndUserAccount.slot) !== null && _a !== void 0 ? _a : 0) &&
|
|
@@ -104,6 +117,10 @@ class OrderSubscriber {
|
|
|
104
117
|
}
|
|
105
118
|
return dlob;
|
|
106
119
|
}
|
|
120
|
+
getSlot() {
|
|
121
|
+
var _a;
|
|
122
|
+
return (_a = this.mostRecentSlot) !== null && _a !== void 0 ? _a : 0;
|
|
123
|
+
}
|
|
107
124
|
async unsubscribe() {
|
|
108
125
|
await this.subscription.unsubscribe();
|
|
109
126
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { OrderSubscriber } from './OrderSubscriber';
|
|
2
|
+
import { Commitment } from '@solana/web3.js';
|
|
2
3
|
export declare class WebsocketSubscription {
|
|
3
4
|
private orderSubscriber;
|
|
5
|
+
private commitment;
|
|
4
6
|
private skipInitialLoad;
|
|
5
7
|
private resubTimeoutMs?;
|
|
6
8
|
private subscriber;
|
|
7
|
-
constructor({ orderSubscriber, skipInitialLoad, resubTimeoutMs, }: {
|
|
9
|
+
constructor({ orderSubscriber, commitment, skipInitialLoad, resubTimeoutMs, }: {
|
|
8
10
|
orderSubscriber: OrderSubscriber;
|
|
11
|
+
commitment: Commitment;
|
|
9
12
|
skipInitialLoad?: boolean;
|
|
10
13
|
resubTimeoutMs?: number;
|
|
11
14
|
});
|
|
@@ -4,8 +4,9 @@ exports.WebsocketSubscription = void 0;
|
|
|
4
4
|
const memcmp_1 = require("../memcmp");
|
|
5
5
|
const webSocketProgramAccountSubscriber_1 = require("../accounts/webSocketProgramAccountSubscriber");
|
|
6
6
|
class WebsocketSubscription {
|
|
7
|
-
constructor({ orderSubscriber, skipInitialLoad = false, resubTimeoutMs, }) {
|
|
7
|
+
constructor({ orderSubscriber, commitment, skipInitialLoad = false, resubTimeoutMs, }) {
|
|
8
8
|
this.orderSubscriber = orderSubscriber;
|
|
9
|
+
this.commitment = commitment;
|
|
9
10
|
this.skipInitialLoad = skipInitialLoad;
|
|
10
11
|
this.resubTimeoutMs = resubTimeoutMs;
|
|
11
12
|
}
|
|
@@ -13,12 +14,12 @@ class WebsocketSubscription {
|
|
|
13
14
|
if (!this.subscriber) {
|
|
14
15
|
this.subscriber = new webSocketProgramAccountSubscriber_1.WebSocketProgramAccountSubscriber('OrderSubscriber', 'User', this.orderSubscriber.driftClient.program, this.orderSubscriber.driftClient.program.account.user.coder.accounts.decode.bind(this.orderSubscriber.driftClient.program.account.user.coder.accounts), {
|
|
15
16
|
filters: [(0, memcmp_1.getUserFilter)(), (0, memcmp_1.getNonIdleUserFilter)()],
|
|
16
|
-
commitment: this.
|
|
17
|
+
commitment: this.commitment,
|
|
17
18
|
}, this.resubTimeoutMs);
|
|
18
19
|
}
|
|
19
20
|
await this.subscriber.subscribe((accountId, account, context) => {
|
|
20
21
|
const userKey = accountId.toBase58();
|
|
21
|
-
this.orderSubscriber.tryUpdateUserAccount(userKey, account, context.slot);
|
|
22
|
+
this.orderSubscriber.tryUpdateUserAccount(userKey, 'decoded', account, context.slot);
|
|
22
23
|
});
|
|
23
24
|
if (!this.skipInitialLoad) {
|
|
24
25
|
await this.orderSubscriber.fetch();
|
|
@@ -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
|
export type OrderSubscriberConfig = {
|
|
@@ -6,10 +6,12 @@ export type OrderSubscriberConfig = {
|
|
|
6
6
|
subscriptionConfig: {
|
|
7
7
|
type: 'polling';
|
|
8
8
|
frequency: number;
|
|
9
|
+
commitment?: Commitment;
|
|
9
10
|
} | {
|
|
10
11
|
type: 'websocket';
|
|
11
12
|
skipInitialLoad?: boolean;
|
|
12
13
|
resubTimeoutMs?: number;
|
|
14
|
+
commitment?: Commitment;
|
|
13
15
|
};
|
|
14
16
|
};
|
|
15
17
|
export interface OrderSubscriberEvents {
|
|
@@ -13,8 +13,10 @@ class SlotSubscriber {
|
|
|
13
13
|
}
|
|
14
14
|
this.currentSlot = await this.connection.getSlot('confirmed');
|
|
15
15
|
this.subscriptionId = this.connection.onSlotChange((slotInfo) => {
|
|
16
|
-
this.currentSlot
|
|
17
|
-
|
|
16
|
+
if (!this.currentSlot || this.currentSlot < slotInfo.slot) {
|
|
17
|
+
this.currentSlot = slotInfo.slot;
|
|
18
|
+
this.eventEmitter.emit('newSlot', slotInfo.slot);
|
|
19
|
+
}
|
|
18
20
|
});
|
|
19
21
|
}
|
|
20
22
|
getSlot() {
|
package/lib/user.js
CHANGED
|
@@ -22,7 +22,7 @@ class User {
|
|
|
22
22
|
this._isSubscribed = val;
|
|
23
23
|
}
|
|
24
24
|
constructor(config) {
|
|
25
|
-
var _a, _b, _c;
|
|
25
|
+
var _a, _b, _c, _d;
|
|
26
26
|
this._isSubscribed = false;
|
|
27
27
|
this.driftClient = config.driftClient;
|
|
28
28
|
this.userAccountPublicKey = config.userAccountPublicKey;
|
|
@@ -32,8 +32,8 @@ class User {
|
|
|
32
32
|
else if (((_b = config.accountSubscription) === null || _b === void 0 ? void 0 : _b.type) === 'custom') {
|
|
33
33
|
this.accountSubscriber = config.accountSubscription.userAccountSubscriber;
|
|
34
34
|
}
|
|
35
|
-
else
|
|
36
|
-
this.accountSubscriber = new webSocketUserAccountSubscriber_1.WebSocketUserAccountSubscriber(config.driftClient.program, config.userAccountPublicKey, config.accountSubscription.resubTimeoutMs, config.accountSubscription.commitment);
|
|
35
|
+
else {
|
|
36
|
+
this.accountSubscriber = new webSocketUserAccountSubscriber_1.WebSocketUserAccountSubscriber(config.driftClient.program, config.userAccountPublicKey, (_c = config.accountSubscription) === null || _c === void 0 ? void 0 : _c.resubTimeoutMs, (_d = config.accountSubscription) === null || _d === void 0 ? void 0 : _d.commitment);
|
|
37
37
|
}
|
|
38
38
|
this.eventEmitter = this.accountSubscriber.eventEmitter;
|
|
39
39
|
}
|
package/lib/userMap/userMap.d.ts
CHANGED
|
@@ -11,20 +11,31 @@ 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);
|
|
38
|
+
addSyncCallback(syncCallback?: (authorities: PublicKey[]) => Promise<void>, syncCallbackCriteria?: SyncCallbackCriteria): void;
|
|
28
39
|
subscribe(): Promise<void>;
|
|
29
40
|
addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount): Promise<void>;
|
|
30
41
|
has(key: string): boolean;
|
|
@@ -56,6 +67,7 @@ export declare class UserMap implements UserMapInterface {
|
|
|
56
67
|
updateWithEventRecord(record: WrappedEvent<any>): Promise<void>;
|
|
57
68
|
values(): IterableIterator<User>;
|
|
58
69
|
size(): number;
|
|
70
|
+
getUniqueAuthorities(useSyncCallbackCriteria?: boolean): PublicKey[];
|
|
59
71
|
sync(): Promise<void>;
|
|
60
72
|
unsubscribe(): Promise<void>;
|
|
61
73
|
}
|