@drift-labs/sdk 2.106.0-beta.1 → 2.106.0-beta.2

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 CHANGED
@@ -1 +1 @@
1
- 2.106.0-beta.1
1
+ 2.106.0-beta.2
@@ -86,6 +86,7 @@ export * from './oracles/pythClient';
86
86
  export * from './oracles/pythPullClient';
87
87
  export * from './oracles/pythLazerClient';
88
88
  export * from './oracles/switchboardOnDemandClient';
89
+ export * from './swift/swiftOrderSubscriber';
89
90
  export * from './tx/fastSingleTxSender';
90
91
  export * from './tx/retryTxSender';
91
92
  export * from './tx/whileValidTxSender';
@@ -109,6 +109,7 @@ __exportStar(require("./oracles/pythClient"), exports);
109
109
  __exportStar(require("./oracles/pythPullClient"), exports);
110
110
  __exportStar(require("./oracles/pythLazerClient"), exports);
111
111
  __exportStar(require("./oracles/switchboardOnDemandClient"), exports);
112
+ __exportStar(require("./swift/swiftOrderSubscriber"), exports);
112
113
  __exportStar(require("./tx/fastSingleTxSender"), exports);
113
114
  __exportStar(require("./tx/retryTxSender"), exports);
114
115
  __exportStar(require("./tx/whileValidTxSender"), exports);
