@drift-labs/sdk 2.98.0-beta.2 → 2.98.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/browser/accounts/pollingHighLeverageModeConfigAccountSubscriber.d.ts +29 -0
- package/lib/browser/accounts/pollingHighLeverageModeConfigAccountSubscriber.js +111 -0
- package/lib/browser/accounts/types.d.ts +14 -1
- package/lib/browser/accounts/webSocketHighLeverageModeConfigAccountSubscriber.d.ts +23 -0
- package/lib/browser/accounts/webSocketHighLeverageModeConfigAccountSubscriber.js +69 -0
- package/lib/browser/addresses/pda.d.ts +1 -0
- package/lib/browser/addresses/pda.js +8 -1
- package/lib/browser/constants/perpMarkets.js +11 -0
- package/lib/browser/constants/spotMarkets.js +4 -4
- package/lib/browser/driftClient.d.ts +20 -6
- package/lib/browser/driftClient.js +68 -24
- package/lib/browser/events/types.d.ts +3 -2
- package/lib/browser/events/types.js +1 -0
- package/lib/browser/idl/drift.json +227 -13
- package/lib/browser/index.d.ts +4 -0
- package/lib/browser/index.js +4 -0
- package/lib/browser/jupiter/jupiterClient.d.ts +6 -0
- package/lib/browser/memcmp.d.ts +3 -0
- package/lib/browser/memcmp.js +28 -1
- package/lib/browser/slot/SlothashSubscriber.d.ts +26 -0
- package/lib/browser/slot/SlothashSubscriber.js +85 -0
- package/lib/browser/types.d.ts +10 -1
- package/lib/browser/userMap/referrerMap.d.ts +45 -0
- package/lib/browser/userMap/referrerMap.js +180 -0
- package/lib/browser/util/digest.d.ts +4 -0
- package/lib/browser/util/digest.js +14 -0
- package/lib/node/accounts/pollingHighLeverageModeConfigAccountSubscriber.d.ts +29 -0
- package/lib/node/accounts/pollingHighLeverageModeConfigAccountSubscriber.js +111 -0
- package/lib/node/accounts/types.d.ts +14 -1
- package/lib/node/accounts/webSocketHighLeverageModeConfigAccountSubscriber.d.ts +23 -0
- package/lib/node/accounts/webSocketHighLeverageModeConfigAccountSubscriber.js +69 -0
- package/lib/node/addresses/pda.d.ts +1 -0
- package/lib/node/addresses/pda.js +8 -1
- package/lib/node/constants/perpMarkets.js +11 -0
- package/lib/node/constants/spotMarkets.js +4 -4
- package/lib/node/driftClient.d.ts +20 -6
- package/lib/node/driftClient.js +68 -24
- package/lib/node/events/types.d.ts +3 -2
- package/lib/node/events/types.js +1 -0
- package/lib/node/idl/drift.json +227 -13
- package/lib/node/index.d.ts +4 -0
- package/lib/node/index.js +4 -0
- package/lib/node/jupiter/jupiterClient.d.ts +6 -0
- package/lib/node/memcmp.d.ts +3 -0
- package/lib/node/memcmp.js +28 -1
- package/lib/node/slot/SlothashSubscriber.d.ts +26 -0
- package/lib/node/slot/SlothashSubscriber.js +85 -0
- package/lib/node/types.d.ts +10 -1
- package/lib/node/userMap/referrerMap.d.ts +45 -0
- package/lib/node/userMap/referrerMap.js +180 -0
- package/lib/node/util/digest.d.ts +4 -0
- package/lib/node/util/digest.js +14 -0
- package/package.json +1 -1
- package/src/accounts/pollingHighLeverageModeConfigAccountSubscriber.ts +189 -0
- package/src/accounts/types.ts +25 -1
- package/src/accounts/webSocketHighLeverageModeConfigAccountSubscriber.ts +131 -0
- package/src/addresses/pda.ts +13 -0
- package/src/constants/perpMarkets.ts +12 -0
- package/src/constants/spotMarkets.ts +4 -4
- package/src/driftClient.ts +139 -42
- package/src/events/types.ts +5 -1
- package/src/idl/drift.json +227 -13
- package/src/index.ts +4 -0
- package/src/jupiter/jupiterClient.ts +6 -0
- package/src/memcmp.ts +27 -0
- package/src/slot/SlothashSubscriber.ts +126 -0
- package/src/types.ts +11 -1
- package/src/userMap/referrerMap.ts +283 -0
- package/src/util/digest.ts +11 -0
- package/tests/ci/verifyConstants.ts +16 -2
package/lib/node/memcmp.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.getUserWithName = exports.getUserThatHasBeenLP = exports.getUserWithAuctionFilter = exports.getUserWithOrderFilter = exports.getNonIdleUserFilter = exports.getUserFilter = void 0;
|
|
6
|
+
exports.getUserStatsIsReferredOrReferrerFilter = exports.getUserStatsIsReferredFilter = exports.getUserStatsFilter = exports.getUserWithName = exports.getUserThatHasBeenLP = exports.getUserWithAuctionFilter = exports.getUserWithOrderFilter = exports.getNonIdleUserFilter = exports.getUserFilter = void 0;
|
|
7
7
|
const bs58_1 = __importDefault(require("bs58"));
|
|
8
8
|
const anchor_1 = require("@coral-xyz/anchor");
|
|
9
9
|
const userName_1 = require("./userName");
|
|
@@ -61,3 +61,30 @@ function getUserWithName(name) {
|
|
|
61
61
|
};
|
|
62
62
|
}
|
|
63
63
|
exports.getUserWithName = getUserWithName;
|
|
64
|
+
function getUserStatsFilter() {
|
|
65
|
+
return {
|
|
66
|
+
memcmp: {
|
|
67
|
+
offset: 0,
|
|
68
|
+
bytes: bs58_1.default.encode(anchor_1.BorshAccountsCoder.accountDiscriminator('UserStats')),
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
exports.getUserStatsFilter = getUserStatsFilter;
|
|
73
|
+
function getUserStatsIsReferredFilter() {
|
|
74
|
+
return {
|
|
75
|
+
memcmp: {
|
|
76
|
+
offset: 188,
|
|
77
|
+
bytes: bs58_1.default.encode(Buffer.from(Uint8Array.from([2]))),
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
exports.getUserStatsIsReferredFilter = getUserStatsIsReferredFilter;
|
|
82
|
+
function getUserStatsIsReferredOrReferrerFilter() {
|
|
83
|
+
return {
|
|
84
|
+
memcmp: {
|
|
85
|
+
offset: 188,
|
|
86
|
+
bytes: bs58_1.default.encode(Buffer.from(Uint8Array.from([3]))),
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
exports.getUserStatsIsReferredOrReferrerFilter = getUserStatsIsReferredOrReferrerFilter;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Commitment, Connection } from '@solana/web3.js';
|
|
3
|
+
type SlothashSubscriberConfig = {
|
|
4
|
+
resubTimeoutMs?: number;
|
|
5
|
+
commitment?: Commitment;
|
|
6
|
+
};
|
|
7
|
+
export type Slothash = {
|
|
8
|
+
slot: number;
|
|
9
|
+
hash: string;
|
|
10
|
+
};
|
|
11
|
+
export declare class SlothashSubscriber {
|
|
12
|
+
private connection;
|
|
13
|
+
currentSlothash: Slothash;
|
|
14
|
+
subscriptionId: number;
|
|
15
|
+
commitment: Commitment;
|
|
16
|
+
timeoutId?: NodeJS.Timeout;
|
|
17
|
+
resubTimeoutMs?: number;
|
|
18
|
+
isUnsubscribing: boolean;
|
|
19
|
+
receivingData: boolean;
|
|
20
|
+
constructor(connection: Connection, config?: SlothashSubscriberConfig);
|
|
21
|
+
subscribe(): Promise<void>;
|
|
22
|
+
private setTimeout;
|
|
23
|
+
getSlothash(): Slothash;
|
|
24
|
+
unsubscribe(onResub?: boolean): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SlothashSubscriber = void 0;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes");
|
|
6
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
7
|
+
class SlothashSubscriber {
|
|
8
|
+
constructor(connection, config) {
|
|
9
|
+
var _a;
|
|
10
|
+
this.connection = connection;
|
|
11
|
+
this.isUnsubscribing = false;
|
|
12
|
+
this.receivingData = false;
|
|
13
|
+
this.resubTimeoutMs = config === null || config === void 0 ? void 0 : config.resubTimeoutMs;
|
|
14
|
+
this.commitment = (_a = config === null || config === void 0 ? void 0 : config.commitment) !== null && _a !== void 0 ? _a : 'processed';
|
|
15
|
+
if (this.resubTimeoutMs < 1000) {
|
|
16
|
+
console.log('resubTimeoutMs should be at least 1000ms to avoid spamming resub');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async subscribe() {
|
|
20
|
+
if (this.subscriptionId != null) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const currentAccountData = await this.connection.getAccountInfo(web3_js_1.SYSVAR_SLOT_HASHES_PUBKEY, this.commitment);
|
|
24
|
+
if (currentAccountData == null) {
|
|
25
|
+
throw new Error('Failed to retrieve current slot hash');
|
|
26
|
+
}
|
|
27
|
+
this.currentSlothash = deserializeSlothash(currentAccountData.data);
|
|
28
|
+
this.subscriptionId = this.connection.onAccountChange(web3_js_1.SYSVAR_SLOT_HASHES_PUBKEY, (slothashInfo, context) => {
|
|
29
|
+
if (!this.currentSlothash || this.currentSlothash.slot < context.slot) {
|
|
30
|
+
if (this.resubTimeoutMs && !this.isUnsubscribing) {
|
|
31
|
+
this.receivingData = true;
|
|
32
|
+
clearTimeout(this.timeoutId);
|
|
33
|
+
this.setTimeout();
|
|
34
|
+
}
|
|
35
|
+
this.currentSlothash = deserializeSlothash(slothashInfo.data);
|
|
36
|
+
}
|
|
37
|
+
}, this.commitment);
|
|
38
|
+
if (this.resubTimeoutMs) {
|
|
39
|
+
this.receivingData = true;
|
|
40
|
+
this.setTimeout();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
setTimeout() {
|
|
44
|
+
this.timeoutId = setTimeout(async () => {
|
|
45
|
+
if (this.isUnsubscribing) {
|
|
46
|
+
// If we are in the process of unsubscribing, do not attempt to resubscribe
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (this.receivingData) {
|
|
50
|
+
console.log(`No new slot in ${this.resubTimeoutMs}ms, slot subscriber resubscribing`);
|
|
51
|
+
await this.unsubscribe(true);
|
|
52
|
+
this.receivingData = false;
|
|
53
|
+
await this.subscribe();
|
|
54
|
+
}
|
|
55
|
+
}, this.resubTimeoutMs);
|
|
56
|
+
}
|
|
57
|
+
getSlothash() {
|
|
58
|
+
return this.currentSlothash;
|
|
59
|
+
}
|
|
60
|
+
async unsubscribe(onResub = false) {
|
|
61
|
+
if (!onResub) {
|
|
62
|
+
this.resubTimeoutMs = undefined;
|
|
63
|
+
}
|
|
64
|
+
this.isUnsubscribing = true;
|
|
65
|
+
clearTimeout(this.timeoutId);
|
|
66
|
+
this.timeoutId = undefined;
|
|
67
|
+
if (this.subscriptionId != null) {
|
|
68
|
+
await this.connection.removeSlotChangeListener(this.subscriptionId);
|
|
69
|
+
this.subscriptionId = undefined;
|
|
70
|
+
this.isUnsubscribing = false;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
this.isUnsubscribing = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
exports.SlothashSubscriber = SlothashSubscriber;
|
|
78
|
+
function deserializeSlothash(data) {
|
|
79
|
+
const slotNumber = new anchor_1.BN(data.subarray(8, 16), 10, 'le');
|
|
80
|
+
const hash = bytes_1.bs58.encode(data.subarray(16, 48));
|
|
81
|
+
return {
|
|
82
|
+
slot: slotNumber.toNumber(),
|
|
83
|
+
hash,
|
|
84
|
+
};
|
|
85
|
+
}
|
package/lib/node/types.d.ts
CHANGED
|
@@ -642,6 +642,15 @@ export type SettlePnlRecord = {
|
|
|
642
642
|
settlePrice: BN;
|
|
643
643
|
explanation: SettlePnlExplanation;
|
|
644
644
|
};
|
|
645
|
+
export type SwiftOrderRecord = {
|
|
646
|
+
ts: BN;
|
|
647
|
+
user: PublicKey;
|
|
648
|
+
hash: string;
|
|
649
|
+
matchingOrderParams: OrderParams;
|
|
650
|
+
swiftOrderMaxSlot: BN;
|
|
651
|
+
swiftOrderUuid: Uint8Array;
|
|
652
|
+
userOrderId: number;
|
|
653
|
+
};
|
|
645
654
|
export type OrderRecord = {
|
|
646
655
|
ts: BN;
|
|
647
656
|
user: PublicKey;
|
|
@@ -1110,10 +1119,10 @@ export declare const DefaultOrderParams: OrderParams;
|
|
|
1110
1119
|
export type SwiftServerMessage = {
|
|
1111
1120
|
slot: BN;
|
|
1112
1121
|
swiftOrderSignature: Uint8Array;
|
|
1122
|
+
uuid: Uint8Array;
|
|
1113
1123
|
};
|
|
1114
1124
|
export type SwiftOrderParamsMessage = {
|
|
1115
1125
|
swiftOrderParams: OptionalOrderParams;
|
|
1116
|
-
expectedOrderId: number;
|
|
1117
1126
|
subAccountId: number;
|
|
1118
1127
|
takeProfitOrderParams: SwiftTriggerOrderParams | null;
|
|
1119
1128
|
stopLossOrderParams: SwiftTriggerOrderParams | null;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { MemcmpFilter } from '@solana/web3.js';
|
|
2
|
+
import { BulkAccountLoader } from '../accounts/bulkAccountLoader';
|
|
3
|
+
import { DriftClient } from '../driftClient';
|
|
4
|
+
import { ReferrerInfo } from '../types';
|
|
5
|
+
export declare class ReferrerMap {
|
|
6
|
+
/**
|
|
7
|
+
* map from authority pubkey to ReferrerInfo.
|
|
8
|
+
* - if a user has not been entered into the map, the value is undefined
|
|
9
|
+
* - if a user has no referrer, the value is null
|
|
10
|
+
* - if a user has a referrer, the value is a ReferrerInfo object
|
|
11
|
+
*/
|
|
12
|
+
private referrerMap;
|
|
13
|
+
private driftClient;
|
|
14
|
+
private bulkAccountLoader;
|
|
15
|
+
private parallelSync;
|
|
16
|
+
private fetchPromise?;
|
|
17
|
+
private fetchPromiseResolver;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new UserStatsMap instance.
|
|
20
|
+
*
|
|
21
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
22
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
23
|
+
*/
|
|
24
|
+
constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader, parallelSync?: boolean);
|
|
25
|
+
/**
|
|
26
|
+
* Subscribe to all UserStats accounts.
|
|
27
|
+
*/
|
|
28
|
+
subscribe(): Promise<void>;
|
|
29
|
+
has(authorityPublicKey: string): boolean;
|
|
30
|
+
get(authorityPublicKey: string): ReferrerInfo | undefined;
|
|
31
|
+
addReferrerInfo(authority: string, referrerInfo?: ReferrerInfo | null): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
34
|
+
* reading one from the blockchain if necessary.
|
|
35
|
+
* @param authorityPublicKey
|
|
36
|
+
* @returns
|
|
37
|
+
*/
|
|
38
|
+
mustGet(authorityPublicKey: string): Promise<ReferrerInfo | undefined>;
|
|
39
|
+
values(): IterableIterator<ReferrerInfo | null>;
|
|
40
|
+
size(): number;
|
|
41
|
+
sync(): Promise<void>;
|
|
42
|
+
syncAll(): Promise<void>;
|
|
43
|
+
syncReferrer(referrerFilter: MemcmpFilter): Promise<void>;
|
|
44
|
+
unsubscribe(): Promise<void>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReferrerMap = void 0;
|
|
4
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const bulkAccountLoader_1 = require("../accounts/bulkAccountLoader");
|
|
6
|
+
const pda_1 = require("../addresses/pda");
|
|
7
|
+
const memcmp_1 = require("../memcmp");
|
|
8
|
+
const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes");
|
|
9
|
+
const DEFAULT_PUBLIC_KEY = web3_js_1.PublicKey.default.toBase58();
|
|
10
|
+
class ReferrerMap {
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new UserStatsMap instance.
|
|
13
|
+
*
|
|
14
|
+
* @param {DriftClient} driftClient - The DriftClient instance.
|
|
15
|
+
* @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
|
|
16
|
+
*/
|
|
17
|
+
constructor(driftClient, bulkAccountLoader, parallelSync) {
|
|
18
|
+
/**
|
|
19
|
+
* map from authority pubkey to ReferrerInfo.
|
|
20
|
+
* - if a user has not been entered into the map, the value is undefined
|
|
21
|
+
* - if a user has no referrer, the value is null
|
|
22
|
+
* - if a user has a referrer, the value is a ReferrerInfo object
|
|
23
|
+
*/
|
|
24
|
+
this.referrerMap = new Map();
|
|
25
|
+
this.driftClient = driftClient;
|
|
26
|
+
if (!bulkAccountLoader) {
|
|
27
|
+
bulkAccountLoader = new bulkAccountLoader_1.BulkAccountLoader(driftClient.connection, driftClient.opts.commitment, 0);
|
|
28
|
+
}
|
|
29
|
+
this.bulkAccountLoader = bulkAccountLoader;
|
|
30
|
+
this.parallelSync = parallelSync !== undefined ? parallelSync : true;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Subscribe to all UserStats accounts.
|
|
34
|
+
*/
|
|
35
|
+
async subscribe() {
|
|
36
|
+
if (this.size() > 0) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
await this.driftClient.subscribe();
|
|
40
|
+
await this.sync();
|
|
41
|
+
}
|
|
42
|
+
has(authorityPublicKey) {
|
|
43
|
+
return this.referrerMap.has(authorityPublicKey);
|
|
44
|
+
}
|
|
45
|
+
get(authorityPublicKey) {
|
|
46
|
+
const info = this.referrerMap.get(authorityPublicKey);
|
|
47
|
+
return info === null ? undefined : info;
|
|
48
|
+
}
|
|
49
|
+
async addReferrerInfo(authority, referrerInfo) {
|
|
50
|
+
if (referrerInfo || referrerInfo === null) {
|
|
51
|
+
this.referrerMap.set(authority, referrerInfo);
|
|
52
|
+
}
|
|
53
|
+
else if (referrerInfo === undefined) {
|
|
54
|
+
const userStatsAccountPublicKey = (0, pda_1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, new web3_js_1.PublicKey(authority));
|
|
55
|
+
const buffer = (await this.driftClient.connection.getAccountInfo(userStatsAccountPublicKey, 'processed')).data;
|
|
56
|
+
const referrer = bytes_1.bs58.encode(buffer.subarray(40, 72));
|
|
57
|
+
const referrerKey = new web3_js_1.PublicKey(referrer);
|
|
58
|
+
this.addReferrerInfo(authority, referrer === DEFAULT_PUBLIC_KEY
|
|
59
|
+
? null
|
|
60
|
+
: {
|
|
61
|
+
referrer: (0, pda_1.getUserAccountPublicKeySync)(this.driftClient.program.programId, referrerKey, 0),
|
|
62
|
+
referrerStats: (0, pda_1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, referrerKey),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Enforce that a UserStats will exist for the given authorityPublicKey,
|
|
68
|
+
* reading one from the blockchain if necessary.
|
|
69
|
+
* @param authorityPublicKey
|
|
70
|
+
* @returns
|
|
71
|
+
*/
|
|
72
|
+
async mustGet(authorityPublicKey) {
|
|
73
|
+
if (!this.has(authorityPublicKey)) {
|
|
74
|
+
await this.addReferrerInfo(authorityPublicKey);
|
|
75
|
+
}
|
|
76
|
+
return this.get(authorityPublicKey);
|
|
77
|
+
}
|
|
78
|
+
values() {
|
|
79
|
+
return this.referrerMap.values();
|
|
80
|
+
}
|
|
81
|
+
size() {
|
|
82
|
+
return this.referrerMap.size;
|
|
83
|
+
}
|
|
84
|
+
async sync() {
|
|
85
|
+
if (this.fetchPromise) {
|
|
86
|
+
return this.fetchPromise;
|
|
87
|
+
}
|
|
88
|
+
this.fetchPromise = new Promise((resolver) => {
|
|
89
|
+
this.fetchPromiseResolver = resolver;
|
|
90
|
+
});
|
|
91
|
+
try {
|
|
92
|
+
if (this.parallelSync) {
|
|
93
|
+
await Promise.all([
|
|
94
|
+
this.syncAll(),
|
|
95
|
+
this.syncReferrer((0, memcmp_1.getUserStatsIsReferredFilter)()),
|
|
96
|
+
this.syncReferrer((0, memcmp_1.getUserStatsIsReferredOrReferrerFilter)()),
|
|
97
|
+
]);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
await this.syncAll();
|
|
101
|
+
await this.syncReferrer((0, memcmp_1.getUserStatsIsReferredFilter)());
|
|
102
|
+
await this.syncReferrer((0, memcmp_1.getUserStatsIsReferredOrReferrerFilter)());
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
console.error('error in referrerMap.sync', e);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
this.fetchPromiseResolver();
|
|
110
|
+
this.fetchPromise = undefined;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async syncAll() {
|
|
114
|
+
const rpcRequestArgs = [
|
|
115
|
+
this.driftClient.program.programId.toBase58(),
|
|
116
|
+
{
|
|
117
|
+
commitment: this.driftClient.opts.commitment,
|
|
118
|
+
filters: [(0, memcmp_1.getUserStatsFilter)()],
|
|
119
|
+
encoding: 'base64',
|
|
120
|
+
dataSlice: {
|
|
121
|
+
offset: 0,
|
|
122
|
+
length: 0,
|
|
123
|
+
},
|
|
124
|
+
withContext: true,
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
const rpcJSONResponse =
|
|
128
|
+
// @ts-ignore
|
|
129
|
+
await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
130
|
+
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
131
|
+
for (const account of rpcResponseAndContext.value) {
|
|
132
|
+
// only add if it isn't already in the map
|
|
133
|
+
// so that if syncReferrer already set it, we dont overwrite
|
|
134
|
+
if (!this.has(account.pubkey)) {
|
|
135
|
+
this.addReferrerInfo(account.pubkey, null);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async syncReferrer(referrerFilter) {
|
|
140
|
+
const rpcRequestArgs = [
|
|
141
|
+
this.driftClient.program.programId.toBase58(),
|
|
142
|
+
{
|
|
143
|
+
commitment: this.driftClient.opts.commitment,
|
|
144
|
+
filters: [(0, memcmp_1.getUserStatsFilter)(), referrerFilter],
|
|
145
|
+
encoding: 'base64',
|
|
146
|
+
dataSlice: {
|
|
147
|
+
offset: 0,
|
|
148
|
+
length: 72,
|
|
149
|
+
},
|
|
150
|
+
withContext: true,
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
const rpcJSONResponse =
|
|
154
|
+
// @ts-ignore
|
|
155
|
+
await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
|
|
156
|
+
const rpcResponseAndContext = rpcJSONResponse.result;
|
|
157
|
+
const batchSize = 1000;
|
|
158
|
+
for (let i = 0; i < rpcResponseAndContext.value.length; i += batchSize) {
|
|
159
|
+
const batch = rpcResponseAndContext.value.slice(i, i + batchSize);
|
|
160
|
+
await Promise.all(batch.map(async (programAccount) => {
|
|
161
|
+
// @ts-ignore
|
|
162
|
+
const buffer = Buffer.from(programAccount.account.data[0], programAccount.account.data[1]);
|
|
163
|
+
const authority = bytes_1.bs58.encode(buffer.subarray(8, 40));
|
|
164
|
+
const referrer = bytes_1.bs58.encode(buffer.subarray(40, 72));
|
|
165
|
+
const referrerKey = new web3_js_1.PublicKey(referrer);
|
|
166
|
+
this.addReferrerInfo(authority, referrer === DEFAULT_PUBLIC_KEY
|
|
167
|
+
? null
|
|
168
|
+
: {
|
|
169
|
+
referrer: (0, pda_1.getUserAccountPublicKeySync)(this.driftClient.program.programId, referrerKey, 0),
|
|
170
|
+
referrerStats: (0, pda_1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, referrerKey),
|
|
171
|
+
});
|
|
172
|
+
}));
|
|
173
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
async unsubscribe() {
|
|
177
|
+
this.referrerMap.clear();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
exports.ReferrerMap = ReferrerMap;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.digestSignature = exports.digest = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
function digest(data) {
|
|
6
|
+
const hash = (0, crypto_1.createHash)('sha256');
|
|
7
|
+
hash.update(data);
|
|
8
|
+
return hash.digest();
|
|
9
|
+
}
|
|
10
|
+
exports.digest = digest;
|
|
11
|
+
function digestSignature(signature) {
|
|
12
|
+
return (0, crypto_1.createHash)('sha256').update(signature).digest('base64');
|
|
13
|
+
}
|
|
14
|
+
exports.digestSignature = digestSignature;
|
package/package.json
CHANGED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DataAndSlot,
|
|
3
|
+
NotSubscribedError,
|
|
4
|
+
HighLeverageModeConfigAccountEvents,
|
|
5
|
+
HighLeverageModeConfigAccountSubscriber,
|
|
6
|
+
} from './types';
|
|
7
|
+
import { Program } from '@coral-xyz/anchor';
|
|
8
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
9
|
+
import { EventEmitter } from 'events';
|
|
10
|
+
import { PublicKey } from '@solana/web3.js';
|
|
11
|
+
import { BulkAccountLoader } from './bulkAccountLoader';
|
|
12
|
+
import { HighLeverageModeConfig } from '../types';
|
|
13
|
+
|
|
14
|
+
export class PollingHighLeverageModeConfigAccountSubscriber
|
|
15
|
+
implements HighLeverageModeConfigAccountSubscriber
|
|
16
|
+
{
|
|
17
|
+
isSubscribed: boolean;
|
|
18
|
+
program: Program;
|
|
19
|
+
eventEmitter: StrictEventEmitter<
|
|
20
|
+
EventEmitter,
|
|
21
|
+
HighLeverageModeConfigAccountEvents
|
|
22
|
+
>;
|
|
23
|
+
highLeverageModeConfigAccountPublicKey: PublicKey;
|
|
24
|
+
|
|
25
|
+
accountLoader: BulkAccountLoader;
|
|
26
|
+
callbackId?: string;
|
|
27
|
+
errorCallbackId?: string;
|
|
28
|
+
|
|
29
|
+
highLeverageModeConfigAccountAndSlot?: DataAndSlot<HighLeverageModeConfig>;
|
|
30
|
+
|
|
31
|
+
public constructor(
|
|
32
|
+
program: Program,
|
|
33
|
+
publicKey: PublicKey,
|
|
34
|
+
accountLoader: BulkAccountLoader
|
|
35
|
+
) {
|
|
36
|
+
this.isSubscribed = false;
|
|
37
|
+
this.program = program;
|
|
38
|
+
this.highLeverageModeConfigAccountPublicKey = publicKey;
|
|
39
|
+
this.accountLoader = accountLoader;
|
|
40
|
+
this.eventEmitter = new EventEmitter();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async subscribe(
|
|
44
|
+
highLeverageModeConfig?: HighLeverageModeConfig
|
|
45
|
+
): Promise<boolean> {
|
|
46
|
+
if (this.isSubscribed) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (highLeverageModeConfig) {
|
|
51
|
+
this.highLeverageModeConfigAccountAndSlot = {
|
|
52
|
+
data: highLeverageModeConfig,
|
|
53
|
+
slot: undefined,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await this.addToAccountLoader();
|
|
58
|
+
|
|
59
|
+
await this.fetchIfUnloaded();
|
|
60
|
+
|
|
61
|
+
if (this.doesAccountExist()) {
|
|
62
|
+
this.eventEmitter.emit('update');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.isSubscribed = true;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async addToAccountLoader(): Promise<void> {
|
|
70
|
+
if (this.callbackId) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.callbackId = await this.accountLoader.addAccount(
|
|
75
|
+
this.highLeverageModeConfigAccountPublicKey,
|
|
76
|
+
(buffer, slot: number) => {
|
|
77
|
+
if (!buffer) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (
|
|
82
|
+
this.highLeverageModeConfigAccountAndSlot &&
|
|
83
|
+
this.highLeverageModeConfigAccountAndSlot.slot > slot
|
|
84
|
+
) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const account = this.program.account.user.coder.accounts.decode(
|
|
89
|
+
'HighLeverageModeConfig',
|
|
90
|
+
buffer
|
|
91
|
+
);
|
|
92
|
+
this.highLeverageModeConfigAccountAndSlot = { data: account, slot };
|
|
93
|
+
this.eventEmitter.emit('highLeverageModeConfigAccountUpdate', account);
|
|
94
|
+
this.eventEmitter.emit('update');
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
|
|
99
|
+
this.eventEmitter.emit('error', error);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async fetchIfUnloaded(): Promise<void> {
|
|
104
|
+
if (this.highLeverageModeConfigAccountAndSlot === undefined) {
|
|
105
|
+
await this.fetch();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async fetch(): Promise<void> {
|
|
110
|
+
try {
|
|
111
|
+
const dataAndContext =
|
|
112
|
+
await this.program.account.highLeverageModeConfig.fetchAndContext(
|
|
113
|
+
this.highLeverageModeConfigAccountPublicKey,
|
|
114
|
+
this.accountLoader.commitment
|
|
115
|
+
);
|
|
116
|
+
if (
|
|
117
|
+
dataAndContext.context.slot >
|
|
118
|
+
(this.highLeverageModeConfigAccountAndSlot?.slot ?? 0)
|
|
119
|
+
) {
|
|
120
|
+
this.highLeverageModeConfigAccountAndSlot = {
|
|
121
|
+
data: dataAndContext.data as HighLeverageModeConfig,
|
|
122
|
+
slot: dataAndContext.context.slot,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.log(
|
|
127
|
+
`PollingHighLeverageModeConfigAccountSubscriber.fetch() HighLeverageModeConfig does not exist: ${e.message}`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
doesAccountExist(): boolean {
|
|
133
|
+
return this.highLeverageModeConfigAccountAndSlot !== undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async unsubscribe(): Promise<void> {
|
|
137
|
+
if (!this.isSubscribed) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
this.accountLoader.removeAccount(
|
|
142
|
+
this.highLeverageModeConfigAccountPublicKey,
|
|
143
|
+
this.callbackId
|
|
144
|
+
);
|
|
145
|
+
this.callbackId = undefined;
|
|
146
|
+
|
|
147
|
+
this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
|
|
148
|
+
this.errorCallbackId = undefined;
|
|
149
|
+
|
|
150
|
+
this.isSubscribed = false;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
assertIsSubscribed(): void {
|
|
154
|
+
if (!this.isSubscribed) {
|
|
155
|
+
throw new NotSubscribedError(
|
|
156
|
+
'You must call `subscribe` before using this function'
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
public getHighLeverageModeConfigAccountAndSlot(): DataAndSlot<HighLeverageModeConfig> {
|
|
162
|
+
this.assertIsSubscribed();
|
|
163
|
+
return this.highLeverageModeConfigAccountAndSlot;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
didSubscriptionSucceed(): boolean {
|
|
167
|
+
return !!this.highLeverageModeConfigAccountAndSlot;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public updateData(
|
|
171
|
+
highLeverageModeConfig: HighLeverageModeConfig,
|
|
172
|
+
slot: number
|
|
173
|
+
): void {
|
|
174
|
+
if (
|
|
175
|
+
!this.highLeverageModeConfigAccountAndSlot ||
|
|
176
|
+
this.highLeverageModeConfigAccountAndSlot.slot < slot
|
|
177
|
+
) {
|
|
178
|
+
this.highLeverageModeConfigAccountAndSlot = {
|
|
179
|
+
data: highLeverageModeConfig,
|
|
180
|
+
slot,
|
|
181
|
+
};
|
|
182
|
+
this.eventEmitter.emit(
|
|
183
|
+
'highLeverageModeConfigAccountUpdate',
|
|
184
|
+
highLeverageModeConfig
|
|
185
|
+
);
|
|
186
|
+
this.eventEmitter.emit('update');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
package/src/accounts/types.ts
CHANGED
|
@@ -11,7 +11,7 @@ import StrictEventEmitter from 'strict-event-emitter-types';
|
|
|
11
11
|
import { EventEmitter } from 'events';
|
|
12
12
|
import { Context, PublicKey } from '@solana/web3.js';
|
|
13
13
|
import { Account } from '@solana/spl-token';
|
|
14
|
-
import { OracleInfo, OraclePriceData } from '..';
|
|
14
|
+
import { HighLeverageModeConfig, OracleInfo, OraclePriceData } from '..';
|
|
15
15
|
import { ChannelOptions, CommitmentLevel } from '../isomorphic/grpc';
|
|
16
16
|
|
|
17
17
|
export interface AccountSubscriber<T> {
|
|
@@ -215,3 +215,27 @@ export type GrpcConfigs = {
|
|
|
215
215
|
commitmentLevel?: CommitmentLevel;
|
|
216
216
|
channelOptions?: ChannelOptions;
|
|
217
217
|
};
|
|
218
|
+
|
|
219
|
+
export interface HighLeverageModeConfigAccountSubscriber {
|
|
220
|
+
eventEmitter: StrictEventEmitter<
|
|
221
|
+
EventEmitter,
|
|
222
|
+
HighLeverageModeConfigAccountEvents
|
|
223
|
+
>;
|
|
224
|
+
isSubscribed: boolean;
|
|
225
|
+
|
|
226
|
+
subscribe(
|
|
227
|
+
highLeverageModeConfigAccount?: HighLeverageModeConfig
|
|
228
|
+
): Promise<boolean>;
|
|
229
|
+
fetch(): Promise<void>;
|
|
230
|
+
unsubscribe(): Promise<void>;
|
|
231
|
+
|
|
232
|
+
getHighLeverageModeConfigAccountAndSlot(): DataAndSlot<HighLeverageModeConfig>;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface HighLeverageModeConfigAccountEvents {
|
|
236
|
+
highLeverageModeConfigAccountUpdate: (
|
|
237
|
+
payload: HighLeverageModeConfig
|
|
238
|
+
) => void;
|
|
239
|
+
update: void;
|
|
240
|
+
error: (e: Error) => void;
|
|
241
|
+
}
|