@drift-labs/sdk 2.49.0-beta.0 → 2.49.0-beta.10
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/{mockUserAccountSubscriber.d.ts → basicUserAccountSubscriber.d.ts} +2 -2
- package/lib/accounts/{mockUserAccountSubscriber.js → basicUserAccountSubscriber.js} +9 -6
- package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.d.ts +29 -0
- package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +110 -0
- package/lib/accounts/types.d.ts +14 -1
- package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.d.ts +23 -0
- package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.js +65 -0
- package/lib/dlob/DLOB.d.ts +6 -2
- package/lib/dlob/DLOB.js +37 -12
- package/lib/driftClient.d.ts +66 -66
- package/lib/driftClient.js +208 -194
- package/lib/events/eventSubscriber.js +2 -1
- package/lib/events/sort.d.ts +2 -2
- package/lib/events/sort.js +6 -23
- package/lib/examples/loadDlob.js +10 -5
- package/lib/index.d.ts +3 -1
- package/lib/index.js +3 -1
- package/lib/math/auction.js +6 -1
- package/lib/orderSubscriber/OrderSubscriber.js +4 -0
- package/lib/orderSubscriber/WebsocketSubscription.d.ts +1 -1
- package/lib/orderSubscriber/WebsocketSubscription.js +8 -6
- package/lib/types.d.ts +0 -2
- package/lib/userMap/PollingSubscription.d.ts +15 -0
- package/lib/userMap/PollingSubscription.js +26 -0
- package/lib/userMap/WebsocketSubscription.d.ts +19 -0
- package/lib/userMap/WebsocketSubscription.js +40 -0
- package/lib/userMap/userMap.d.ts +15 -18
- package/lib/userMap/userMap.js +62 -31
- package/lib/userMap/userMapConfig.d.ts +20 -0
- package/lib/userMap/userMapConfig.js +2 -0
- package/package.json +1 -1
- package/src/accounts/{mockUserAccountSubscriber.ts → basicUserAccountSubscriber.ts} +8 -6
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +185 -0
- package/src/accounts/types.ts +21 -0
- package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +127 -0
- package/src/dlob/DLOB.ts +55 -15
- package/src/driftClient.ts +429 -272
- package/src/events/eventSubscriber.ts +2 -1
- package/src/events/sort.ts +7 -29
- package/src/examples/loadDlob.ts +11 -6
- package/src/index.ts +3 -1
- package/src/math/auction.ts +8 -1
- package/src/orderSubscriber/OrderSubscriber.ts +4 -0
- package/src/orderSubscriber/WebsocketSubscription.ts +19 -16
- package/src/types.ts +0 -2
- package/src/userMap/PollingSubscription.ts +46 -0
- package/src/userMap/WebsocketSubscription.ts +74 -0
- package/src/userMap/userMap.ts +88 -60
- package/src/userMap/userMapConfig.ts +31 -0
- package/tests/amm/test.ts +6 -3
- package/tests/dlob/helpers.ts +2 -6
- package/tests/dlob/test.ts +194 -0
|
@@ -23,7 +23,7 @@ class EventSubscriber {
|
|
|
23
23
|
this.txEventCache = new txEventCache_1.TxEventCache(this.options.maxTx);
|
|
24
24
|
this.eventListMap = new Map();
|
|
25
25
|
for (const eventType of this.options.eventTypes) {
|
|
26
|
-
this.eventListMap.set(eventType, new eventList_1.EventList(eventType, this.options.maxEventsPerType, (0, sort_1.getSortFn)(this.options.orderBy, this.options.orderDir
|
|
26
|
+
this.eventListMap.set(eventType, new eventList_1.EventList(eventType, this.options.maxEventsPerType, (0, sort_1.getSortFn)(this.options.orderBy, this.options.orderDir), this.options.orderDir));
|
|
27
27
|
}
|
|
28
28
|
this.eventEmitter = new events_1.EventEmitter();
|
|
29
29
|
if (this.options.logProviderConfig.type === 'websocket') {
|
|
@@ -89,6 +89,7 @@ class EventSubscriber {
|
|
|
89
89
|
}
|
|
90
90
|
if (!this.lastSeenSlot || slot > this.lastSeenSlot) {
|
|
91
91
|
this.lastSeenTxSig = txSig;
|
|
92
|
+
this.lastSeenSlot = slot;
|
|
92
93
|
}
|
|
93
94
|
if (this.lastSeenBlockTime === undefined ||
|
|
94
95
|
mostRecentBlockTime > this.lastSeenBlockTime) {
|
package/lib/events/sort.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { EventSubscriptionOrderBy, EventSubscriptionOrderDirection,
|
|
2
|
-
export declare function getSortFn(orderBy: EventSubscriptionOrderBy, orderDir: EventSubscriptionOrderDirection
|
|
1
|
+
import { EventSubscriptionOrderBy, EventSubscriptionOrderDirection, SortFn } from './types';
|
|
2
|
+
export declare function getSortFn(orderBy: EventSubscriptionOrderBy, orderDir: EventSubscriptionOrderDirection): SortFn;
|
package/lib/events/sort.js
CHANGED
|
@@ -1,41 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getSortFn = void 0;
|
|
4
|
-
const index_1 = require("../index");
|
|
5
4
|
function clientSortAscFn() {
|
|
6
5
|
return 'less than';
|
|
7
6
|
}
|
|
8
7
|
function clientSortDescFn() {
|
|
9
8
|
return 'greater than';
|
|
10
9
|
}
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
function orderActionRecordSortFn(currentEvent, newEvent) {
|
|
15
|
-
var _a, _b;
|
|
16
|
-
const currentEventMarketIndex = currentEvent.marketIndex;
|
|
17
|
-
const newEventMarketIndex = newEvent.marketIndex;
|
|
18
|
-
if (currentEventMarketIndex !== newEventMarketIndex) {
|
|
19
|
-
return currentEvent.ts.lte(newEvent.ts) ? 'less than' : 'greater than';
|
|
20
|
-
}
|
|
21
|
-
if (((_a = currentEvent.fillRecordId) === null || _a === void 0 ? void 0 : _a.gt(index_1.ZERO)) && ((_b = newEvent.fillRecordId) === null || _b === void 0 ? void 0 : _b.gt(index_1.ZERO))) {
|
|
22
|
-
return currentEvent.fillRecordId.lte(newEvent.fillRecordId)
|
|
10
|
+
function blockchainSortFn(currentEvent, newEvent) {
|
|
11
|
+
if (currentEvent.slot == newEvent.slot) {
|
|
12
|
+
return currentEvent.txSigIndex < newEvent.txSigIndex
|
|
23
13
|
? 'less than'
|
|
24
14
|
: 'greater than';
|
|
25
15
|
}
|
|
26
|
-
|
|
27
|
-
return currentEvent.ts.lte(newEvent.ts) ? 'less than' : 'greater than';
|
|
28
|
-
}
|
|
16
|
+
return currentEvent.slot < newEvent.slot ? 'less than' : 'greater than';
|
|
29
17
|
}
|
|
30
|
-
function getSortFn(orderBy, orderDir
|
|
18
|
+
function getSortFn(orderBy, orderDir) {
|
|
31
19
|
if (orderBy === 'client') {
|
|
32
20
|
return orderDir === 'asc' ? clientSortAscFn : clientSortDescFn;
|
|
33
21
|
}
|
|
34
|
-
|
|
35
|
-
case 'OrderActionRecord':
|
|
36
|
-
return orderActionRecordSortFn;
|
|
37
|
-
default:
|
|
38
|
-
return defaultBlockchainSortFn;
|
|
39
|
-
}
|
|
22
|
+
return blockchainSortFn;
|
|
40
23
|
}
|
|
41
24
|
exports.getSortFn = getSortFn;
|
package/lib/examples/loadDlob.js
CHANGED
|
@@ -35,15 +35,20 @@ const main = async () => {
|
|
|
35
35
|
console.log('Subscribing drift client...');
|
|
36
36
|
await driftClient.subscribe();
|
|
37
37
|
console.log('Loading user map...');
|
|
38
|
-
const userMap = new __1.UserMap(
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
const userMap = new __1.UserMap({
|
|
39
|
+
driftClient,
|
|
40
|
+
subscriptionConfig: {
|
|
41
|
+
type: 'websocket',
|
|
42
|
+
commitment: 'processed',
|
|
43
|
+
},
|
|
44
|
+
skipInitialLoad: false,
|
|
45
|
+
includeIdle: false,
|
|
41
46
|
});
|
|
42
47
|
// fetches all users and subscribes for updates
|
|
43
48
|
await userMap.subscribe();
|
|
44
49
|
console.log('Loading dlob from user map...');
|
|
45
|
-
const
|
|
46
|
-
await
|
|
50
|
+
const slot = await driftClient.connection.getSlot();
|
|
51
|
+
const dlob = await userMap.getDLOB(slot);
|
|
47
52
|
console.log('number of orders', dlob.getDLOBOrders().length);
|
|
48
53
|
dlob.clear();
|
|
49
54
|
console.log('Unsubscribing users...');
|
package/lib/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './types';
|
|
|
9
9
|
export * from './constants/perpMarkets';
|
|
10
10
|
export * from './accounts/fetch';
|
|
11
11
|
export * from './accounts/webSocketDriftClientAccountSubscriber';
|
|
12
|
+
export * from './accounts/webSocketInsuranceFundStakeAccountSubscriber';
|
|
12
13
|
export * from './accounts/bulkAccountLoader';
|
|
13
14
|
export * from './accounts/bulkUserSubscription';
|
|
14
15
|
export * from './accounts/bulkUserStatsSubscription';
|
|
@@ -17,7 +18,8 @@ export * from './accounts/pollingOracleAccountSubscriber';
|
|
|
17
18
|
export * from './accounts/pollingTokenAccountSubscriber';
|
|
18
19
|
export * from './accounts/pollingUserAccountSubscriber';
|
|
19
20
|
export * from './accounts/pollingUserStatsAccountSubscriber';
|
|
20
|
-
export * from './accounts/
|
|
21
|
+
export * from './accounts/pollingInsuranceFundStakeAccountSubscriber';
|
|
22
|
+
export * from './accounts/basicUserAccountSubscriber';
|
|
21
23
|
export * from './accounts/types';
|
|
22
24
|
export * from './addresses/pda';
|
|
23
25
|
export * from './adminClient';
|
package/lib/index.js
CHANGED
|
@@ -32,6 +32,7 @@ __exportStar(require("./types"), exports);
|
|
|
32
32
|
__exportStar(require("./constants/perpMarkets"), exports);
|
|
33
33
|
__exportStar(require("./accounts/fetch"), exports);
|
|
34
34
|
__exportStar(require("./accounts/webSocketDriftClientAccountSubscriber"), exports);
|
|
35
|
+
__exportStar(require("./accounts/webSocketInsuranceFundStakeAccountSubscriber"), exports);
|
|
35
36
|
__exportStar(require("./accounts/bulkAccountLoader"), exports);
|
|
36
37
|
__exportStar(require("./accounts/bulkUserSubscription"), exports);
|
|
37
38
|
__exportStar(require("./accounts/bulkUserStatsSubscription"), exports);
|
|
@@ -40,7 +41,8 @@ __exportStar(require("./accounts/pollingOracleAccountSubscriber"), exports);
|
|
|
40
41
|
__exportStar(require("./accounts/pollingTokenAccountSubscriber"), exports);
|
|
41
42
|
__exportStar(require("./accounts/pollingUserAccountSubscriber"), exports);
|
|
42
43
|
__exportStar(require("./accounts/pollingUserStatsAccountSubscriber"), exports);
|
|
43
|
-
__exportStar(require("./accounts/
|
|
44
|
+
__exportStar(require("./accounts/pollingInsuranceFundStakeAccountSubscriber"), exports);
|
|
45
|
+
__exportStar(require("./accounts/basicUserAccountSubscriber"), exports);
|
|
44
46
|
__exportStar(require("./accounts/types"), exports);
|
|
45
47
|
__exportStar(require("./addresses/pda"), exports);
|
|
46
48
|
__exportStar(require("./adminClient"), exports);
|
package/lib/math/auction.js
CHANGED
|
@@ -18,7 +18,12 @@ function isFallbackAvailableLiquiditySource(order, minAuctionDuration, slot) {
|
|
|
18
18
|
}
|
|
19
19
|
exports.isFallbackAvailableLiquiditySource = isFallbackAvailableLiquiditySource;
|
|
20
20
|
function getAuctionPrice(order, slot, oraclePrice) {
|
|
21
|
-
if ((0, types_1.isOneOfVariant)(order.orderType, [
|
|
21
|
+
if ((0, types_1.isOneOfVariant)(order.orderType, [
|
|
22
|
+
'market',
|
|
23
|
+
'triggerMarket',
|
|
24
|
+
'limit',
|
|
25
|
+
'triggerLimit',
|
|
26
|
+
])) {
|
|
22
27
|
return getAuctionPriceForFixedAuction(order, slot);
|
|
23
28
|
}
|
|
24
29
|
else if ((0, types_1.isVariant)(order.orderType, 'oracle')) {
|
|
@@ -60,11 +60,15 @@ class OrderSubscriber {
|
|
|
60
60
|
const key = programAccount.pubkey.toString();
|
|
61
61
|
programAccountSet.add(key);
|
|
62
62
|
this.tryUpdateUserAccount(key, 'raw', programAccount.account.data, slot);
|
|
63
|
+
// give event loop a chance to breathe
|
|
64
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
63
65
|
}
|
|
64
66
|
for (const key of this.usersAccounts.keys()) {
|
|
65
67
|
if (!programAccountSet.has(key)) {
|
|
66
68
|
this.usersAccounts.delete(key);
|
|
67
69
|
}
|
|
70
|
+
// give event loop a chance to breathe
|
|
71
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
catch (e) {
|
|
@@ -5,7 +5,7 @@ export declare class WebsocketSubscription {
|
|
|
5
5
|
private commitment;
|
|
6
6
|
private skipInitialLoad;
|
|
7
7
|
private resubTimeoutMs?;
|
|
8
|
-
private subscriber
|
|
8
|
+
private subscriber?;
|
|
9
9
|
constructor({ orderSubscriber, commitment, skipInitialLoad, resubTimeoutMs, }: {
|
|
10
10
|
orderSubscriber: OrderSubscriber;
|
|
11
11
|
commitment: Commitment;
|
|
@@ -11,12 +11,13 @@ class WebsocketSubscription {
|
|
|
11
11
|
this.resubTimeoutMs = resubTimeoutMs;
|
|
12
12
|
}
|
|
13
13
|
async subscribe() {
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
filters: [(0, memcmp_1.getUserFilter)(), (0, memcmp_1.getNonIdleUserFilter)()],
|
|
17
|
-
commitment: this.commitment,
|
|
18
|
-
}, this.resubTimeoutMs);
|
|
14
|
+
if (this.subscriber) {
|
|
15
|
+
return;
|
|
19
16
|
}
|
|
17
|
+
this.subscriber = new webSocketProgramAccountSubscriber_1.WebSocketProgramAccountSubscriber('OrderSubscriber', 'User', this.orderSubscriber.driftClient.program, this.orderSubscriber.driftClient.program.account.user.coder.accounts.decodeUnchecked.bind(this.orderSubscriber.driftClient.program.account.user.coder.accounts), {
|
|
18
|
+
filters: [(0, memcmp_1.getUserFilter)(), (0, memcmp_1.getNonIdleUserFilter)()],
|
|
19
|
+
commitment: this.commitment,
|
|
20
|
+
}, this.resubTimeoutMs);
|
|
20
21
|
await this.subscriber.subscribe((accountId, account, context) => {
|
|
21
22
|
const userKey = accountId.toBase58();
|
|
22
23
|
this.orderSubscriber.tryUpdateUserAccount(userKey, 'decoded', account, context.slot);
|
|
@@ -28,7 +29,8 @@ class WebsocketSubscription {
|
|
|
28
29
|
async unsubscribe() {
|
|
29
30
|
if (!this.subscriber)
|
|
30
31
|
return;
|
|
31
|
-
this.subscriber.unsubscribe();
|
|
32
|
+
await this.subscriber.unsubscribe();
|
|
33
|
+
this.subscriber = undefined;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
exports.WebsocketSubscription = WebsocketSubscription;
|
package/lib/types.d.ts
CHANGED
|
@@ -1025,8 +1025,6 @@ export interface IVersionedWallet {
|
|
|
1025
1025
|
}
|
|
1026
1026
|
export type FeeStructure = {
|
|
1027
1027
|
feeTiers: FeeTier[];
|
|
1028
|
-
makerRebateNumerator: BN;
|
|
1029
|
-
makerRebateDenominator: BN;
|
|
1030
1028
|
fillerRewardStructure: OrderFillerRewardStructure;
|
|
1031
1029
|
flatFillerFee: BN;
|
|
1032
1030
|
referrerRewardEpochUpperBound: BN;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { UserMap } from './userMap';
|
|
2
|
+
export declare class PollingSubscription {
|
|
3
|
+
private userMap;
|
|
4
|
+
private frequency;
|
|
5
|
+
private skipInitialLoad;
|
|
6
|
+
intervalId?: ReturnType<typeof setTimeout>;
|
|
7
|
+
constructor({ userMap, frequency, skipInitialLoad, }: {
|
|
8
|
+
userMap: UserMap;
|
|
9
|
+
frequency: number;
|
|
10
|
+
skipInitialLoad?: boolean;
|
|
11
|
+
includeIdle?: boolean;
|
|
12
|
+
});
|
|
13
|
+
subscribe(): Promise<void>;
|
|
14
|
+
unsubscribe(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PollingSubscription = void 0;
|
|
4
|
+
class PollingSubscription {
|
|
5
|
+
constructor({ userMap, frequency, skipInitialLoad = false, }) {
|
|
6
|
+
this.userMap = userMap;
|
|
7
|
+
this.frequency = frequency;
|
|
8
|
+
this.skipInitialLoad = skipInitialLoad;
|
|
9
|
+
}
|
|
10
|
+
async subscribe() {
|
|
11
|
+
if (this.intervalId) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
this.intervalId = setInterval(this.userMap.sync.bind(this.userMap), this.frequency);
|
|
15
|
+
if (!this.skipInitialLoad) {
|
|
16
|
+
await this.userMap.sync();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async unsubscribe() {
|
|
20
|
+
if (this.intervalId) {
|
|
21
|
+
clearInterval(this.intervalId);
|
|
22
|
+
this.intervalId = undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
exports.PollingSubscription = PollingSubscription;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { UserMap } from './userMap';
|
|
2
|
+
import { Commitment } from '@solana/web3.js';
|
|
3
|
+
export declare class WebsocketSubscription {
|
|
4
|
+
private userMap;
|
|
5
|
+
private commitment;
|
|
6
|
+
private skipInitialLoad;
|
|
7
|
+
private resubTimeoutMs?;
|
|
8
|
+
private includeIdle?;
|
|
9
|
+
private subscriber;
|
|
10
|
+
constructor({ userMap, commitment, skipInitialLoad, resubTimeoutMs, includeIdle, }: {
|
|
11
|
+
userMap: UserMap;
|
|
12
|
+
commitment: Commitment;
|
|
13
|
+
skipInitialLoad?: boolean;
|
|
14
|
+
resubTimeoutMs?: number;
|
|
15
|
+
includeIdle?: boolean;
|
|
16
|
+
});
|
|
17
|
+
subscribe(): Promise<void>;
|
|
18
|
+
unsubscribe(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebsocketSubscription = void 0;
|
|
4
|
+
const memcmp_1 = require("../memcmp");
|
|
5
|
+
const webSocketProgramAccountSubscriber_1 = require("../accounts/webSocketProgramAccountSubscriber");
|
|
6
|
+
class WebsocketSubscription {
|
|
7
|
+
constructor({ userMap, commitment, skipInitialLoad = false, resubTimeoutMs, includeIdle = false, }) {
|
|
8
|
+
this.userMap = userMap;
|
|
9
|
+
this.commitment = commitment;
|
|
10
|
+
this.skipInitialLoad = skipInitialLoad;
|
|
11
|
+
this.resubTimeoutMs = resubTimeoutMs;
|
|
12
|
+
this.includeIdle = includeIdle || false;
|
|
13
|
+
}
|
|
14
|
+
async subscribe() {
|
|
15
|
+
if (!this.subscriber) {
|
|
16
|
+
const filters = [(0, memcmp_1.getUserFilter)()];
|
|
17
|
+
if (!this.includeIdle) {
|
|
18
|
+
filters.push((0, memcmp_1.getNonIdleUserFilter)());
|
|
19
|
+
}
|
|
20
|
+
this.subscriber = new webSocketProgramAccountSubscriber_1.WebSocketProgramAccountSubscriber('UserMap', 'User', this.userMap.driftClient.program, this.userMap.driftClient.program.account.user.coder.accounts.decodeUnchecked.bind(this.userMap.driftClient.program.account.user.coder.accounts), {
|
|
21
|
+
filters,
|
|
22
|
+
commitment: this.commitment,
|
|
23
|
+
}, this.resubTimeoutMs);
|
|
24
|
+
}
|
|
25
|
+
await this.subscriber.subscribe((accountId, account, context) => {
|
|
26
|
+
const userKey = accountId.toBase58();
|
|
27
|
+
this.userMap.updateUserAccount(userKey, account, context.slot);
|
|
28
|
+
});
|
|
29
|
+
if (!this.skipInitialLoad) {
|
|
30
|
+
await this.userMap.sync();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async unsubscribe() {
|
|
34
|
+
if (!this.subscriber)
|
|
35
|
+
return;
|
|
36
|
+
await this.subscriber.unsubscribe();
|
|
37
|
+
this.subscriber = undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.WebsocketSubscription = WebsocketSubscription;
|
package/lib/userMap/userMap.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { User, DriftClient, UserAccount, OrderRecord,
|
|
1
|
+
import { User, DriftClient, UserAccount, OrderRecord, WrappedEvent, DLOB } from '..';
|
|
2
2
|
import { PublicKey } from '@solana/web3.js';
|
|
3
|
+
import { UserAccountFilterCriteria as UserFilterCriteria, UserMapConfig } from './userMapConfig';
|
|
3
4
|
export interface UserMapInterface {
|
|
4
5
|
subscribe(): Promise<void>;
|
|
5
6
|
unsubscribe(): Promise<void>;
|
|
@@ -11,33 +12,23 @@ export interface UserMapInterface {
|
|
|
11
12
|
updateWithOrderRecord(record: OrderRecord): Promise<void>;
|
|
12
13
|
values(): IterableIterator<User>;
|
|
13
14
|
}
|
|
14
|
-
export type SyncCallbackCriteria = {
|
|
15
|
-
hasOpenOrders: boolean;
|
|
16
|
-
};
|
|
17
15
|
export declare class UserMap implements UserMapInterface {
|
|
18
16
|
private userMap;
|
|
19
|
-
|
|
20
|
-
private
|
|
17
|
+
driftClient: DriftClient;
|
|
18
|
+
private connection;
|
|
19
|
+
private commitment;
|
|
21
20
|
private includeIdle;
|
|
22
21
|
private lastNumberOfSubAccounts;
|
|
22
|
+
private subscription;
|
|
23
23
|
private stateAccountUpdateCallback;
|
|
24
|
-
private syncCallback;
|
|
25
|
-
private syncCallbackCriteria;
|
|
26
24
|
private syncPromise?;
|
|
27
25
|
private syncPromiseResolver;
|
|
28
26
|
/**
|
|
29
27
|
* Constructs a new UserMap instance.
|
|
30
|
-
*
|
|
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
|
|
36
28
|
*/
|
|
37
|
-
constructor(
|
|
38
|
-
addSyncCallback(syncCallback?: (authorities: PublicKey[]) => Promise<void>, syncCallbackCriteria?: SyncCallbackCriteria): void;
|
|
29
|
+
constructor(config: UserMapConfig);
|
|
39
30
|
subscribe(): Promise<void>;
|
|
40
|
-
addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount): Promise<void>;
|
|
31
|
+
addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount, slot?: number): Promise<void>;
|
|
41
32
|
has(key: string): boolean;
|
|
42
33
|
/**
|
|
43
34
|
* gets the User for a particular userAccountPublicKey, if no User exists, undefined is returned
|
|
@@ -67,7 +58,13 @@ export declare class UserMap implements UserMapInterface {
|
|
|
67
58
|
updateWithEventRecord(record: WrappedEvent<any>): Promise<void>;
|
|
68
59
|
values(): IterableIterator<User>;
|
|
69
60
|
size(): number;
|
|
70
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
|
|
63
|
+
* @param filterCriteria: Users must meet these criteria to be included
|
|
64
|
+
* @returns
|
|
65
|
+
*/
|
|
66
|
+
getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[];
|
|
71
67
|
sync(): Promise<void>;
|
|
72
68
|
unsubscribe(): Promise<void>;
|
|
69
|
+
updateUserAccount(key: string, userAccount: UserAccount, slot: number): Promise<void>;
|
|
73
70
|
}
|
package/lib/userMap/userMap.js
CHANGED
|
@@ -5,33 +5,46 @@ const __1 = require("..");
|
|
|
5
5
|
const web3_js_1 = require("@solana/web3.js");
|
|
6
6
|
const buffer_1 = require("buffer");
|
|
7
7
|
const memcmp_1 = require("../memcmp");
|
|
8
|
+
const WebsocketSubscription_1 = require("./WebsocketSubscription");
|
|
9
|
+
const PollingSubscription_1 = require("./PollingSubscription");
|
|
8
10
|
class UserMap {
|
|
9
11
|
/**
|
|
10
12
|
* Constructs a new UserMap instance.
|
|
11
|
-
*
|
|
12
|
-
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
13
|
-
* @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
|
|
14
|
-
* @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
|
|
15
|
-
* @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
|
|
16
|
-
* @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
|
|
17
13
|
*/
|
|
18
|
-
constructor(
|
|
14
|
+
constructor(config) {
|
|
15
|
+
var _a, _b;
|
|
19
16
|
this.userMap = new Map();
|
|
20
17
|
this.stateAccountUpdateCallback = async (state) => {
|
|
21
|
-
if (state.numberOfSubAccounts
|
|
18
|
+
if (!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)) {
|
|
22
19
|
await this.sync();
|
|
23
20
|
this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
|
|
24
21
|
}
|
|
25
22
|
};
|
|
26
|
-
this.driftClient = driftClient;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
23
|
+
this.driftClient = config.driftClient;
|
|
24
|
+
if (config.connection) {
|
|
25
|
+
this.connection = config.connection;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.connection = this.driftClient.connection;
|
|
29
|
+
}
|
|
30
|
+
this.commitment =
|
|
31
|
+
(_a = config.subscriptionConfig.commitment) !== null && _a !== void 0 ? _a : this.driftClient.opts.commitment;
|
|
32
|
+
this.includeIdle = (_b = config.includeIdle) !== null && _b !== void 0 ? _b : false;
|
|
33
|
+
if (config.subscriptionConfig.type === 'polling') {
|
|
34
|
+
this.subscription = new PollingSubscription_1.PollingSubscription({
|
|
35
|
+
userMap: this,
|
|
36
|
+
frequency: config.subscriptionConfig.frequency,
|
|
37
|
+
skipInitialLoad: config.skipInitialLoad,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.subscription = new WebsocketSubscription_1.WebsocketSubscription({
|
|
42
|
+
userMap: this,
|
|
43
|
+
commitment: this.commitment,
|
|
44
|
+
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
45
|
+
skipInitialLoad: config.skipInitialLoad,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
35
48
|
}
|
|
36
49
|
async subscribe() {
|
|
37
50
|
if (this.size() > 0) {
|
|
@@ -41,13 +54,16 @@ class UserMap {
|
|
|
41
54
|
this.lastNumberOfSubAccounts =
|
|
42
55
|
this.driftClient.getStateAccount().numberOfSubAccounts;
|
|
43
56
|
this.driftClient.eventEmitter.on('stateAccountUpdate', this.stateAccountUpdateCallback);
|
|
44
|
-
await this.
|
|
57
|
+
await this.subscription.subscribe();
|
|
45
58
|
}
|
|
46
|
-
async addPubkey(userAccountPublicKey, userAccount) {
|
|
59
|
+
async addPubkey(userAccountPublicKey, userAccount, slot) {
|
|
47
60
|
const user = new __1.User({
|
|
48
61
|
driftClient: this.driftClient,
|
|
49
62
|
userAccountPublicKey,
|
|
50
|
-
accountSubscription:
|
|
63
|
+
accountSubscription: {
|
|
64
|
+
type: 'custom',
|
|
65
|
+
userAccountSubscriber: new __1.BasicUserAccountSubscriber(userAccountPublicKey, userAccount, slot),
|
|
66
|
+
},
|
|
51
67
|
});
|
|
52
68
|
await user.subscribe(userAccount);
|
|
53
69
|
this.userMap.set(userAccountPublicKey.toString(), user);
|
|
@@ -148,11 +164,15 @@ class UserMap {
|
|
|
148
164
|
size() {
|
|
149
165
|
return this.userMap.size;
|
|
150
166
|
}
|
|
151
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
|
|
169
|
+
* @param filterCriteria: Users must meet these criteria to be included
|
|
170
|
+
* @returns
|
|
171
|
+
*/
|
|
172
|
+
getUniqueAuthorities(filterCriteria) {
|
|
152
173
|
const usersMeetingCriteria = Array.from(this.userMap.values()).filter((user) => {
|
|
153
174
|
let pass = true;
|
|
154
|
-
if (
|
|
155
|
-
this.syncCallbackCriteria.hasOpenOrders) {
|
|
175
|
+
if (filterCriteria && filterCriteria.hasOpenOrders) {
|
|
156
176
|
pass = pass && user.getUserAccount().hasOpenOrder;
|
|
157
177
|
}
|
|
158
178
|
return pass;
|
|
@@ -176,7 +196,7 @@ class UserMap {
|
|
|
176
196
|
const rpcRequestArgs = [
|
|
177
197
|
this.driftClient.program.programId.toBase58(),
|
|
178
198
|
{
|
|
179
|
-
commitment: this.
|
|
199
|
+
commitment: this.commitment,
|
|
180
200
|
filters,
|
|
181
201
|
encoding: 'base64',
|
|
182
202
|
withContext: true,
|
|
@@ -184,7 +204,7 @@ class UserMap {
|
|
|
184
204
|
];
|
|
185
205
|
const rpcJSONResponse =
|
|
186
206
|
// @ts-ignore
|
|
187
|
-
await this.
|
|
207
|
+
await this.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
188
208
|
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
189
209
|
const slot = rpcResponseAndContext.context.slot;
|
|
190
210
|
const programAccountBufferMap = new Map();
|
|
@@ -195,9 +215,11 @@ class UserMap {
|
|
|
195
215
|
}
|
|
196
216
|
for (const [key, buffer] of programAccountBufferMap.entries()) {
|
|
197
217
|
if (!this.has(key)) {
|
|
198
|
-
const userAccount = this.driftClient.program.account.user.coder.accounts.
|
|
218
|
+
const userAccount = this.driftClient.program.account.user.coder.accounts.decodeUnchecked('User', buffer);
|
|
199
219
|
await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
|
|
200
220
|
}
|
|
221
|
+
// give event loop a chance to breathe
|
|
222
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
201
223
|
}
|
|
202
224
|
for (const [key, user] of this.userMap.entries()) {
|
|
203
225
|
if (!programAccountBufferMap.has(key)) {
|
|
@@ -205,16 +227,15 @@ class UserMap {
|
|
|
205
227
|
this.userMap.delete(key);
|
|
206
228
|
}
|
|
207
229
|
else {
|
|
208
|
-
const userAccount = this.driftClient.program.account.user.coder.accounts.
|
|
230
|
+
const userAccount = this.driftClient.program.account.user.coder.accounts.decodeUnchecked('User', programAccountBufferMap.get(key));
|
|
209
231
|
user.accountSubscriber.updateData(userAccount, slot);
|
|
210
232
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
await this.syncCallback(this.getUniqueAuthorities());
|
|
233
|
+
// give event loop a chance to breathe
|
|
234
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
214
235
|
}
|
|
215
236
|
}
|
|
216
237
|
catch (e) {
|
|
217
|
-
console.error(`Error in UserMap.sync()
|
|
238
|
+
console.error(`Error in UserMap.sync():`);
|
|
218
239
|
console.error(e);
|
|
219
240
|
}
|
|
220
241
|
finally {
|
|
@@ -223,6 +244,7 @@ class UserMap {
|
|
|
223
244
|
}
|
|
224
245
|
}
|
|
225
246
|
async unsubscribe() {
|
|
247
|
+
await this.subscription.unsubscribe();
|
|
226
248
|
for (const [key, user] of this.userMap.entries()) {
|
|
227
249
|
await user.unsubscribe();
|
|
228
250
|
this.userMap.delete(key);
|
|
@@ -232,5 +254,14 @@ class UserMap {
|
|
|
232
254
|
this.lastNumberOfSubAccounts = undefined;
|
|
233
255
|
}
|
|
234
256
|
}
|
|
257
|
+
async updateUserAccount(key, userAccount, slot) {
|
|
258
|
+
if (!this.userMap.has(key)) {
|
|
259
|
+
this.addPubkey(new web3_js_1.PublicKey(key), userAccount, slot);
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
const user = this.userMap.get(key);
|
|
263
|
+
user.accountSubscriber.updateData(userAccount, slot);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
235
266
|
}
|
|
236
267
|
exports.UserMap = UserMap;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Commitment, Connection } from '@solana/web3.js';
|
|
2
|
+
import { DriftClient } from '../driftClient';
|
|
3
|
+
export type UserAccountFilterCriteria = {
|
|
4
|
+
hasOpenOrders: boolean;
|
|
5
|
+
};
|
|
6
|
+
export type UserMapConfig = {
|
|
7
|
+
driftClient: DriftClient;
|
|
8
|
+
connection?: Connection;
|
|
9
|
+
subscriptionConfig: {
|
|
10
|
+
type: 'polling';
|
|
11
|
+
frequency: number;
|
|
12
|
+
commitment?: Commitment;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'websocket';
|
|
15
|
+
resubTimeoutMs?: number;
|
|
16
|
+
commitment?: Commitment;
|
|
17
|
+
};
|
|
18
|
+
skipInitialLoad?: boolean;
|
|
19
|
+
includeIdle?: boolean;
|
|
20
|
+
};
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import StrictEventEmitter from 'strict-event-emitter-types';
|
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
5
|
import { UserAccount } from '../types';
|
|
6
6
|
|
|
7
|
-
export class
|
|
7
|
+
export class BasicUserAccountSubscriber implements UserAccountSubscriber {
|
|
8
8
|
isSubscribed: boolean;
|
|
9
9
|
eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
|
|
10
10
|
userAccountPublicKey: PublicKey;
|
|
@@ -16,8 +16,8 @@ export class MockUserAccountSubscriber implements UserAccountSubscriber {
|
|
|
16
16
|
|
|
17
17
|
public constructor(
|
|
18
18
|
userAccountPublicKey: PublicKey,
|
|
19
|
-
data
|
|
20
|
-
slot
|
|
19
|
+
data?: UserAccount,
|
|
20
|
+
slot?: number
|
|
21
21
|
) {
|
|
22
22
|
this.isSubscribed = true;
|
|
23
23
|
this.eventEmitter = new EventEmitter();
|
|
@@ -46,8 +46,10 @@ export class MockUserAccountSubscriber implements UserAccountSubscriber {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
public updateData(userAccount: UserAccount, slot: number): void {
|
|
49
|
-
this.user
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
if (!this.user || slot >= (this.user.slot ?? 0)) {
|
|
50
|
+
this.user = { data: userAccount, slot };
|
|
51
|
+
this.eventEmitter.emit('userAccountUpdate', userAccount);
|
|
52
|
+
this.eventEmitter.emit('update');
|
|
53
|
+
}
|
|
52
54
|
}
|
|
53
55
|
}
|