@@ -0,0 +1,28 @@
1
+ import { DriftClient, DriftEnv, OptionalOrderParams, SwiftOrderParamsMessage, UserMap } from '..';
2
+ import { Keypair, TransactionInstruction } from '@solana/web3.js';
3
+ export type SwiftOrderSubscriberConfig = {
4
+ driftClient: DriftClient;
5
+ userMap: UserMap;
6
+ driftEnv: DriftEnv;
7
+ endpoint?: string;
8
+ marketIndexes: number[];
9
+ keypair: Keypair;
10
+ };
11
+ export declare class SwiftOrderSubscriber {
12
+ private config;
13
+ private onOrder;
14
+ private heartbeatTimeout;
15
+ private readonly heartbeatIntervalMs;
16
+ private ws;
17
+ private driftClient;
18
+ private userMap;
19
+ subscribed: boolean;
20
+ constructor(config: SwiftOrderSubscriberConfig, onOrder: (orderMessageRaw: any, swiftOrderParamsMessage: SwiftOrderParamsMessage) => Promise<void>);
21
+ getSymbolForMarketIndex(marketIndex: number): string;
22
+ generateChallengeResponse(nonce: string): string;
23
+ handleAuthMessage(message: any): void;
24
+ subscribe(): Promise<void>;
25
+ getPlaceAndMakeSwiftOrderIxs(orderMessageRaw: any, swiftOrderParamsMessage: SwiftOrderParamsMessage, makerOrderParams: OptionalOrderParams): Promise<TransactionInstruction[]>;
26
+ private startHeartbeatTimer;
27
+ private reconnect;
28
+ }
@@ -0,0 +1,129 @@
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.SwiftOrderSubscriber = void 0;
7
+ const __1 = require("..");
8
+ const web3_js_1 = require("@solana/web3.js");
9
+ const tweetnacl_1 = __importDefault(require("tweetnacl"));
10
+ const tweetnacl_util_1 = require("tweetnacl-util");
11
+ const ws_1 = __importDefault(require("ws"));
12
+ class SwiftOrderSubscriber {
13
+ constructor(config, onOrder) {
14
+ this.config = config;
15
+ this.onOrder = onOrder;
16
+ this.heartbeatTimeout = null;
17
+ this.heartbeatIntervalMs = 60000;
18
+ this.ws = null;
19
+ this.subscribed = false;
20
+ this.driftClient = config.driftClient;
21
+ this.userMap = config.userMap;
22
+ }
23
+ getSymbolForMarketIndex(marketIndex) {
24
+ const markets = this.config.driftEnv === 'devnet'
25
+ ? __1.DevnetPerpMarkets
26
+ : __1.MainnetPerpMarkets;
27
+ return markets[marketIndex].symbol;
28
+ }
29
+ generateChallengeResponse(nonce) {
30
+ const messageBytes = (0, tweetnacl_util_1.decodeUTF8)(nonce);
31
+ const signature = tweetnacl_1.default.sign.detached(messageBytes, this.config.keypair.secretKey);
32
+ const signatureBase64 = Buffer.from(signature).toString('base64');
33
+ return signatureBase64;
34
+ }
35
+ handleAuthMessage(message) {
36
+ var _a, _b;
37
+ if (message['channel'] === 'auth' && message['nonce'] != null) {
38
+ const signatureBase64 = this.generateChallengeResponse(message['nonce']);
39
+ (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({
40
+ pubkey: this.config.keypair.publicKey.toBase58(),
41
+ signature: signatureBase64,
42
+ }));
43
+ }
44
+ if (message['channel'] === 'auth' &&
45
+ ((_b = message['message']) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'authenticated') {
46
+ this.subscribed = true;
47
+ this.config.marketIndexes.forEach(async (marketIndex) => {
48
+ var _a;
49
+ (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({
50
+ action: 'subscribe',
51
+ market_type: 'perp',
52
+ market_name: this.getSymbolForMarketIndex(marketIndex),
53
+ }));
54
+ await new Promise((resolve) => setTimeout(resolve, 100));
55
+ });
56
+ }
57
+ }
58
+ async subscribe() {
59
+ const endpoint = this.config.endpoint || this.config.driftEnv === 'devnet'
60
+ ? 'wss://master.swift.drift.trade/ws'
61
+ : 'wss://swift.drift.trade/ws';
62
+ const ws = new ws_1.default(endpoint + '?pubkey=' + this.config.keypair.publicKey.toBase58());
63
+ this.ws = ws;
64
+ ws.on('open', async () => {
65
+ console.log('Connected to the server');
66
+ ws.on('message', async (data) => {
67
+ const message = JSON.parse(data.toString());
68
+ this.startHeartbeatTimer();
69
+ if (message['channel'] === 'auth') {
70
+ this.handleAuthMessage(message);
71
+ }
72
+ if (message['order']) {
73
+ const order = JSON.parse(message['order']);
74
+ const swiftOrderParamsBuf = Buffer.from(order['order_message'], 'base64');
75
+ const swiftOrderParamsMessage = this.driftClient.program.coder.types.decode('SwiftOrderParamsMessage', swiftOrderParamsBuf);
76
+ if (!swiftOrderParamsMessage.swiftOrderParams.price) {
77
+ console.error(`order has no price: ${JSON.stringify(swiftOrderParamsMessage.swiftOrderParams)}`);
78
+ return;
79
+ }
80
+ this.onOrder(order, swiftOrderParamsMessage);
81
+ }
82
+ });
83
+ ws.on('close', () => {
84
+ console.log('Disconnected from the server');
85
+ this.reconnect();
86
+ });
87
+ ws.on('error', (error) => {
88
+ console.error('WebSocket error:', error);
89
+ this.reconnect();
90
+ });
91
+ });
92
+ }
93
+ async getPlaceAndMakeSwiftOrderIxs(orderMessageRaw, swiftOrderParamsMessage, makerOrderParams) {
94
+ const swiftOrderParamsBuf = Buffer.from(orderMessageRaw['order_message'], 'base64');
95
+ const takerAuthority = new web3_js_1.PublicKey(orderMessageRaw['taker_authority']);
96
+ const takerUserPubkey = await (0, __1.getUserAccountPublicKey)(this.driftClient.program.programId, takerAuthority, swiftOrderParamsMessage.subAccountId);
97
+ const takerUserAccount = (await this.userMap.mustGet(takerUserPubkey.toString())).getUserAccount();
98
+ const ixs = await this.driftClient.getPlaceAndMakeSwiftPerpOrderIxs(swiftOrderParamsBuf, Buffer.from(orderMessageRaw['order_signature'], 'base64'), (0, tweetnacl_util_1.decodeUTF8)(orderMessageRaw['uuid']), {
99
+ taker: takerUserPubkey,
100
+ takerUserAccount,
101
+ takerStats: (0, __1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, takerUserAccount.authority),
102
+ }, Object.assign({}, makerOrderParams, {
103
+ postOnly: __1.PostOnlyParams.MUST_POST_ONLY,
104
+ immediateOrCancel: true,
105
+ marketType: __1.MarketType.PERP,
106
+ }));
107
+ return ixs;
108
+ }
109
+ startHeartbeatTimer() {
110
+ if (this.heartbeatTimeout) {
111
+ clearTimeout(this.heartbeatTimeout);
112
+ }
113
+ this.heartbeatTimeout = setTimeout(() => {
114
+ console.warn('No heartbeat received within 30 seconds, reconnecting...');
115
+ this.reconnect();
116
+ }, this.heartbeatIntervalMs);
117
+ }
118
+ reconnect() {
119
+ if (this.ws) {
120
+ this.ws.removeAllListeners();
121
+ this.ws.terminate();
122
+ }
123
+ console.log('Reconnecting to WebSocket...');
124
+ setTimeout(() => {
125
+ this.subscribe();
126
+ }, 1000);
127
+ }
128
+ }
129
+ exports.SwiftOrderSubscriber = SwiftOrderSubscriber;
@@ -86,6 +86,7 @@ export * from './oracles/pythClient';
86
86
  export * from './oracles/pythPullClient';
87
87
  export * from './oracles/pythLazerClient';
88
88
  export * from './oracles/switchboardOnDemandClient';
89
+ export * from './swift/swiftOrderSubscriber';
89
90
  export * from './tx/fastSingleTxSender';
90
91
  export * from './tx/retryTxSender';
91
92
  export * from './tx/whileValidTxSender';
package/lib/node/index.js CHANGED
@@ -109,6 +109,7 @@ __exportStar(require("./oracles/pythClient"), exports);
109
109
  __exportStar(require("./oracles/pythPullClient"), exports);
110
110
  __exportStar(require("./oracles/pythLazerClient"), exports);
111
111
  __exportStar(require("./oracles/switchboardOnDemandClient"), exports);
112
+ __exportStar(require("./swift/swiftOrderSubscriber"), exports);
112
113
  __exportStar(require("./tx/fastSingleTxSender"), exports);
113
114
  __exportStar(require("./tx/retryTxSender"), exports);
114
115
  __exportStar(require("./tx/whileValidTxSender"), exports);
@@ -0,0 +1,28 @@
1
+ import { DriftClient, DriftEnv, OptionalOrderParams, SwiftOrderParamsMessage, UserMap } from '..';
2
+ import { Keypair, TransactionInstruction } from '@solana/web3.js';
3
+ export type SwiftOrderSubscriberConfig = {
4
+ driftClient: DriftClient;
5
+ userMap: UserMap;
6
+ driftEnv: DriftEnv;
7
+ endpoint?: string;
8
+ marketIndexes: number[];
9
+ keypair: Keypair;
10
+ };
11
+ export declare class SwiftOrderSubscriber {
12
+ private config;
13
+ private onOrder;
14
+ private heartbeatTimeout;
15
+ private readonly heartbeatIntervalMs;
16
+ private ws;
17
+ private driftClient;
18
+ private userMap;
19
+ subscribed: boolean;
20
+ constructor(config: SwiftOrderSubscriberConfig, onOrder: (orderMessageRaw: any, swiftOrderParamsMessage: SwiftOrderParamsMessage) => Promise<void>);
21
+ getSymbolForMarketIndex(marketIndex: number): string;
22
+ generateChallengeResponse(nonce: string): string;
23
+ handleAuthMessage(message: any): void;
24
+ subscribe(): Promise<void>;
25
+ getPlaceAndMakeSwiftOrderIxs(orderMessageRaw: any, swiftOrderParamsMessage: SwiftOrderParamsMessage, makerOrderParams: OptionalOrderParams): Promise<TransactionInstruction[]>;
26
+ private startHeartbeatTimer;
27
+ private reconnect;
28
+ }
@@ -0,0 +1,129 @@
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.SwiftOrderSubscriber = void 0;
7
+ const __1 = require("..");
8
+ const web3_js_1 = require("@solana/web3.js");
9
+ const tweetnacl_1 = __importDefault(require("tweetnacl"));
10
+ const tweetnacl_util_1 = require("tweetnacl-util");
11
+ const ws_1 = __importDefault(require("ws"));
12
+ class SwiftOrderSubscriber {
13
+ constructor(config, onOrder) {
14
+ this.config = config;
15
+ this.onOrder = onOrder;
16
+ this.heartbeatTimeout = null;
17
+ this.heartbeatIntervalMs = 60000;
18
+ this.ws = null;
19
+ this.subscribed = false;
20
+ this.driftClient = config.driftClient;
21
+ this.userMap = config.userMap;
22
+ }
23
+ getSymbolForMarketIndex(marketIndex) {
24
+ const markets = this.config.driftEnv === 'devnet'
25
+ ? __1.DevnetPerpMarkets
26
+ : __1.MainnetPerpMarkets;
27
+ return markets[marketIndex].symbol;
28
+ }
29
+ generateChallengeResponse(nonce) {
30
+ const messageBytes = (0, tweetnacl_util_1.decodeUTF8)(nonce);
31
+ const signature = tweetnacl_1.default.sign.detached(messageBytes, this.config.keypair.secretKey);
32
+ const signatureBase64 = Buffer.from(signature).toString('base64');
33
+ return signatureBase64;
34
+ }
35
+ handleAuthMessage(message) {
36
+ var _a, _b;
37
+ if (message['channel'] === 'auth' && message['nonce'] != null) {
38
+ const signatureBase64 = this.generateChallengeResponse(message['nonce']);
39
+ (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({
40
+ pubkey: this.config.keypair.publicKey.toBase58(),
41
+ signature: signatureBase64,
42
+ }));
43
+ }
44
+ if (message['channel'] === 'auth' &&
45
+ ((_b = message['message']) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'authenticated') {
46
+ this.subscribed = true;
47
+ this.config.marketIndexes.forEach(async (marketIndex) => {
48
+ var _a;
49
+ (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify({
50
+ action: 'subscribe',
51
+ market_type: 'perp',
52
+ market_name: this.getSymbolForMarketIndex(marketIndex),
53
+ }));
54
+ await new Promise((resolve) => setTimeout(resolve, 100));
55
+ });
56
+ }
57
+ }
58
+ async subscribe() {
59
+ const endpoint = this.config.endpoint || this.config.driftEnv === 'devnet'
60
+ ? 'wss://master.swift.drift.trade/ws'
61
+ : 'wss://swift.drift.trade/ws';
62
+ const ws = new ws_1.default(endpoint + '?pubkey=' + this.config.keypair.publicKey.toBase58());
63
+ this.ws = ws;
64
+ ws.on('open', async () => {
65
+ console.log('Connected to the server');
66
+ ws.on('message', async (data) => {
67
+ const message = JSON.parse(data.toString());
68
+ this.startHeartbeatTimer();
69
+ if (message['channel'] === 'auth') {
70
+ this.handleAuthMessage(message);
71
+ }
72
+ if (message['order']) {
73
+ const order = JSON.parse(message['order']);
74
+ const swiftOrderParamsBuf = Buffer.from(order['order_message'], 'base64');
75
+ const swiftOrderParamsMessage = this.driftClient.program.coder.types.decode('SwiftOrderParamsMessage', swiftOrderParamsBuf);
76
+ if (!swiftOrderParamsMessage.swiftOrderParams.price) {
77
+ console.error(`order has no price: ${JSON.stringify(swiftOrderParamsMessage.swiftOrderParams)}`);
78
+ return;
79
+ }
80
+ this.onOrder(order, swiftOrderParamsMessage);
81
+ }
82
+ });
83
+ ws.on('close', () => {
84
+ console.log('Disconnected from the server');
85
+ this.reconnect();
86
+ });
87
+ ws.on('error', (error) => {
88
+ console.error('WebSocket error:', error);
89
+ this.reconnect();
90
+ });
91
+ });
92
+ }
93
+ async getPlaceAndMakeSwiftOrderIxs(orderMessageRaw, swiftOrderParamsMessage, makerOrderParams) {
94
+ const swiftOrderParamsBuf = Buffer.from(orderMessageRaw['order_message'], 'base64');
95
+ const takerAuthority = new web3_js_1.PublicKey(orderMessageRaw['taker_authority']);
96
+ const takerUserPubkey = await (0, __1.getUserAccountPublicKey)(this.driftClient.program.programId, takerAuthority, swiftOrderParamsMessage.subAccountId);
97
+ const takerUserAccount = (await this.userMap.mustGet(takerUserPubkey.toString())).getUserAccount();
98
+ const ixs = await this.driftClient.getPlaceAndMakeSwiftPerpOrderIxs(swiftOrderParamsBuf, Buffer.from(orderMessageRaw['order_signature'], 'base64'), (0, tweetnacl_util_1.decodeUTF8)(orderMessageRaw['uuid']), {
99
+ taker: takerUserPubkey,
100
+ takerUserAccount,
101
+ takerStats: (0, __1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, takerUserAccount.authority),
102
+ }, Object.assign({}, makerOrderParams, {
103
+ postOnly: __1.PostOnlyParams.MUST_POST_ONLY,
104
+ immediateOrCancel: true,
105
+ marketType: __1.MarketType.PERP,
106
+ }));
107
+ return ixs;
108
+ }
109
+ startHeartbeatTimer() {
110
+ if (this.heartbeatTimeout) {
111
+ clearTimeout(this.heartbeatTimeout);
112
+ }
113
+ this.heartbeatTimeout = setTimeout(() => {
114
+ console.warn('No heartbeat received within 30 seconds, reconnecting...');
115
+ this.reconnect();
116
+ }, this.heartbeatIntervalMs);
117
+ }
118
+ reconnect() {
119
+ if (this.ws) {
120
+ this.ws.removeAllListeners();
121
+ this.ws.terminate();
122
+ }
123
+ console.log('Reconnecting to WebSocket...');
124
+ setTimeout(() => {
125
+ this.subscribe();
126
+ }, 1000);
127
+ }
128
+ }
129
+ exports.SwiftOrderSubscriber = SwiftOrderSubscriber;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.106.0-beta.1",
3
+ "version": "2.106.0-beta.2",
4
4
  "main": "lib/node/index.js",
5
5
  "types": "lib/node/index.d.ts",
6
6
  "browser": "./lib/browser/index.js",
@@ -56,6 +56,7 @@
56
56
  "solana-bankrun": "0.3.1",
57
57
  "strict-event-emitter-types": "2.0.0",
58
58
  "tweetnacl": "1.0.3",
59
+ "tweetnacl-util": "0.15.1",
59
60
  "uuid": "8.3.2",
60
61
  "yargs": "17.7.2",
61
62
  "zstddec": "0.1.0"
package/src/index.ts CHANGED
@@ -87,6 +87,7 @@ export * from './oracles/pythClient';
87
87
  export * from './oracles/pythPullClient';
88
88
  export * from './oracles/pythLazerClient';
89
89
  export * from './oracles/switchboardOnDemandClient';
90
+ export * from './swift/swiftOrderSubscriber';
90
91
  export * from './tx/fastSingleTxSender';
91
92
  export * from './tx/retryTxSender';
92
93
  export * from './tx/whileValidTxSender';
@@ -0,0 +1,211 @@
1
+ import {
2
+ DevnetPerpMarkets,
3
+ DriftClient,
4
+ DriftEnv,
5
+ getUserAccountPublicKey,
6
+ getUserStatsAccountPublicKey,
7
+ MainnetPerpMarkets,
8
+ MarketType,
9
+ OptionalOrderParams,
10
+ PostOnlyParams,
11
+ SwiftOrderParamsMessage,
12
+ UserMap,
13
+ } from '..';
14
+ import { Keypair, PublicKey, TransactionInstruction } from '@solana/web3.js';
15
+ import nacl from 'tweetnacl';
16
+ import { decodeUTF8 } from 'tweetnacl-util';
17
+ import WebSocket from 'ws';
18
+
19
+ export type SwiftOrderSubscriberConfig = {
20
+ driftClient: DriftClient;
21
+ userMap: UserMap;
22
+ driftEnv: DriftEnv;
23
+ endpoint?: string;
24
+ marketIndexes: number[];
25
+ keypair: Keypair;
26
+ };
27
+
28
+ export class SwiftOrderSubscriber {
29
+ private heartbeatTimeout: NodeJS.Timeout | null = null;
30
+ private readonly heartbeatIntervalMs = 60000;
31
+ private ws: WebSocket | null = null;
32
+ private driftClient: DriftClient;
33
+ private userMap: UserMap;
34
+ subscribed = false;
35
+
36
+ constructor(
37
+ private config: SwiftOrderSubscriberConfig,
38
+ private onOrder: (
39
+ orderMessageRaw: any,
40
+ swiftOrderParamsMessage: SwiftOrderParamsMessage
41
+ ) => Promise<void>
42
+ ) {
43
+ this.driftClient = config.driftClient;
44
+ this.userMap = config.userMap;
45
+ }
46
+
47
+ getSymbolForMarketIndex(marketIndex: number): string {
48
+ const markets =
49
+ this.config.driftEnv === 'devnet'
50
+ ? DevnetPerpMarkets
51
+ : MainnetPerpMarkets;
52
+ return markets[marketIndex].symbol;
53
+ }
54
+
55
+ generateChallengeResponse(nonce: string): string {
56
+ const messageBytes = decodeUTF8(nonce);
57
+ const signature = nacl.sign.detached(
58
+ messageBytes,
59
+ this.config.keypair.secretKey
60
+ );
61
+ const signatureBase64 = Buffer.from(signature).toString('base64');
62
+ return signatureBase64;
63
+ }
64
+
65
+ handleAuthMessage(message: any): void {
66
+ if (message['channel'] === 'auth' && message['nonce'] != null) {
67
+ const signatureBase64 = this.generateChallengeResponse(message['nonce']);
68
+ this.ws?.send(
69
+ JSON.stringify({
70
+ pubkey: this.config.keypair.publicKey.toBase58(),
71
+ signature: signatureBase64,
72
+ })
73
+ );
74
+ }
75
+
76
+ if (
77
+ message['channel'] === 'auth' &&
78
+ message['message']?.toLowerCase() === 'authenticated'
79
+ ) {
80
+ this.subscribed = true;
81
+ this.config.marketIndexes.forEach(async (marketIndex) => {
82
+ this.ws?.send(
83
+ JSON.stringify({
84
+ action: 'subscribe',
85
+ market_type: 'perp',
86
+ market_name: this.getSymbolForMarketIndex(marketIndex),
87
+ })
88
+ );
89
+ await new Promise((resolve) => setTimeout(resolve, 100));
90
+ });
91
+ }
92
+ }
93
+
94
+ async subscribe(): Promise<void> {
95
+ const endpoint =
96
+ this.config.endpoint || this.config.driftEnv === 'devnet'
97
+ ? 'wss://master.swift.drift.trade/ws'
98
+ : 'wss://swift.drift.trade/ws';
99
+ const ws = new WebSocket(
100
+ endpoint + '?pubkey=' + this.config.keypair.publicKey.toBase58()
101
+ );
102
+ this.ws = ws;
103
+ ws.on('open', async () => {
104
+ console.log('Connected to the server');
105
+
106
+ ws.on('message', async (data: WebSocket.Data) => {
107
+ const message = JSON.parse(data.toString());
108
+ this.startHeartbeatTimer();
109
+
110
+ if (message['channel'] === 'auth') {
111
+ this.handleAuthMessage(message);
112
+ }
113
+
114
+ if (message['order']) {
115
+ const order = JSON.parse(message['order']);
116
+ const swiftOrderParamsBuf = Buffer.from(
117
+ order['order_message'],
118
+ 'base64'
119
+ );
120
+ const swiftOrderParamsMessage: SwiftOrderParamsMessage =
121
+ this.driftClient.program.coder.types.decode(
122
+ 'SwiftOrderParamsMessage',
123
+ swiftOrderParamsBuf
124
+ );
125
+
126
+ if (!swiftOrderParamsMessage.swiftOrderParams.price) {
127
+ console.error(
128
+ `order has no price: ${JSON.stringify(
129
+ swiftOrderParamsMessage.swiftOrderParams
130
+ )}`
131
+ );
132
+ return;
133
+ }
134
+
135
+ this.onOrder(order, swiftOrderParamsMessage);
136
+ }
137
+ });
138
+
139
+ ws.on('close', () => {
140
+ console.log('Disconnected from the server');
141
+ this.reconnect();
142
+ });
143
+
144
+ ws.on('error', (error: Error) => {
145
+ console.error('WebSocket error:', error);
146
+ this.reconnect();
147
+ });
148
+ });
149
+ }
150
+
151
+ async getPlaceAndMakeSwiftOrderIxs(
152
+ orderMessageRaw: any,
153
+ swiftOrderParamsMessage: SwiftOrderParamsMessage,
154
+ makerOrderParams: OptionalOrderParams
155
+ ): Promise<TransactionInstruction[]> {
156
+ const swiftOrderParamsBuf = Buffer.from(
157
+ orderMessageRaw['order_message'],
158
+ 'base64'
159
+ );
160
+ const takerAuthority = new PublicKey(orderMessageRaw['taker_authority']);
161
+ const takerUserPubkey = await getUserAccountPublicKey(
162
+ this.driftClient.program.programId,
163
+ takerAuthority,
164
+ swiftOrderParamsMessage.subAccountId
165
+ );
166
+ const takerUserAccount = (
167
+ await this.userMap.mustGet(takerUserPubkey.toString())
168
+ ).getUserAccount();
169
+ const ixs = await this.driftClient.getPlaceAndMakeSwiftPerpOrderIxs(
170
+ swiftOrderParamsBuf,
171
+ Buffer.from(orderMessageRaw['order_signature'], 'base64'),
172
+ decodeUTF8(orderMessageRaw['uuid']),
173
+ {
174
+ taker: takerUserPubkey,
175
+ takerUserAccount,
176
+ takerStats: getUserStatsAccountPublicKey(
177
+ this.driftClient.program.programId,
178
+ takerUserAccount.authority
179
+ ),
180
+ },
181
+ Object.assign({}, makerOrderParams, {
182
+ postOnly: PostOnlyParams.MUST_POST_ONLY,
183
+ immediateOrCancel: true,
184
+ marketType: MarketType.PERP,
185
+ })
186
+ );
187
+ return ixs;
188
+ }
189
+
190
+ private startHeartbeatTimer() {
191
+ if (this.heartbeatTimeout) {
192
+ clearTimeout(this.heartbeatTimeout);
193
+ }
194
+ this.heartbeatTimeout = setTimeout(() => {
195
+ console.warn('No heartbeat received within 30 seconds, reconnecting...');
196
+ this.reconnect();
197
+ }, this.heartbeatIntervalMs);
198
+ }
199
+
200
+ private reconnect() {
201
+ if (this.ws) {
202
+ this.ws.removeAllListeners();
203
+ this.ws.terminate();
204
+ }
205
+
206
+ console.log('Reconnecting to WebSocket...');
207
+ setTimeout(() => {
208
+ this.subscribe();
209
+ }, 1000);
210
+ }
211
+ }