@drift-labs/sdk 2.49.0-beta.9 → 2.52.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/VERSION +1 -1
- package/lib/accounts/{mockUserAccountSubscriber.d.ts → basicUserAccountSubscriber.d.ts} +6 -2
- package/lib/accounts/{mockUserAccountSubscriber.js → basicUserAccountSubscriber.js} +13 -6
- package/lib/accounts/bulkAccountLoader.js +7 -1
- package/lib/accounts/oneShotUserAccountSubscriber.d.ts +17 -0
- package/lib/accounts/oneShotUserAccountSubscriber.js +48 -0
- package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +1 -0
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +17 -0
- package/lib/constants/numericConstants.d.ts +3 -0
- package/lib/constants/numericConstants.js +4 -1
- package/lib/constants/perpMarkets.js +40 -0
- package/lib/constants/spotMarkets.js +10 -0
- package/lib/decode/user.d.ts +3 -0
- package/lib/decode/user.js +329 -0
- package/lib/driftClient.d.ts +6 -1
- package/lib/driftClient.js +35 -10
- package/lib/examples/loadDlob.js +10 -5
- package/lib/idl/drift.json +32 -2
- package/lib/index.d.ts +3 -1
- package/lib/index.js +3 -1
- package/lib/math/state.d.ts +5 -0
- package/lib/math/state.js +27 -0
- package/lib/math/superStake.d.ts +43 -0
- package/lib/math/superStake.js +64 -22
- package/lib/orderSubscriber/OrderSubscriber.d.ts +3 -0
- package/lib/orderSubscriber/OrderSubscriber.js +17 -3
- package/lib/orderSubscriber/WebsocketSubscription.d.ts +1 -1
- package/lib/orderSubscriber/WebsocketSubscription.js +8 -6
- package/lib/orderSubscriber/types.d.ts +4 -1
- package/lib/types.d.ts +2 -1
- package/lib/user.d.ts +2 -1
- package/lib/user.js +13 -5
- package/lib/userMap/PollingSubscription.d.ts +15 -0
- package/lib/userMap/PollingSubscription.js +28 -0
- package/lib/userMap/WebsocketSubscription.d.ts +23 -0
- package/lib/userMap/WebsocketSubscription.js +41 -0
- package/lib/userMap/userMap.d.ts +16 -18
- package/lib/userMap/userMap.js +73 -34
- package/lib/userMap/userMapConfig.d.ts +21 -0
- package/lib/userMap/userMapConfig.js +2 -0
- package/lib/userStats.d.ts +1 -0
- package/lib/userStats.js +3 -0
- package/package.json +2 -1
- package/src/accounts/{mockUserAccountSubscriber.ts → basicUserAccountSubscriber.ts} +12 -6
- package/src/accounts/bulkAccountLoader.ts +10 -3
- package/src/accounts/oneShotUserAccountSubscriber.ts +64 -0
- package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +1 -0
- package/src/adminClient.ts +27 -0
- package/src/constants/numericConstants.ts +4 -0
- package/src/constants/perpMarkets.ts +40 -0
- package/src/constants/spotMarkets.ts +10 -0
- package/src/decode/user.ts +358 -0
- package/src/driftClient.ts +62 -15
- package/src/examples/loadDlob.ts +11 -6
- package/src/idl/drift.json +33 -3
- package/src/index.ts +3 -1
- package/src/math/state.ts +26 -0
- package/src/math/superStake.ts +108 -20
- package/src/orderSubscriber/OrderSubscriber.ts +33 -7
- package/src/orderSubscriber/WebsocketSubscription.ts +17 -16
- package/src/orderSubscriber/types.ts +15 -2
- package/src/types.ts +2 -1
- package/src/user.ts +19 -6
- package/src/userMap/PollingSubscription.ts +48 -0
- package/src/userMap/WebsocketSubscription.ts +76 -0
- package/src/userMap/userMap.ts +105 -70
- package/src/userMap/userMapConfig.ts +34 -0
- package/src/userStats.ts +8 -0
- package/tests/amm/test.ts +6 -3
- package/tests/decode/test.ts +266 -0
- package/tests/decode/userAccountBufferStrings.ts +102 -0
- package/tests/dlob/helpers.ts +1 -0
- package/tests/dlob/test.ts +10 -8
- package/tests/user/helpers.ts +0 -1
package/lib/user.js
CHANGED
|
@@ -251,7 +251,7 @@ class User {
|
|
|
251
251
|
* @returns : the dust base asset amount (ie, < stepsize)
|
|
252
252
|
* @returns : pnl from settle
|
|
253
253
|
*/
|
|
254
|
-
getPerpPositionWithLPSettle(marketIndex, originalPosition, burnLpShares = false) {
|
|
254
|
+
getPerpPositionWithLPSettle(marketIndex, originalPosition, burnLpShares = false, includeRemainderInBaseAmount = false) {
|
|
255
255
|
var _a;
|
|
256
256
|
originalPosition =
|
|
257
257
|
(_a = originalPosition !== null && originalPosition !== void 0 ? originalPosition : this.getPerpPosition(marketIndex)) !== null && _a !== void 0 ? _a : this.getEmptyPosition(marketIndex);
|
|
@@ -396,7 +396,12 @@ class User {
|
|
|
396
396
|
else {
|
|
397
397
|
position.lastCumulativeFundingRate = numericConstants_1.ZERO;
|
|
398
398
|
}
|
|
399
|
-
|
|
399
|
+
const remainderBeforeRemoval = new _1.BN(position.remainderBaseAssetAmount);
|
|
400
|
+
if (includeRemainderInBaseAmount) {
|
|
401
|
+
position.baseAssetAmount = position.baseAssetAmount.add(remainderBeforeRemoval);
|
|
402
|
+
position.remainderBaseAssetAmount = 0;
|
|
403
|
+
}
|
|
404
|
+
return [position, remainderBeforeRemoval, pnl];
|
|
400
405
|
}
|
|
401
406
|
/**
|
|
402
407
|
* calculates Buying Power = free collateral / initial margin ratio
|
|
@@ -852,15 +857,18 @@ class User {
|
|
|
852
857
|
getTotalAssetValue(marginCategory) {
|
|
853
858
|
return this.getSpotMarketAssetValue(undefined, marginCategory, true).add(this.getUnrealizedPNL(true, undefined, marginCategory));
|
|
854
859
|
}
|
|
860
|
+
getNetUsdValue() {
|
|
861
|
+
const netSpotValue = this.getNetSpotMarketValue();
|
|
862
|
+
const unrealizedPnl = this.getUnrealizedPNL(true, undefined, undefined);
|
|
863
|
+
return netSpotValue.add(unrealizedPnl);
|
|
864
|
+
}
|
|
855
865
|
/**
|
|
856
866
|
* Calculates the all time P&L of the user.
|
|
857
867
|
*
|
|
858
868
|
* Net withdraws + Net spot market value + Net unrealized P&L -
|
|
859
869
|
*/
|
|
860
870
|
getTotalAllTimePnl() {
|
|
861
|
-
const
|
|
862
|
-
const unrealizedPnl = this.getUnrealizedPNL(true, undefined, undefined);
|
|
863
|
-
const netUsdValue = netBankValue.add(unrealizedPnl);
|
|
871
|
+
const netUsdValue = this.getNetUsdValue();
|
|
864
872
|
const totalDeposits = this.getUserAccount().totalDeposits;
|
|
865
873
|
const totalWithdraws = this.getUserAccount().totalWithdraws;
|
|
866
874
|
const totalPnl = netUsdValue.add(totalWithdraws).sub(totalDeposits);
|
|
@@ -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,28 @@
|
|
|
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
|
+
if (this.frequency > 0) {
|
|
15
|
+
this.intervalId = setInterval(this.userMap.sync.bind(this.userMap), this.frequency);
|
|
16
|
+
}
|
|
17
|
+
if (!this.skipInitialLoad) {
|
|
18
|
+
await this.userMap.sync();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async unsubscribe() {
|
|
22
|
+
if (this.intervalId) {
|
|
23
|
+
clearInterval(this.intervalId);
|
|
24
|
+
this.intervalId = undefined;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.PollingSubscription = PollingSubscription;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { UserMap } from './userMap';
|
|
3
|
+
import { UserAccount } from '../types';
|
|
4
|
+
import { Commitment } from '@solana/web3.js';
|
|
5
|
+
export declare class WebsocketSubscription {
|
|
6
|
+
private userMap;
|
|
7
|
+
private commitment;
|
|
8
|
+
private skipInitialLoad;
|
|
9
|
+
private resubTimeoutMs?;
|
|
10
|
+
private includeIdle?;
|
|
11
|
+
private decodeFn;
|
|
12
|
+
private subscriber;
|
|
13
|
+
constructor({ userMap, commitment, skipInitialLoad, resubTimeoutMs, includeIdle, decodeFn, }: {
|
|
14
|
+
userMap: UserMap;
|
|
15
|
+
commitment: Commitment;
|
|
16
|
+
skipInitialLoad?: boolean;
|
|
17
|
+
resubTimeoutMs?: number;
|
|
18
|
+
includeIdle?: boolean;
|
|
19
|
+
decodeFn: (name: string, data: Buffer) => UserAccount;
|
|
20
|
+
});
|
|
21
|
+
subscribe(): Promise<void>;
|
|
22
|
+
unsubscribe(): Promise<void>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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, decodeFn, }) {
|
|
8
|
+
this.userMap = userMap;
|
|
9
|
+
this.commitment = commitment;
|
|
10
|
+
this.skipInitialLoad = skipInitialLoad;
|
|
11
|
+
this.resubTimeoutMs = resubTimeoutMs;
|
|
12
|
+
this.includeIdle = includeIdle || false;
|
|
13
|
+
this.decodeFn = decodeFn;
|
|
14
|
+
}
|
|
15
|
+
async subscribe() {
|
|
16
|
+
if (!this.subscriber) {
|
|
17
|
+
const filters = [(0, memcmp_1.getUserFilter)()];
|
|
18
|
+
if (!this.includeIdle) {
|
|
19
|
+
filters.push((0, memcmp_1.getNonIdleUserFilter)());
|
|
20
|
+
}
|
|
21
|
+
this.subscriber = new webSocketProgramAccountSubscriber_1.WebSocketProgramAccountSubscriber('UserMap', 'User', this.userMap.driftClient.program, this.decodeFn, {
|
|
22
|
+
filters,
|
|
23
|
+
commitment: this.commitment,
|
|
24
|
+
}, this.resubTimeoutMs);
|
|
25
|
+
}
|
|
26
|
+
await this.subscriber.subscribe((accountId, account, context) => {
|
|
27
|
+
const userKey = accountId.toBase58();
|
|
28
|
+
this.userMap.updateUserAccount(userKey, account, context.slot);
|
|
29
|
+
});
|
|
30
|
+
if (!this.skipInitialLoad) {
|
|
31
|
+
await this.userMap.sync();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async unsubscribe() {
|
|
35
|
+
if (!this.subscriber)
|
|
36
|
+
return;
|
|
37
|
+
await this.subscriber.unsubscribe();
|
|
38
|
+
this.subscriber = undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
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,24 @@ 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
|
|
25
|
-
private syncCallbackCriteria;
|
|
24
|
+
private decode;
|
|
26
25
|
private syncPromise?;
|
|
27
26
|
private syncPromiseResolver;
|
|
28
27
|
/**
|
|
29
28
|
* 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
29
|
*/
|
|
37
|
-
constructor(
|
|
38
|
-
addSyncCallback(syncCallback?: (authorities: PublicKey[]) => Promise<void>, syncCallbackCriteria?: SyncCallbackCriteria): void;
|
|
30
|
+
constructor(config: UserMapConfig);
|
|
39
31
|
subscribe(): Promise<void>;
|
|
40
|
-
addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount): Promise<void>;
|
|
32
|
+
addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount, slot?: number): Promise<void>;
|
|
41
33
|
has(key: string): boolean;
|
|
42
34
|
/**
|
|
43
35
|
* gets the User for a particular userAccountPublicKey, if no User exists, undefined is returned
|
|
@@ -67,7 +59,13 @@ export declare class UserMap implements UserMapInterface {
|
|
|
67
59
|
updateWithEventRecord(record: WrappedEvent<any>): Promise<void>;
|
|
68
60
|
values(): IterableIterator<User>;
|
|
69
61
|
size(): number;
|
|
70
|
-
|
|
62
|
+
/**
|
|
63
|
+
* Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
|
|
64
|
+
* @param filterCriteria: Users must meet these criteria to be included
|
|
65
|
+
* @returns
|
|
66
|
+
*/
|
|
67
|
+
getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[];
|
|
71
68
|
sync(): Promise<void>;
|
|
72
69
|
unsubscribe(): Promise<void>;
|
|
70
|
+
updateUserAccount(key: string, userAccount: UserAccount, slot: number): Promise<void>;
|
|
73
71
|
}
|
package/lib/userMap/userMap.js
CHANGED
|
@@ -5,33 +5,57 @@ 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");
|
|
10
|
+
const user_1 = require("../decode/user");
|
|
8
11
|
class UserMap {
|
|
9
12
|
/**
|
|
10
13
|
* 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
14
|
*/
|
|
18
|
-
constructor(
|
|
15
|
+
constructor(config) {
|
|
16
|
+
var _a, _b, _c;
|
|
19
17
|
this.userMap = new Map();
|
|
20
18
|
this.stateAccountUpdateCallback = async (state) => {
|
|
21
|
-
if (state.numberOfSubAccounts
|
|
19
|
+
if (!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)) {
|
|
22
20
|
await this.sync();
|
|
23
21
|
this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
|
|
24
22
|
}
|
|
25
23
|
};
|
|
26
|
-
this.driftClient = driftClient;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
24
|
+
this.driftClient = config.driftClient;
|
|
25
|
+
if (config.connection) {
|
|
26
|
+
this.connection = config.connection;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
this.connection = this.driftClient.connection;
|
|
30
|
+
}
|
|
31
|
+
this.commitment =
|
|
32
|
+
(_a = config.subscriptionConfig.commitment) !== null && _a !== void 0 ? _a : this.driftClient.opts.commitment;
|
|
33
|
+
this.includeIdle = (_b = config.includeIdle) !== null && _b !== void 0 ? _b : false;
|
|
34
|
+
let decodeFn;
|
|
35
|
+
if ((_c = config.fastDecode) !== null && _c !== void 0 ? _c : true) {
|
|
36
|
+
decodeFn = (name, buffer) => (0, user_1.decodeUser)(buffer);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
decodeFn =
|
|
40
|
+
this.driftClient.program.account.user.coder.accounts.decodeUnchecked.bind(this.driftClient.program.account.user.coder.accounts);
|
|
41
|
+
}
|
|
42
|
+
this.decode = decodeFn;
|
|
43
|
+
if (config.subscriptionConfig.type === 'polling') {
|
|
44
|
+
this.subscription = new PollingSubscription_1.PollingSubscription({
|
|
45
|
+
userMap: this,
|
|
46
|
+
frequency: config.subscriptionConfig.frequency,
|
|
47
|
+
skipInitialLoad: config.skipInitialLoad,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.subscription = new WebsocketSubscription_1.WebsocketSubscription({
|
|
52
|
+
userMap: this,
|
|
53
|
+
commitment: this.commitment,
|
|
54
|
+
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
55
|
+
skipInitialLoad: config.skipInitialLoad,
|
|
56
|
+
decodeFn,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
35
59
|
}
|
|
36
60
|
async subscribe() {
|
|
37
61
|
if (this.size() > 0) {
|
|
@@ -41,13 +65,16 @@ class UserMap {
|
|
|
41
65
|
this.lastNumberOfSubAccounts =
|
|
42
66
|
this.driftClient.getStateAccount().numberOfSubAccounts;
|
|
43
67
|
this.driftClient.eventEmitter.on('stateAccountUpdate', this.stateAccountUpdateCallback);
|
|
44
|
-
await this.
|
|
68
|
+
await this.subscription.subscribe();
|
|
45
69
|
}
|
|
46
|
-
async addPubkey(userAccountPublicKey, userAccount) {
|
|
70
|
+
async addPubkey(userAccountPublicKey, userAccount, slot) {
|
|
47
71
|
const user = new __1.User({
|
|
48
72
|
driftClient: this.driftClient,
|
|
49
73
|
userAccountPublicKey,
|
|
50
|
-
accountSubscription:
|
|
74
|
+
accountSubscription: {
|
|
75
|
+
type: 'custom',
|
|
76
|
+
userAccountSubscriber: new __1.OneShotUserAccountSubscriber(this.driftClient.program, userAccountPublicKey, userAccount, slot, this.commitment),
|
|
77
|
+
},
|
|
51
78
|
});
|
|
52
79
|
await user.subscribe(userAccount);
|
|
53
80
|
this.userMap.set(userAccountPublicKey.toString(), user);
|
|
@@ -148,11 +175,15 @@ class UserMap {
|
|
|
148
175
|
size() {
|
|
149
176
|
return this.userMap.size;
|
|
150
177
|
}
|
|
151
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
|
|
180
|
+
* @param filterCriteria: Users must meet these criteria to be included
|
|
181
|
+
* @returns
|
|
182
|
+
*/
|
|
183
|
+
getUniqueAuthorities(filterCriteria) {
|
|
152
184
|
const usersMeetingCriteria = Array.from(this.userMap.values()).filter((user) => {
|
|
153
185
|
let pass = true;
|
|
154
|
-
if (
|
|
155
|
-
this.syncCallbackCriteria.hasOpenOrders) {
|
|
186
|
+
if (filterCriteria && filterCriteria.hasOpenOrders) {
|
|
156
187
|
pass = pass && user.getUserAccount().hasOpenOrder;
|
|
157
188
|
}
|
|
158
189
|
return pass;
|
|
@@ -176,7 +207,7 @@ class UserMap {
|
|
|
176
207
|
const rpcRequestArgs = [
|
|
177
208
|
this.driftClient.program.programId.toBase58(),
|
|
178
209
|
{
|
|
179
|
-
commitment: this.
|
|
210
|
+
commitment: this.commitment,
|
|
180
211
|
filters,
|
|
181
212
|
encoding: 'base64',
|
|
182
213
|
withContext: true,
|
|
@@ -184,7 +215,7 @@ class UserMap {
|
|
|
184
215
|
];
|
|
185
216
|
const rpcJSONResponse =
|
|
186
217
|
// @ts-ignore
|
|
187
|
-
await this.
|
|
218
|
+
await this.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
188
219
|
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
189
220
|
const slot = rpcResponseAndContext.context.slot;
|
|
190
221
|
const programAccountBufferMap = new Map();
|
|
@@ -195,26 +226,24 @@ class UserMap {
|
|
|
195
226
|
}
|
|
196
227
|
for (const [key, buffer] of programAccountBufferMap.entries()) {
|
|
197
228
|
if (!this.has(key)) {
|
|
198
|
-
const userAccount = this.
|
|
229
|
+
const userAccount = this.decode('User', buffer);
|
|
199
230
|
await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
|
|
231
|
+
this.userMap.get(key).accountSubscriber.updateData(userAccount, slot);
|
|
200
232
|
}
|
|
233
|
+
// give event loop a chance to breathe
|
|
234
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
201
235
|
}
|
|
202
236
|
for (const [key, user] of this.userMap.entries()) {
|
|
203
237
|
if (!programAccountBufferMap.has(key)) {
|
|
204
238
|
await user.unsubscribe();
|
|
205
239
|
this.userMap.delete(key);
|
|
206
240
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
user.accountSubscriber.updateData(userAccount, slot);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
if (this.syncCallback) {
|
|
213
|
-
await this.syncCallback(this.getUniqueAuthorities());
|
|
241
|
+
// give event loop a chance to breathe
|
|
242
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
214
243
|
}
|
|
215
244
|
}
|
|
216
245
|
catch (e) {
|
|
217
|
-
console.error(`Error in UserMap.sync()
|
|
246
|
+
console.error(`Error in UserMap.sync():`);
|
|
218
247
|
console.error(e);
|
|
219
248
|
}
|
|
220
249
|
finally {
|
|
@@ -223,6 +252,7 @@ class UserMap {
|
|
|
223
252
|
}
|
|
224
253
|
}
|
|
225
254
|
async unsubscribe() {
|
|
255
|
+
await this.subscription.unsubscribe();
|
|
226
256
|
for (const [key, user] of this.userMap.entries()) {
|
|
227
257
|
await user.unsubscribe();
|
|
228
258
|
this.userMap.delete(key);
|
|
@@ -232,5 +262,14 @@ class UserMap {
|
|
|
232
262
|
this.lastNumberOfSubAccounts = undefined;
|
|
233
263
|
}
|
|
234
264
|
}
|
|
265
|
+
async updateUserAccount(key, userAccount, slot) {
|
|
266
|
+
if (!this.userMap.has(key)) {
|
|
267
|
+
this.addPubkey(new web3_js_1.PublicKey(key), userAccount, slot);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
const user = this.userMap.get(key);
|
|
271
|
+
user.accountSubscriber.updateData(userAccount, slot);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
235
274
|
}
|
|
236
275
|
exports.UserMap = UserMap;
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
fastDecode?: boolean;
|
|
21
|
+
};
|
package/lib/userStats.d.ts
CHANGED
package/lib/userStats.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@drift-labs/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.52.0-beta.1",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"types": "lib/index.d.ts",
|
|
6
6
|
"author": "crispheaney",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"uuid": "^8.3.2"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
+
"object-sizeof": "^2.6.3",
|
|
47
48
|
"@types/big.js": "^6.2.0",
|
|
48
49
|
"@types/bn.js": "^5.1.3",
|
|
49
50
|
"@types/chai": "^4.3.1",
|
|
@@ -4,7 +4,11 @@ import StrictEventEmitter from 'strict-event-emitter-types';
|
|
|
4
4
|
import { EventEmitter } from 'events';
|
|
5
5
|
import { UserAccount } from '../types';
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Basic implementation of UserAccountSubscriber. It will only take in UserAccount
|
|
9
|
+
* data during initialization and will not fetch or subscribe to updates.
|
|
10
|
+
*/
|
|
11
|
+
export class BasicUserAccountSubscriber implements UserAccountSubscriber {
|
|
8
12
|
isSubscribed: boolean;
|
|
9
13
|
eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
|
|
10
14
|
userAccountPublicKey: PublicKey;
|
|
@@ -16,8 +20,8 @@ export class MockUserAccountSubscriber implements UserAccountSubscriber {
|
|
|
16
20
|
|
|
17
21
|
public constructor(
|
|
18
22
|
userAccountPublicKey: PublicKey,
|
|
19
|
-
data
|
|
20
|
-
slot
|
|
23
|
+
data?: UserAccount,
|
|
24
|
+
slot?: number
|
|
21
25
|
) {
|
|
22
26
|
this.isSubscribed = true;
|
|
23
27
|
this.eventEmitter = new EventEmitter();
|
|
@@ -46,8 +50,10 @@ export class MockUserAccountSubscriber implements UserAccountSubscriber {
|
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
public updateData(userAccount: UserAccount, slot: number): void {
|
|
49
|
-
this.user
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
if (!this.user || slot >= (this.user.slot ?? 0)) {
|
|
54
|
+
this.user = { data: userAccount, slot };
|
|
55
|
+
this.eventEmitter.emit('userAccountUpdate', userAccount);
|
|
56
|
+
this.eventEmitter.emit('update');
|
|
57
|
+
}
|
|
52
58
|
}
|
|
53
59
|
}
|
|
@@ -78,6 +78,7 @@ export class BulkAccountLoader {
|
|
|
78
78
|
if (existingAccountToLoad) {
|
|
79
79
|
existingAccountToLoad.callbacks.delete(callbackId);
|
|
80
80
|
if (existingAccountToLoad.callbacks.size === 0) {
|
|
81
|
+
this.bufferAndSlotMap.delete(publicKey.toString());
|
|
81
82
|
this.accountsToLoad.delete(existingAccountToLoad.publicKey.toString());
|
|
82
83
|
}
|
|
83
84
|
}
|
|
@@ -153,9 +154,11 @@ export class BulkAccountLoader {
|
|
|
153
154
|
const requests = new Array<{ methodName: string; args: any }>();
|
|
154
155
|
for (const accountsToLoadChunk of accountsToLoadChunks) {
|
|
155
156
|
const args = [
|
|
156
|
-
accountsToLoadChunk
|
|
157
|
-
|
|
158
|
-
|
|
157
|
+
accountsToLoadChunk
|
|
158
|
+
.filter((accountToLoad) => accountToLoad.callbacks.size > 0)
|
|
159
|
+
.map((accountToLoad) => {
|
|
160
|
+
return accountToLoad.publicKey.toBase58();
|
|
161
|
+
}),
|
|
159
162
|
{ commitment: this.commitment },
|
|
160
163
|
];
|
|
161
164
|
|
|
@@ -190,6 +193,10 @@ export class BulkAccountLoader {
|
|
|
190
193
|
|
|
191
194
|
const accountsToLoad = accountsToLoadChunks[i];
|
|
192
195
|
accountsToLoad.forEach((accountToLoad, j) => {
|
|
196
|
+
if (accountToLoad.callbacks.size === 0) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
193
200
|
const key = accountToLoad.publicKey.toBase58();
|
|
194
201
|
const oldRPCResponse = this.bufferAndSlotMap.get(key);
|
|
195
202
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Commitment, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { UserAccount } from '../types';
|
|
3
|
+
import { BasicUserAccountSubscriber } from './basicUserAccountSubscriber';
|
|
4
|
+
import { Program } from '@coral-xyz/anchor';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Simple implementation of UserAccountSubscriber. It will fetch the UserAccount
|
|
8
|
+
* date on subscribe (or call to fetch) if no account data is provided on init.
|
|
9
|
+
* Expect to use only 1 RPC call unless you call fetch repeatedly.
|
|
10
|
+
*/
|
|
11
|
+
export class OneShotUserAccountSubscriber extends BasicUserAccountSubscriber {
|
|
12
|
+
program: Program;
|
|
13
|
+
commitment: Commitment;
|
|
14
|
+
|
|
15
|
+
public constructor(
|
|
16
|
+
program: Program,
|
|
17
|
+
userAccountPublicKey: PublicKey,
|
|
18
|
+
data?: UserAccount,
|
|
19
|
+
slot?: number,
|
|
20
|
+
commitment?: Commitment
|
|
21
|
+
) {
|
|
22
|
+
super(userAccountPublicKey, data, slot);
|
|
23
|
+
this.program = program;
|
|
24
|
+
this.commitment = commitment ?? 'confirmed';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async subscribe(userAccount?: UserAccount): Promise<boolean> {
|
|
28
|
+
if (userAccount) {
|
|
29
|
+
this.user = { data: userAccount, slot: this.user.slot };
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
await this.fetchIfUnloaded();
|
|
34
|
+
if (this.doesAccountExist()) {
|
|
35
|
+
this.eventEmitter.emit('update');
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async fetchIfUnloaded(): Promise<void> {
|
|
41
|
+
if (this.user.data === undefined) {
|
|
42
|
+
await this.fetch();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async fetch(): Promise<void> {
|
|
47
|
+
try {
|
|
48
|
+
const dataAndContext = await this.program.account.user.fetchAndContext(
|
|
49
|
+
this.userAccountPublicKey,
|
|
50
|
+
this.commitment
|
|
51
|
+
);
|
|
52
|
+
if (dataAndContext.context.slot > (this.user?.slot ?? 0)) {
|
|
53
|
+
this.user = {
|
|
54
|
+
data: dataAndContext.data as UserAccount,
|
|
55
|
+
slot: dataAndContext.context.slot,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error(
|
|
60
|
+
`OneShotUserAccountSubscriber.fetch() UserAccount does not exist: ${e.message}`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/adminClient.ts
CHANGED
|
@@ -846,6 +846,20 @@ export class AdminClient extends DriftClient {
|
|
|
846
846
|
);
|
|
847
847
|
}
|
|
848
848
|
|
|
849
|
+
public async updateStateMaxInitializeUserFee(
|
|
850
|
+
maxInitializeUserFee: number
|
|
851
|
+
): Promise<TransactionSignature> {
|
|
852
|
+
return await this.program.rpc.updateStateMaxInitializeUserFee(
|
|
853
|
+
maxInitializeUserFee,
|
|
854
|
+
{
|
|
855
|
+
accounts: {
|
|
856
|
+
admin: this.wallet.publicKey,
|
|
857
|
+
state: await this.getStatePublicKey(),
|
|
858
|
+
},
|
|
859
|
+
}
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
|
|
849
863
|
public async updateWithdrawGuardThreshold(
|
|
850
864
|
spotMarketIndex: number,
|
|
851
865
|
withdrawGuardThreshold: BN
|
|
@@ -1167,6 +1181,19 @@ export class AdminClient extends DriftClient {
|
|
|
1167
1181
|
});
|
|
1168
1182
|
}
|
|
1169
1183
|
|
|
1184
|
+
public async updatePhoenixFulfillmentConfigStatus(
|
|
1185
|
+
phoenixFulfillmentConfig: PublicKey,
|
|
1186
|
+
status: SpotFulfillmentConfigStatus
|
|
1187
|
+
): Promise<TransactionSignature> {
|
|
1188
|
+
return await this.program.rpc.phoenixFulfillmentConfigStatus(status, {
|
|
1189
|
+
accounts: {
|
|
1190
|
+
admin: this.wallet.publicKey,
|
|
1191
|
+
state: await this.getStatePublicKey(),
|
|
1192
|
+
phoenixFulfillmentConfig,
|
|
1193
|
+
},
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1170
1197
|
public async updateSpotMarketExpiry(
|
|
1171
1198
|
spotMarketIndex: number,
|
|
1172
1199
|
expiryTs: BN
|
|
@@ -99,3 +99,7 @@ export const OPEN_ORDER_MARGIN_REQUIREMENT = QUOTE_PRECISION.div(new BN(100));
|
|
|
99
99
|
export const DEFAULT_REVENUE_SINCE_LAST_FUNDING_SPREAD_RETREAT = new BN(
|
|
100
100
|
-25
|
|
101
101
|
).mul(QUOTE_PRECISION);
|
|
102
|
+
|
|
103
|
+
export const ACCOUNT_AGE_DELETION_CUTOFF_SECONDS = 60 * 60 * 24 * 13; // 13 days
|
|
104
|
+
export const IDLE_TIME_SLOTS = 9000;
|
|
105
|
+
export const SLOT_TIME_ESTIMATE_MS = 400;
|