@drift-labs/sdk 2.25.4 → 2.26.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/examples/phoenix.ts +40 -0
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +5 -4
- package/lib/accounts/pollingUserAccountSubscriber.js +35 -48
- package/lib/accounts/pollingUserStatsAccountSubscriber.d.ts +4 -4
- package/lib/accounts/pollingUserStatsAccountSubscriber.js +30 -47
- package/lib/accounts/types.d.ts +2 -1
- package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
- package/lib/accounts/webSocketAccountSubscriber.js +4 -3
- package/lib/accounts/webSocketUserAccountSubscriber.d.ts +1 -0
- package/lib/accounts/webSocketUserAccountSubscriber.js +9 -0
- package/lib/addresses/pda.d.ts +1 -0
- package/lib/addresses/pda.js +8 -1
- package/lib/adminClient.d.ts +1 -0
- package/lib/adminClient.js +20 -0
- package/lib/dlob/DLOBApiClient.d.ts +14 -0
- package/lib/dlob/DLOBApiClient.js +34 -0
- package/lib/dlob/DLOBSubscriber.d.ts +19 -0
- package/lib/dlob/DLOBSubscriber.js +42 -0
- package/lib/dlob/types.d.ts +16 -0
- package/lib/dlob/types.js +2 -0
- package/lib/driftClient.d.ts +13 -10
- package/lib/driftClient.js +124 -28
- package/lib/examples/loadDlob.js +2 -4
- package/lib/idl/drift.json +264 -82
- package/lib/index.d.ts +4 -0
- package/lib/index.js +4 -0
- package/lib/phoenix/phoenixFulfillmentConfigMap.d.ts +10 -0
- package/lib/phoenix/phoenixFulfillmentConfigMap.js +17 -0
- package/lib/phoenix/phoenixSubscriber.d.ts +34 -0
- package/lib/phoenix/phoenixSubscriber.js +79 -0
- package/lib/slot/SlotSubscriber.js +3 -0
- package/lib/tx/retryTxSender.d.ts +2 -1
- package/lib/tx/retryTxSender.js +5 -1
- package/lib/tx/types.d.ts +4 -1
- package/lib/types.d.ts +11 -0
- package/lib/userMap/userMap.d.ts +17 -6
- package/lib/userMap/userMap.js +74 -46
- package/lib/userMap/userStatsMap.d.ts +6 -3
- package/lib/userMap/userStatsMap.js +30 -30
- package/package.json +2 -1
- package/src/accounts/pollingUserAccountSubscriber.ts +51 -63
- package/src/accounts/pollingUserStatsAccountSubscriber.ts +46 -62
- package/src/accounts/types.ts +2 -1
- package/src/accounts/webSocketAccountSubscriber.ts +4 -3
- package/src/accounts/webSocketUserAccountSubscriber.ts +10 -0
- package/src/addresses/pda.ts +13 -0
- package/src/adminClient.ts +34 -0
- package/src/dlob/DLOBApiClient.ts +37 -0
- package/src/dlob/DLOBSubscriber.ts +57 -0
- package/src/dlob/types.ts +20 -0
- package/src/driftClient.ts +216 -59
- package/src/examples/loadDlob.ts +2 -4
- package/src/idl/drift.json +264 -82
- package/src/index.ts +4 -0
- package/src/phoenix/phoenixFulfillmentConfigMap.ts +26 -0
- package/src/phoenix/phoenixSubscriber.ts +156 -0
- package/src/slot/SlotSubscriber.ts +4 -0
- package/src/tx/retryTxSender.ts +19 -2
- package/src/tx/types.ts +13 -0
- package/src/types.ts +12 -0
- package/src/userMap/userMap.ts +111 -60
- package/src/userMap/userStatsMap.ts +43 -44
- package/tests/dlob/helpers.ts +3 -1
- package/src/assert/assert.js +0 -9
- package/src/token/index.js +0 -38
- package/src/util/computeUnits.js +0 -27
- package/src/util/getTokenAddress.js +0 -9
- package/src/util/promiseTimeout.js +0 -14
- package/src/util/tps.js +0 -27
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
2
|
+
import { PRICE_PRECISION, PhoenixSubscriber } from '../src';
|
|
3
|
+
import { PROGRAM_ID } from '@ellipsis-labs/phoenix-sdk';
|
|
4
|
+
|
|
5
|
+
export async function listenToBook(): Promise<void> {
|
|
6
|
+
const connection = new Connection('https://api.mainnet-beta.solana.com');
|
|
7
|
+
|
|
8
|
+
const phoenixSubscriber = new PhoenixSubscriber({
|
|
9
|
+
connection,
|
|
10
|
+
programId: PROGRAM_ID,
|
|
11
|
+
marketAddress: new PublicKey(
|
|
12
|
+
'4DoNfFBfF7UokCC2FQzriy7yHK6DY6NVdYpuekQ5pRgg'
|
|
13
|
+
),
|
|
14
|
+
accountSubscription: {
|
|
15
|
+
type: 'websocket',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
await phoenixSubscriber.subscribe();
|
|
20
|
+
|
|
21
|
+
for (let i = 0; i < 10; i++) {
|
|
22
|
+
const bid = phoenixSubscriber.getBestBid().toNumber() / PRICE_PRECISION;
|
|
23
|
+
const ask = phoenixSubscriber.getBestAsk().toNumber() / PRICE_PRECISION;
|
|
24
|
+
console.log(`iter ${i}:`, bid.toFixed(3), '@', ask.toFixed(3));
|
|
25
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
await phoenixSubscriber.unsubscribe();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
(async function () {
|
|
32
|
+
try {
|
|
33
|
+
await listenToBook();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.log('Error: ', err);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
process.exit(0);
|
|
40
|
+
})();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { DataAndSlot,
|
|
2
|
+
import { DataAndSlot, UserAccountEvents, UserAccountSubscriber } from './types';
|
|
3
3
|
import { Program } from '@coral-xyz/anchor';
|
|
4
4
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
@@ -12,16 +12,17 @@ export declare class PollingUserAccountSubscriber implements UserAccountSubscrib
|
|
|
12
12
|
eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
|
|
13
13
|
userAccountPublicKey: PublicKey;
|
|
14
14
|
accountLoader: BulkAccountLoader;
|
|
15
|
-
|
|
15
|
+
callbackId?: string;
|
|
16
16
|
errorCallbackId?: string;
|
|
17
17
|
user?: DataAndSlot<UserAccount>;
|
|
18
18
|
constructor(program: Program, userAccountPublicKey: PublicKey, accountLoader: BulkAccountLoader);
|
|
19
|
-
subscribe(): Promise<boolean>;
|
|
19
|
+
subscribe(userAccount?: UserAccount): Promise<boolean>;
|
|
20
20
|
addToAccountLoader(): Promise<void>;
|
|
21
21
|
fetchIfUnloaded(): Promise<void>;
|
|
22
22
|
fetch(): Promise<void>;
|
|
23
|
-
|
|
23
|
+
doesAccountExist(): boolean;
|
|
24
24
|
unsubscribe(): Promise<void>;
|
|
25
25
|
assertIsSubscribed(): void;
|
|
26
26
|
getUserAccountAndSlot(): DataAndSlot<UserAccount>;
|
|
27
|
+
updateData(userAccount: UserAccount, slot: number): void;
|
|
27
28
|
}
|
|
@@ -3,95 +3,75 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PollingUserAccountSubscriber = void 0;
|
|
4
4
|
const types_1 = require("./types");
|
|
5
5
|
const events_1 = require("events");
|
|
6
|
-
const utils_1 = require("./utils");
|
|
7
6
|
class PollingUserAccountSubscriber {
|
|
8
7
|
constructor(program, userAccountPublicKey, accountLoader) {
|
|
9
|
-
this.accountsToPoll = new Map();
|
|
10
8
|
this.isSubscribed = false;
|
|
11
9
|
this.program = program;
|
|
12
10
|
this.accountLoader = accountLoader;
|
|
13
11
|
this.eventEmitter = new events_1.EventEmitter();
|
|
14
12
|
this.userAccountPublicKey = userAccountPublicKey;
|
|
15
13
|
}
|
|
16
|
-
async subscribe() {
|
|
14
|
+
async subscribe(userAccount) {
|
|
17
15
|
if (this.isSubscribed) {
|
|
18
16
|
return true;
|
|
19
17
|
}
|
|
18
|
+
if (userAccount) {
|
|
19
|
+
this.user = { data: userAccount, slot: undefined };
|
|
20
|
+
}
|
|
20
21
|
await this.addToAccountLoader();
|
|
21
22
|
await this.fetchIfUnloaded();
|
|
22
|
-
if (this.
|
|
23
|
+
if (this.doesAccountExist()) {
|
|
23
24
|
this.eventEmitter.emit('update');
|
|
24
25
|
}
|
|
25
26
|
this.isSubscribed = true;
|
|
26
27
|
return true;
|
|
27
28
|
}
|
|
28
29
|
async addToAccountLoader() {
|
|
29
|
-
if (this.
|
|
30
|
+
if (this.callbackId) {
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
|
-
this.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
this.callbackId = await this.accountLoader.addAccount(this.userAccountPublicKey, (buffer, slot) => {
|
|
34
|
+
if (!buffer) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (this.user && this.user.slot > slot) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const account = this.program.account.user.coder.accounts.decode('User', buffer);
|
|
41
|
+
this.user = { data: account, slot };
|
|
42
|
+
this.eventEmitter.emit('userAccountUpdate', account);
|
|
43
|
+
this.eventEmitter.emit('update');
|
|
36
44
|
});
|
|
37
|
-
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
38
|
-
accountToPoll.callbackId = await this.accountLoader.addAccount(accountToPoll.publicKey, (buffer, slot) => {
|
|
39
|
-
if (!buffer) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const account = this.program.account[accountToPoll.key].coder.accounts.decode((0, utils_1.capitalize)(accountToPoll.key), buffer);
|
|
43
|
-
this[accountToPoll.key] = { data: account, slot };
|
|
44
|
-
// @ts-ignore
|
|
45
|
-
this.eventEmitter.emit(accountToPoll.eventType, account);
|
|
46
|
-
this.eventEmitter.emit('update');
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
45
|
this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
|
|
50
46
|
this.eventEmitter.emit('error', error);
|
|
51
47
|
});
|
|
52
48
|
}
|
|
53
49
|
async fetchIfUnloaded() {
|
|
54
|
-
|
|
55
|
-
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
56
|
-
if (this[accountToPoll.key] === undefined) {
|
|
57
|
-
shouldFetch = true;
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (shouldFetch) {
|
|
50
|
+
if (this.user === undefined) {
|
|
62
51
|
await this.fetch();
|
|
63
52
|
}
|
|
64
53
|
}
|
|
65
54
|
async fetch() {
|
|
55
|
+
var _a, _b;
|
|
66
56
|
await this.accountLoader.load();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
57
|
+
const { buffer, slot } = this.accountLoader.getBufferAndSlot(this.userAccountPublicKey);
|
|
58
|
+
const currentSlot = (_b = (_a = this.user) === null || _a === void 0 ? void 0 : _a.slot) !== null && _b !== void 0 ? _b : 0;
|
|
59
|
+
if (buffer && slot > currentSlot) {
|
|
60
|
+
const account = this.program.account.user.coder.accounts.decode('User', buffer);
|
|
61
|
+
this.user = { data: account, slot };
|
|
73
62
|
}
|
|
74
63
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
78
|
-
if (!this[accountToPoll.key]) {
|
|
79
|
-
success = false;
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return success;
|
|
64
|
+
doesAccountExist() {
|
|
65
|
+
return this.user !== undefined;
|
|
84
66
|
}
|
|
85
67
|
async unsubscribe() {
|
|
86
68
|
if (!this.isSubscribed) {
|
|
87
69
|
return;
|
|
88
70
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
71
|
+
this.accountLoader.removeAccount(this.userAccountPublicKey, this.callbackId);
|
|
72
|
+
this.callbackId = undefined;
|
|
92
73
|
this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
|
|
93
74
|
this.errorCallbackId = undefined;
|
|
94
|
-
this.accountsToPoll.clear();
|
|
95
75
|
this.isSubscribed = false;
|
|
96
76
|
}
|
|
97
77
|
assertIsSubscribed() {
|
|
@@ -103,5 +83,12 @@ class PollingUserAccountSubscriber {
|
|
|
103
83
|
this.assertIsSubscribed();
|
|
104
84
|
return this.user;
|
|
105
85
|
}
|
|
86
|
+
updateData(userAccount, slot) {
|
|
87
|
+
if (!this.user || this.user.slot < slot) {
|
|
88
|
+
this.user = { data: userAccount, slot };
|
|
89
|
+
this.eventEmitter.emit('userAccountUpdate', userAccount);
|
|
90
|
+
this.eventEmitter.emit('update');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
106
93
|
}
|
|
107
94
|
exports.PollingUserAccountSubscriber = PollingUserAccountSubscriber;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { DataAndSlot,
|
|
2
|
+
import { DataAndSlot, UserStatsAccountSubscriber, UserStatsAccountEvents } from './types';
|
|
3
3
|
import { Program } from '@coral-xyz/anchor';
|
|
4
4
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
@@ -12,15 +12,15 @@ export declare class PollingUserStatsAccountSubscriber implements UserStatsAccou
|
|
|
12
12
|
eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
|
|
13
13
|
userStatsAccountPublicKey: PublicKey;
|
|
14
14
|
accountLoader: BulkAccountLoader;
|
|
15
|
-
|
|
15
|
+
callbackId?: string;
|
|
16
16
|
errorCallbackId?: string;
|
|
17
17
|
userStats?: DataAndSlot<UserStatsAccount>;
|
|
18
18
|
constructor(program: Program, userStatsAccountPublicKey: PublicKey, accountLoader: BulkAccountLoader);
|
|
19
|
-
subscribe(): Promise<boolean>;
|
|
19
|
+
subscribe(userStatsAccount?: UserStatsAccount): Promise<boolean>;
|
|
20
20
|
addToAccountLoader(): Promise<void>;
|
|
21
21
|
fetchIfUnloaded(): Promise<void>;
|
|
22
22
|
fetch(): Promise<void>;
|
|
23
|
-
|
|
23
|
+
doesAccountExist(): boolean;
|
|
24
24
|
unsubscribe(): Promise<void>;
|
|
25
25
|
assertIsSubscribed(): void;
|
|
26
26
|
getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount>;
|
|
@@ -3,92 +3,75 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.PollingUserStatsAccountSubscriber = void 0;
|
|
4
4
|
const types_1 = require("./types");
|
|
5
5
|
const events_1 = require("events");
|
|
6
|
-
const utils_1 = require("./utils");
|
|
7
6
|
class PollingUserStatsAccountSubscriber {
|
|
8
7
|
constructor(program, userStatsAccountPublicKey, accountLoader) {
|
|
9
|
-
this.accountsToPoll = new Map();
|
|
10
8
|
this.isSubscribed = false;
|
|
11
9
|
this.program = program;
|
|
12
10
|
this.accountLoader = accountLoader;
|
|
13
11
|
this.eventEmitter = new events_1.EventEmitter();
|
|
14
12
|
this.userStatsAccountPublicKey = userStatsAccountPublicKey;
|
|
15
13
|
}
|
|
16
|
-
async subscribe() {
|
|
14
|
+
async subscribe(userStatsAccount) {
|
|
17
15
|
if (this.isSubscribed) {
|
|
18
16
|
return true;
|
|
19
17
|
}
|
|
18
|
+
if (userStatsAccount) {
|
|
19
|
+
this.userStats = { data: userStatsAccount, slot: undefined };
|
|
20
|
+
}
|
|
20
21
|
await this.addToAccountLoader();
|
|
21
22
|
await this.fetchIfUnloaded();
|
|
22
|
-
if (this.
|
|
23
|
+
if (this.doesAccountExist()) {
|
|
23
24
|
this.eventEmitter.emit('update');
|
|
24
25
|
}
|
|
25
26
|
this.isSubscribed = true;
|
|
26
27
|
return true;
|
|
27
28
|
}
|
|
28
29
|
async addToAccountLoader() {
|
|
29
|
-
if (this.
|
|
30
|
+
if (this.callbackId !== undefined) {
|
|
30
31
|
return;
|
|
31
32
|
}
|
|
32
|
-
this.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
this.callbackId = await this.accountLoader.addAccount(this.userStatsAccountPublicKey, (buffer, slot) => {
|
|
34
|
+
if (!buffer) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (this.userStats && this.userStats.slot > slot) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const account = this.program.account.userStats.coder.accounts.decode('UserStats', buffer);
|
|
41
|
+
this.userStats = { data: account, slot };
|
|
42
|
+
this.eventEmitter.emit('userStatsAccountUpdate', account);
|
|
43
|
+
this.eventEmitter.emit('update');
|
|
36
44
|
});
|
|
37
|
-
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
38
|
-
accountToPoll.callbackId = await this.accountLoader.addAccount(accountToPoll.publicKey, (buffer, slot) => {
|
|
39
|
-
if (!buffer) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const account = this.program.account[accountToPoll.key].coder.accounts.decode((0, utils_1.capitalize)(accountToPoll.key), buffer);
|
|
43
|
-
this[accountToPoll.key] = { data: account, slot };
|
|
44
|
-
// @ts-ignore
|
|
45
|
-
this.eventEmitter.emit(accountToPoll.eventType, account);
|
|
46
|
-
this.eventEmitter.emit('update');
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
45
|
this.errorCallbackId = this.accountLoader.addErrorCallbacks((error) => {
|
|
50
46
|
this.eventEmitter.emit('error', error);
|
|
51
47
|
});
|
|
52
48
|
}
|
|
53
49
|
async fetchIfUnloaded() {
|
|
54
|
-
|
|
55
|
-
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
56
|
-
if (this[accountToPoll.key] === undefined) {
|
|
57
|
-
shouldFetch = true;
|
|
58
|
-
break;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
if (shouldFetch) {
|
|
50
|
+
if (this.userStats === undefined) {
|
|
62
51
|
await this.fetch();
|
|
63
52
|
}
|
|
64
53
|
}
|
|
65
54
|
async fetch() {
|
|
55
|
+
var _a, _b;
|
|
66
56
|
await this.accountLoader.load();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
57
|
+
const { buffer, slot } = this.accountLoader.getBufferAndSlot(this.userStatsAccountPublicKey);
|
|
58
|
+
const currentSlot = (_b = (_a = this.userStats) === null || _a === void 0 ? void 0 : _a.slot) !== null && _b !== void 0 ? _b : 0;
|
|
59
|
+
if (buffer && slot > currentSlot) {
|
|
60
|
+
const account = this.program.account.userStats.coder.accounts.decode('UserStats', buffer);
|
|
61
|
+
this.userStats = { data: account, slot };
|
|
73
62
|
}
|
|
74
63
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
78
|
-
if (!this[accountToPoll.key]) {
|
|
79
|
-
success = false;
|
|
80
|
-
break;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return success;
|
|
64
|
+
doesAccountExist() {
|
|
65
|
+
return this.userStats !== undefined;
|
|
84
66
|
}
|
|
85
67
|
async unsubscribe() {
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
if (!this.isSubscribed) {
|
|
69
|
+
return;
|
|
88
70
|
}
|
|
71
|
+
this.accountLoader.removeAccount(this.userStatsAccountPublicKey, this.callbackId);
|
|
72
|
+
this.callbackId = undefined;
|
|
89
73
|
this.accountLoader.removeErrorCallbacks(this.errorCallbackId);
|
|
90
74
|
this.errorCallbackId = undefined;
|
|
91
|
-
this.accountsToPoll.clear();
|
|
92
75
|
this.isSubscribed = false;
|
|
93
76
|
}
|
|
94
77
|
assertIsSubscribed() {
|
package/lib/accounts/types.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface AccountSubscriber<T> {
|
|
|
11
11
|
subscribe(onChange: (data: T) => void): Promise<void>;
|
|
12
12
|
fetch(): Promise<void>;
|
|
13
13
|
unsubscribe(): Promise<void>;
|
|
14
|
-
setData(userAccount: T): void;
|
|
14
|
+
setData(userAccount: T, slot?: number): void;
|
|
15
15
|
}
|
|
16
16
|
export declare class NotSubscribedError extends Error {
|
|
17
17
|
name: string;
|
|
@@ -51,6 +51,7 @@ export interface UserAccountSubscriber {
|
|
|
51
51
|
isSubscribed: boolean;
|
|
52
52
|
subscribe(userAccount?: UserAccount): Promise<boolean>;
|
|
53
53
|
fetch(): Promise<void>;
|
|
54
|
+
updateData(userAccount: UserAccount, slot: number): void;
|
|
54
55
|
unsubscribe(): Promise<void>;
|
|
55
56
|
getUserAccountAndSlot(): DataAndSlot<UserAccount>;
|
|
56
57
|
}
|
|
@@ -13,7 +13,7 @@ export declare class WebSocketAccountSubscriber<T> implements AccountSubscriber<
|
|
|
13
13
|
listenerId?: number;
|
|
14
14
|
constructor(accountName: string, program: Program, accountPublicKey: PublicKey, decodeBuffer?: (buffer: Buffer) => T);
|
|
15
15
|
subscribe(onChange: (data: T) => void): Promise<void>;
|
|
16
|
-
setData(data: T): void;
|
|
16
|
+
setData(data: T, slot?: number): void;
|
|
17
17
|
fetch(): Promise<void>;
|
|
18
18
|
handleRpcResponse(context: Context, accountInfo?: AccountInfo<Buffer>): void;
|
|
19
19
|
decodeBuffer(buffer: Buffer): T;
|
|
@@ -21,13 +21,14 @@ class WebSocketAccountSubscriber {
|
|
|
21
21
|
this.handleRpcResponse(context, accountInfo);
|
|
22
22
|
}, this.program.provider.opts.commitment);
|
|
23
23
|
}
|
|
24
|
-
setData(data) {
|
|
25
|
-
|
|
24
|
+
setData(data, slot) {
|
|
25
|
+
const newSlot = slot || 0;
|
|
26
|
+
if (this.dataAndSlot && this.dataAndSlot.slot > newSlot) {
|
|
26
27
|
return;
|
|
27
28
|
}
|
|
28
29
|
this.dataAndSlot = {
|
|
29
30
|
data,
|
|
30
|
-
slot
|
|
31
|
+
slot,
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
34
|
async fetch() {
|
|
@@ -46,5 +46,14 @@ class WebSocketUserAccountSubscriber {
|
|
|
46
46
|
this.assertIsSubscribed();
|
|
47
47
|
return this.userDataAccountSubscriber.dataAndSlot;
|
|
48
48
|
}
|
|
49
|
+
updateData(userAccount, slot) {
|
|
50
|
+
var _a;
|
|
51
|
+
const currentDataSlot = ((_a = this.userDataAccountSubscriber.dataAndSlot) === null || _a === void 0 ? void 0 : _a.slot) || 0;
|
|
52
|
+
if (currentDataSlot < slot) {
|
|
53
|
+
this.userDataAccountSubscriber.setData(userAccount, slot);
|
|
54
|
+
this.eventEmitter.emit('userAccountUpdate', userAccount);
|
|
55
|
+
this.eventEmitter.emit('update');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
49
58
|
}
|
|
50
59
|
exports.WebSocketUserAccountSubscriber = WebSocketUserAccountSubscriber;
|
package/lib/addresses/pda.d.ts
CHANGED
|
@@ -15,4 +15,5 @@ export declare function getDriftSignerPublicKey(programId: PublicKey): PublicKey
|
|
|
15
15
|
export declare function getSerumOpenOrdersPublicKey(programId: PublicKey, market: PublicKey): PublicKey;
|
|
16
16
|
export declare function getSerumSignerPublicKey(programId: PublicKey, market: PublicKey, nonce: BN): PublicKey;
|
|
17
17
|
export declare function getSerumFulfillmentConfigPublicKey(programId: PublicKey, market: PublicKey): PublicKey;
|
|
18
|
+
export declare function getPhoenixFulfillmentConfigPublicKey(programId: PublicKey, market: PublicKey): PublicKey;
|
|
18
19
|
export declare function getReferrerNamePublicKeySync(programId: PublicKey, nameBuffer: number[]): PublicKey;
|
package/lib/addresses/pda.js
CHANGED
|
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.getReferrerNamePublicKeySync = exports.getSerumFulfillmentConfigPublicKey = exports.getSerumSignerPublicKey = exports.getSerumOpenOrdersPublicKey = exports.getDriftSignerPublicKey = exports.getInsuranceFundStakeAccountPublicKey = exports.getInsuranceFundVaultPublicKey = exports.getSpotMarketVaultPublicKey = exports.getSpotMarketPublicKey = exports.getPerpMarketPublicKey = exports.getUserStatsAccountPublicKey = exports.getUserAccountPublicKeySync = exports.getUserAccountPublicKey = exports.getUserAccountPublicKeyAndNonce = exports.getDriftStateAccountPublicKey = exports.getDriftStateAccountPublicKeyAndNonce = void 0;
|
|
26
|
+
exports.getReferrerNamePublicKeySync = exports.getPhoenixFulfillmentConfigPublicKey = exports.getSerumFulfillmentConfigPublicKey = exports.getSerumSignerPublicKey = exports.getSerumOpenOrdersPublicKey = exports.getDriftSignerPublicKey = exports.getInsuranceFundStakeAccountPublicKey = exports.getInsuranceFundVaultPublicKey = exports.getSpotMarketVaultPublicKey = exports.getSpotMarketPublicKey = exports.getPerpMarketPublicKey = exports.getUserStatsAccountPublicKey = exports.getUserAccountPublicKeySync = exports.getUserAccountPublicKey = exports.getUserAccountPublicKeyAndNonce = exports.getDriftStateAccountPublicKey = exports.getDriftStateAccountPublicKeyAndNonce = void 0;
|
|
27
27
|
const web3_js_1 = require("@solana/web3.js");
|
|
28
28
|
const anchor = __importStar(require("@coral-xyz/anchor"));
|
|
29
29
|
async function getDriftStateAccountPublicKeyAndNonce(programId) {
|
|
@@ -119,6 +119,13 @@ function getSerumFulfillmentConfigPublicKey(programId, market) {
|
|
|
119
119
|
], programId)[0];
|
|
120
120
|
}
|
|
121
121
|
exports.getSerumFulfillmentConfigPublicKey = getSerumFulfillmentConfigPublicKey;
|
|
122
|
+
function getPhoenixFulfillmentConfigPublicKey(programId, market) {
|
|
123
|
+
return web3_js_1.PublicKey.findProgramAddressSync([
|
|
124
|
+
Buffer.from(anchor.utils.bytes.utf8.encode('phoenix_fulfillment_config')),
|
|
125
|
+
market.toBuffer(),
|
|
126
|
+
], programId)[0];
|
|
127
|
+
}
|
|
128
|
+
exports.getPhoenixFulfillmentConfigPublicKey = getPhoenixFulfillmentConfigPublicKey;
|
|
122
129
|
function getReferrerNamePublicKeySync(programId, nameBuffer) {
|
|
123
130
|
return web3_js_1.PublicKey.findProgramAddressSync([
|
|
124
131
|
Buffer.from(anchor.utils.bytes.utf8.encode('referrer_name')),
|
package/lib/adminClient.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare class AdminClient extends DriftClient {
|
|
|
6
6
|
initialize(usdcMint: PublicKey, _adminControlsPrices: boolean): Promise<[TransactionSignature]>;
|
|
7
7
|
initializeSpotMarket(mint: PublicKey, optimalUtilization: number, optimalRate: number, maxRate: number, oracle: PublicKey, oracleSource: OracleSource, initialAssetWeight: number, maintenanceAssetWeight: number, initialLiabilityWeight: number, maintenanceLiabilityWeight: number, imfFactor?: number, liquidatorFee?: number, activeStatus?: boolean, name?: string): Promise<TransactionSignature>;
|
|
8
8
|
initializeSerumFulfillmentConfig(marketIndex: number, serumMarket: PublicKey, serumProgram: PublicKey): Promise<TransactionSignature>;
|
|
9
|
+
initializePhoenixFulfillmentConfig(marketIndex: number, phoenixMarket: PublicKey): Promise<TransactionSignature>;
|
|
9
10
|
initializePerpMarket(marketIndex: number, priceOracle: PublicKey, baseAssetReserve: BN, quoteAssetReserve: BN, periodicity: BN, pegMultiplier?: BN, oracleSource?: OracleSource, marginRatioInitial?: number, marginRatioMaintenance?: number, liquidatorFee?: number, activeStatus?: boolean, name?: string): Promise<TransactionSignature>;
|
|
10
11
|
deleteInitializedPerpMarket(marketIndex: number): Promise<TransactionSignature>;
|
|
11
12
|
moveAmmPrice(perpMarketIndex: number, baseAssetReserve: BN, quoteAssetReserve: BN, sqrtK?: BN): Promise<TransactionSignature>;
|
package/lib/adminClient.js
CHANGED
|
@@ -36,6 +36,7 @@ const driftClient_1 = require("./driftClient");
|
|
|
36
36
|
const numericConstants_1 = require("./constants/numericConstants");
|
|
37
37
|
const trade_1 = require("./math/trade");
|
|
38
38
|
const amm_1 = require("./math/amm");
|
|
39
|
+
const phoenix_sdk_1 = require("@ellipsis-labs/phoenix-sdk");
|
|
39
40
|
class AdminClient extends driftClient_1.DriftClient {
|
|
40
41
|
async initialize(usdcMint, _adminControlsPrices) {
|
|
41
42
|
const stateAccountRPCResponse = await this.connection.getParsedAccountInfo(await this.getStatePublicKey());
|
|
@@ -107,6 +108,25 @@ class AdminClient extends driftClient_1.DriftClient {
|
|
|
107
108
|
const { txSig } = await this.sendTransaction(tx, [], this.opts);
|
|
108
109
|
return txSig;
|
|
109
110
|
}
|
|
111
|
+
async initializePhoenixFulfillmentConfig(marketIndex, phoenixMarket) {
|
|
112
|
+
const phoenixFulfillmentConfig = (0, pda_1.getPhoenixFulfillmentConfigPublicKey)(this.program.programId, phoenixMarket);
|
|
113
|
+
const tx = await this.program.transaction.initializePhoenixFulfillmentConfig(marketIndex, {
|
|
114
|
+
accounts: {
|
|
115
|
+
admin: this.wallet.publicKey,
|
|
116
|
+
state: await this.getStatePublicKey(),
|
|
117
|
+
baseSpotMarket: this.getSpotMarketAccount(marketIndex).pubkey,
|
|
118
|
+
quoteSpotMarket: this.getQuoteSpotMarketAccount().pubkey,
|
|
119
|
+
driftSigner: this.getSignerPublicKey(),
|
|
120
|
+
phoenixMarket: phoenixMarket,
|
|
121
|
+
phoenixProgram: phoenix_sdk_1.PROGRAM_ID,
|
|
122
|
+
rent: web3_js_1.SYSVAR_RENT_PUBKEY,
|
|
123
|
+
systemProgram: anchor.web3.SystemProgram.programId,
|
|
124
|
+
phoenixFulfillmentConfig,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
const { txSig } = await this.sendTransaction(tx, [], this.opts);
|
|
128
|
+
return txSig;
|
|
129
|
+
}
|
|
110
130
|
async initializePerpMarket(marketIndex, priceOracle, baseAssetReserve, quoteAssetReserve, periodicity, pegMultiplier = numericConstants_1.PEG_PRECISION, oracleSource = types_1.OracleSource.PYTH, marginRatioInitial = 2000, marginRatioMaintenance = 500, liquidatorFee = 0, activeStatus = true, name = userName_1.DEFAULT_MARKET_NAME) {
|
|
111
131
|
const currentPerpMarketIndex = this.getStateAccount().numberOfMarkets;
|
|
112
132
|
const perpMarketPublicKey = await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, currentPerpMarketIndex);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DLOBOrdersCoder } from './DLOBOrders';
|
|
2
|
+
import { DLOB } from './DLOB';
|
|
3
|
+
type DLOBApiClientConfig = {
|
|
4
|
+
url: string;
|
|
5
|
+
};
|
|
6
|
+
export declare class DLOBApiClient {
|
|
7
|
+
url: string;
|
|
8
|
+
dlobCoder: DLOBOrdersCoder;
|
|
9
|
+
lastSeenDLOB: DLOB;
|
|
10
|
+
lastSeenSlot: number;
|
|
11
|
+
constructor(config: DLOBApiClientConfig);
|
|
12
|
+
getDLOB(slot: number): Promise<DLOB>;
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DLOBApiClient = void 0;
|
|
7
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
+
const DLOBOrders_1 = require("./DLOBOrders");
|
|
9
|
+
const DLOB_1 = require("./DLOB");
|
|
10
|
+
class DLOBApiClient {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.dlobCoder = DLOBOrders_1.DLOBOrdersCoder.create();
|
|
13
|
+
this.lastSeenSlot = 0;
|
|
14
|
+
this.url = config.url;
|
|
15
|
+
}
|
|
16
|
+
async getDLOB(slot) {
|
|
17
|
+
const r = await (0, node_fetch_1.default)(this.url);
|
|
18
|
+
if (!r.ok) {
|
|
19
|
+
throw new Error(`Failed to fetch DLOB from ${this.url}`);
|
|
20
|
+
}
|
|
21
|
+
const resp = await r.json();
|
|
22
|
+
const responseSlot = resp['slot'];
|
|
23
|
+
if (responseSlot > this.lastSeenSlot) {
|
|
24
|
+
const dlobOrdersBuffer = Buffer.from(resp['data'], 'base64');
|
|
25
|
+
const dlobOrders = this.dlobCoder.decode(Buffer.from(dlobOrdersBuffer));
|
|
26
|
+
const dlob = new DLOB_1.DLOB();
|
|
27
|
+
dlob.initFromOrders(dlobOrders, slot);
|
|
28
|
+
this.lastSeenDLOB = dlob;
|
|
29
|
+
this.lastSeenSlot = responseSlot;
|
|
30
|
+
}
|
|
31
|
+
return this.lastSeenDLOB;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.DLOBApiClient = DLOBApiClient;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { DLOB } from './DLOB';
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
5
|
+
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
6
|
+
import { DLOBSource, DLOBSubscriberEvents, DLOBSubscriptionConfig, SlotSource } from './types';
|
|
7
|
+
export declare class DLOBSubscriber {
|
|
8
|
+
dlobSource: DLOBSource;
|
|
9
|
+
slotSource: SlotSource;
|
|
10
|
+
updateFrequency: number;
|
|
11
|
+
intervalId?: NodeJS.Timeout;
|
|
12
|
+
dlob: DLOB;
|
|
13
|
+
eventEmitter: StrictEventEmitter<EventEmitter, DLOBSubscriberEvents>;
|
|
14
|
+
constructor(config: DLOBSubscriptionConfig);
|
|
15
|
+
subscribe(): Promise<void>;
|
|
16
|
+
updateDLOB(): Promise<void>;
|
|
17
|
+
getDLOB(): DLOB;
|
|
18
|
+
unsubscribe(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DLOBSubscriber = void 0;
|
|
4
|
+
const DLOB_1 = require("./DLOB");
|
|
5
|
+
const events_1 = require("events");
|
|
6
|
+
class DLOBSubscriber {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.dlob = new DLOB_1.DLOB();
|
|
9
|
+
this.dlobSource = config.dlobSource;
|
|
10
|
+
this.slotSource = config.slotSource;
|
|
11
|
+
this.updateFrequency = config.updateFrequency;
|
|
12
|
+
this.eventEmitter = new events_1.EventEmitter();
|
|
13
|
+
}
|
|
14
|
+
async subscribe() {
|
|
15
|
+
if (this.intervalId) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
await this.updateDLOB();
|
|
19
|
+
this.intervalId = setInterval(async () => {
|
|
20
|
+
try {
|
|
21
|
+
await this.updateDLOB();
|
|
22
|
+
this.eventEmitter.emit('update', this.dlob);
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
this.eventEmitter.emit('error', e);
|
|
26
|
+
}
|
|
27
|
+
}, this.updateFrequency);
|
|
28
|
+
}
|
|
29
|
+
async updateDLOB() {
|
|
30
|
+
this.dlob = await this.dlobSource.getDLOB(this.slotSource.getSlot());
|
|
31
|
+
}
|
|
32
|
+
getDLOB() {
|
|
33
|
+
return this.dlob;
|
|
34
|
+
}
|
|
35
|
+
async unsubscribe() {
|
|
36
|
+
if (this.intervalId) {
|
|
37
|
+
clearInterval(this.intervalId);
|
|
38
|
+
this.intervalId = undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.DLOBSubscriber = DLOBSubscriber;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DLOB } from './DLOB';
|
|
2
|
+
export type DLOBSubscriptionConfig = {
|
|
3
|
+
dlobSource: DLOBSource;
|
|
4
|
+
slotSource: SlotSource;
|
|
5
|
+
updateFrequency: number;
|
|
6
|
+
};
|
|
7
|
+
export interface DLOBSubscriberEvents {
|
|
8
|
+
update: (dlob: DLOB) => void;
|
|
9
|
+
error: (e: Error) => void;
|
|
10
|
+
}
|
|
11
|
+
export interface DLOBSource {
|
|
12
|
+
getDLOB(slot: number): Promise<DLOB>;
|
|
13
|
+
}
|
|
14
|
+
export interface SlotSource {
|
|
15
|
+
getSlot(): number;
|
|
16
|
+
}
|