@drift-labs/jit-proxy 0.8.0 → 0.10.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/jit-proxy",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "dependencies": {
5
5
  "@coral-xyz/anchor": "^0.26.0",
6
6
  "@solana/web3.js": "1.73.2",
@@ -2,6 +2,7 @@ import {
2
2
  BN,
3
3
  DriftClient,
4
4
  isVariant,
5
+ MarketType,
5
6
  PostOnlyParams,
6
7
  QUOTE_SPOT_MARKET_INDEX,
7
8
  ReferrerInfo,
@@ -33,6 +34,13 @@ export class PriceType {
33
34
  static readonly ORACLE = { oracle: {} };
34
35
  }
35
36
 
37
+ export type OrderConstraint = {
38
+ maxPosition: BN;
39
+ minPosition: BN;
40
+ marketIndex: number;
41
+ marketType: MarketType;
42
+ };
43
+
36
44
  export class JitProxyClient {
37
45
  private driftClient: DriftClient;
38
46
  private program: Program<JitProxy>;
@@ -135,4 +143,41 @@ export class JitProxyClient {
135
143
  .remainingAccounts(remainingAccounts)
136
144
  .instruction();
137
145
  }
146
+
147
+ public async getCheckOrderConstraintIx({
148
+ subAccountId,
149
+ orderConstraints,
150
+ }: {
151
+ subAccountId: number;
152
+ orderConstraints: OrderConstraint[];
153
+ }): Promise<TransactionInstruction> {
154
+ subAccountId =
155
+ subAccountId !== undefined
156
+ ? subAccountId
157
+ : this.driftClient.activeSubAccountId;
158
+
159
+ const readablePerpMarketIndex = [];
160
+ const readableSpotMarketIndexes = [];
161
+ for (const orderConstraint of orderConstraints) {
162
+ if (isVariant(orderConstraint.marketType, 'perp')) {
163
+ readablePerpMarketIndex.push(orderConstraint.marketIndex);
164
+ } else {
165
+ readableSpotMarketIndexes.push(orderConstraint.marketIndex);
166
+ }
167
+ }
168
+
169
+ const remainingAccounts = this.driftClient.getRemainingAccounts({
170
+ userAccounts: [this.driftClient.getUserAccount(subAccountId)],
171
+ readableSpotMarketIndexes,
172
+ readablePerpMarketIndex,
173
+ });
174
+
175
+ return this.program.methods
176
+ .checkOrderConstraints(orderConstraints)
177
+ .accounts({
178
+ user: await this.driftClient.getUserAccountPublicKey(subAccountId),
179
+ })
180
+ .remainingAccounts(remainingAccounts)
181
+ .instruction();
182
+ }
138
183
  }
@@ -1,5 +1,5 @@
1
1
  export type JitProxy = {
2
- version: '0.1.0';
2
+ version: '0.10.0';
3
3
  name: 'jit_proxy';
4
4
  instructions: [
5
5
  {
@@ -49,6 +49,26 @@ export type JitProxy = {
49
49
  };
50
50
  }
51
51
  ];
52
+ },
53
+ {
54
+ name: 'checkOrderConstraints';
55
+ accounts: [
56
+ {
57
+ name: 'user';
58
+ isMut: false;
59
+ isSigner: false;
60
+ }
61
+ ];
62
+ args: [
63
+ {
64
+ name: 'constraints';
65
+ type: {
66
+ vec: {
67
+ defined: 'OrderConstraint';
68
+ };
69
+ };
70
+ }
71
+ ];
52
72
  }
53
73
  ];
54
74
  types: [
@@ -94,6 +114,32 @@ export type JitProxy = {
94
114
  ];
95
115
  };
96
116
  },
117
+ {
118
+ name: 'OrderConstraint';
119
+ type: {
120
+ kind: 'struct';
121
+ fields: [
122
+ {
123
+ name: 'maxPosition';
124
+ type: 'i64';
125
+ },
126
+ {
127
+ name: 'minPosition';
128
+ type: 'i64';
129
+ },
130
+ {
131
+ name: 'marketIndex';
132
+ type: 'u16';
133
+ },
134
+ {
135
+ name: 'marketType';
136
+ type: {
137
+ defined: 'MarketType';
138
+ };
139
+ }
140
+ ];
141
+ };
142
+ },
97
143
  {
98
144
  name: 'PostOnlyParam';
99
145
  type: {
@@ -124,6 +170,20 @@ export type JitProxy = {
124
170
  }
125
171
  ];
126
172
  };
173
+ },
174
+ {
175
+ name: 'MarketType';
176
+ type: {
177
+ kind: 'enum';
178
+ variants: [
179
+ {
180
+ name: 'Perp';
181
+ },
182
+ {
183
+ name: 'Spot';
184
+ }
185
+ ];
186
+ };
127
187
  }
128
188
  ];
129
189
  errors: [
@@ -141,12 +201,17 @@ export type JitProxy = {
141
201
  code: 6002;
142
202
  name: 'TakerOrderNotFound';
143
203
  msg: 'TakerOrderNotFound';
204
+ },
205
+ {
206
+ code: 6003;
207
+ name: 'OrderSizeBreached';
208
+ msg: 'OrderSizeBreached';
144
209
  }
145
210
  ];
146
211
  };
147
212
 
148
213
  export const IDL: JitProxy = {
149
- version: '0.1.0',
214
+ version: '0.10.0',
150
215
  name: 'jit_proxy',
151
216
  instructions: [
152
217
  {
@@ -197,6 +262,26 @@ export const IDL: JitProxy = {
197
262
  },
198
263
  ],
199
264
  },
265
+ {
266
+ name: 'checkOrderConstraints',
267
+ accounts: [
268
+ {
269
+ name: 'user',
270
+ isMut: false,
271
+ isSigner: false,
272
+ },
273
+ ],
274
+ args: [
275
+ {
276
+ name: 'constraints',
277
+ type: {
278
+ vec: {
279
+ defined: 'OrderConstraint',
280
+ },
281
+ },
282
+ },
283
+ ],
284
+ },
200
285
  ],
201
286
  types: [
202
287
  {
@@ -241,6 +326,32 @@ export const IDL: JitProxy = {
241
326
  ],
242
327
  },
243
328
  },
329
+ {
330
+ name: 'OrderConstraint',
331
+ type: {
332
+ kind: 'struct',
333
+ fields: [
334
+ {
335
+ name: 'maxPosition',
336
+ type: 'i64',
337
+ },
338
+ {
339
+ name: 'minPosition',
340
+ type: 'i64',
341
+ },
342
+ {
343
+ name: 'marketIndex',
344
+ type: 'u16',
345
+ },
346
+ {
347
+ name: 'marketType',
348
+ type: {
349
+ defined: 'MarketType',
350
+ },
351
+ },
352
+ ],
353
+ },
354
+ },
244
355
  {
245
356
  name: 'PostOnlyParam',
246
357
  type: {
@@ -272,6 +383,20 @@ export const IDL: JitProxy = {
272
383
  ],
273
384
  },
274
385
  },
386
+ {
387
+ name: 'MarketType',
388
+ type: {
389
+ kind: 'enum',
390
+ variants: [
391
+ {
392
+ name: 'Perp',
393
+ },
394
+ {
395
+ name: 'Spot',
396
+ },
397
+ ],
398
+ },
399
+ },
275
400
  ],
276
401
  errors: [
277
402
  {
@@ -289,5 +414,10 @@ export const IDL: JitProxy = {
289
414
  name: 'TakerOrderNotFound',
290
415
  msg: 'TakerOrderNotFound',
291
416
  },
417
+ {
418
+ code: 6003,
419
+ name: 'OrderSizeBreached',
420
+ msg: 'OrderSizeBreached',
421
+ },
292
422
  ],
293
423
  };
package/lib/index.d.ts DELETED
@@ -1,4 +0,0 @@
1
- export * from './types/jit_proxy';
2
- export * from './jitProxyClient';
3
- export * from './jitter/jitterSniper';
4
- export * from './jitter/jitterShotgun';
package/lib/index.js DELETED
@@ -1,16 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- __exportStar(require("./types/jit_proxy"), exports);
14
- __exportStar(require("./jitProxyClient"), exports);
15
- __exportStar(require("./jitter/jitterSniper"), exports);
16
- __exportStar(require("./jitter/jitterShotgun"), exports);
@@ -1,36 +0,0 @@
1
- /// <reference types="bn.js" />
2
- import { BN, DriftClient, PostOnlyParams, ReferrerInfo, TxParams, UserAccount } from '@drift-labs/sdk';
3
- import { PublicKey, TransactionInstruction } from '@solana/web3.js';
4
- import { TxSigAndSlot } from '@drift-labs/sdk/lib/tx/types';
5
- export declare type JitIxParams = {
6
- takerKey: PublicKey;
7
- takerStatsKey: PublicKey;
8
- taker: UserAccount;
9
- takerOrderId: number;
10
- maxPosition: BN;
11
- minPosition: BN;
12
- bid: BN;
13
- ask: BN;
14
- postOnly: PostOnlyParams | null;
15
- priceType?: PriceType;
16
- referrerInfo?: ReferrerInfo;
17
- subAccountId?: number;
18
- };
19
- export declare class PriceType {
20
- static readonly LIMIT: {
21
- limit: {};
22
- };
23
- static readonly ORACLE: {
24
- oracle: {};
25
- };
26
- }
27
- export declare class JitProxyClient {
28
- private driftClient;
29
- private program;
30
- constructor({ driftClient, programId, }: {
31
- driftClient: DriftClient;
32
- programId: PublicKey;
33
- });
34
- jit(params: JitIxParams, txParams?: TxParams): Promise<TxSigAndSlot>;
35
- getJitIx({ takerKey, takerStatsKey, taker, takerOrderId, maxPosition, minPosition, bid, ask, postOnly, priceType, referrerInfo, subAccountId, }: JitIxParams): Promise<TransactionInstruction>;
36
- }
@@ -1,84 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JitProxyClient = exports.PriceType = void 0;
4
- const sdk_1 = require("@drift-labs/sdk");
5
- const jit_proxy_1 = require("./types/jit_proxy");
6
- const anchor_1 = require("@coral-xyz/anchor");
7
- class PriceType {
8
- }
9
- exports.PriceType = PriceType;
10
- PriceType.LIMIT = { limit: {} };
11
- PriceType.ORACLE = { oracle: {} };
12
- class JitProxyClient {
13
- constructor({ driftClient, programId, }) {
14
- this.driftClient = driftClient;
15
- this.program = new anchor_1.Program(jit_proxy_1.IDL, programId, driftClient.provider);
16
- }
17
- async jit(params, txParams) {
18
- const ix = await this.getJitIx(params);
19
- const tx = await this.driftClient.buildTransaction([ix], txParams);
20
- return await this.driftClient.sendTransaction(tx);
21
- }
22
- async getJitIx({ takerKey, takerStatsKey, taker, takerOrderId, maxPosition, minPosition, bid, ask, postOnly = null, priceType = PriceType.LIMIT, referrerInfo, subAccountId, }) {
23
- subAccountId =
24
- subAccountId !== undefined
25
- ? subAccountId
26
- : this.driftClient.activeSubAccountId;
27
- const order = taker.orders.find((order) => order.orderId === takerOrderId);
28
- const remainingAccounts = this.driftClient.getRemainingAccounts({
29
- userAccounts: [taker, this.driftClient.getUserAccount(subAccountId)],
30
- writableSpotMarketIndexes: sdk_1.isVariant(order.marketType, 'spot')
31
- ? [order.marketIndex, sdk_1.QUOTE_SPOT_MARKET_INDEX]
32
- : [],
33
- writablePerpMarketIndexes: sdk_1.isVariant(order.marketType, 'perp')
34
- ? [order.marketIndex]
35
- : [],
36
- });
37
- if (referrerInfo) {
38
- remainingAccounts.push({
39
- pubkey: referrerInfo.referrer,
40
- isWritable: true,
41
- isSigner: false,
42
- });
43
- remainingAccounts.push({
44
- pubkey: referrerInfo.referrerStats,
45
- isWritable: true,
46
- isSigner: false,
47
- });
48
- }
49
- if (sdk_1.isVariant(order.marketType, 'spot')) {
50
- remainingAccounts.push({
51
- pubkey: this.driftClient.getSpotMarketAccount(order.marketIndex).vault,
52
- isWritable: false,
53
- isSigner: false,
54
- });
55
- remainingAccounts.push({
56
- pubkey: this.driftClient.getQuoteSpotMarketAccount().vault,
57
- isWritable: false,
58
- isSigner: false,
59
- });
60
- }
61
- const jitParams = {
62
- takerOrderId,
63
- maxPosition,
64
- minPosition,
65
- bid,
66
- ask,
67
- postOnly,
68
- priceType,
69
- };
70
- return this.program.methods
71
- .jit(jitParams)
72
- .accounts({
73
- taker: takerKey,
74
- takerStats: takerStatsKey,
75
- state: await this.driftClient.getStatePublicKey(),
76
- user: await this.driftClient.getUserAccountPublicKey(subAccountId),
77
- userStats: this.driftClient.getUserStatsAccountPublicKey(),
78
- driftProgram: this.driftClient.program.programId,
79
- })
80
- .remainingAccounts(remainingAccounts)
81
- .instruction();
82
- }
83
- }
84
- exports.JitProxyClient = JitProxyClient;
@@ -1,35 +0,0 @@
1
- /// <reference types="bn.js" />
2
- import { JitProxyClient, PriceType } from '../jitProxyClient';
3
- import { PublicKey } from '@solana/web3.js';
4
- import { AuctionSubscriber, BN, DriftClient, Order, UserAccount, UserStatsMap } from '@drift-labs/sdk';
5
- export declare type UserFilter = (userAccount: UserAccount, userKey: string, order: Order) => boolean;
6
- export declare type JitParams = {
7
- bid: BN;
8
- ask: BN;
9
- minPosition: BN;
10
- maxPosition: any;
11
- priceType: PriceType;
12
- subAccountId?: number;
13
- };
14
- export declare abstract class BaseJitter {
15
- auctionSubscriber: AuctionSubscriber;
16
- driftClient: DriftClient;
17
- jitProxyClient: JitProxyClient;
18
- userStatsMap: UserStatsMap;
19
- perpParams: Map<number, JitParams>;
20
- spotParams: Map<number, JitParams>;
21
- onGoingAuctions: Map<string, Promise<void>>;
22
- userFilter: UserFilter;
23
- constructor({ auctionSubscriber, jitProxyClient, driftClient, userStatsMap, }: {
24
- driftClient: DriftClient;
25
- auctionSubscriber: AuctionSubscriber;
26
- jitProxyClient: JitProxyClient;
27
- userStatsMap?: UserStatsMap;
28
- });
29
- subscribe(): Promise<void>;
30
- createTryFill(taker: UserAccount, takerKey: PublicKey, takerStatsKey: PublicKey, order: Order, orderSignature: string): () => Promise<void>;
31
- getOrderSignatures(takerKey: string, orderId: number): string;
32
- updatePerpParams(marketIndex: number, params: JitParams): void;
33
- updateSpotParams(marketIndex: number, params: JitParams): void;
34
- setUserFilter(userFilter: UserFilter | undefined): void;
35
- }
@@ -1,88 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BaseJitter = void 0;
4
- const sdk_1 = require("@drift-labs/sdk");
5
- class BaseJitter {
6
- constructor({ auctionSubscriber, jitProxyClient, driftClient, userStatsMap, }) {
7
- this.perpParams = new Map();
8
- this.spotParams = new Map();
9
- this.onGoingAuctions = new Map();
10
- this.auctionSubscriber = auctionSubscriber;
11
- this.driftClient = driftClient;
12
- this.jitProxyClient = jitProxyClient;
13
- this.userStatsMap =
14
- userStatsMap ||
15
- new sdk_1.UserStatsMap(this.driftClient, {
16
- type: 'polling',
17
- accountLoader: new sdk_1.BulkAccountLoader(this.driftClient.connection, 'confirmed', 0),
18
- });
19
- }
20
- async subscribe() {
21
- await this.driftClient.subscribe();
22
- await this.userStatsMap.subscribe();
23
- await this.auctionSubscriber.subscribe();
24
- this.auctionSubscriber.eventEmitter.on('onAccountUpdate', async (taker, takerKey, slot) => {
25
- const takerKeyString = takerKey.toBase58();
26
- const takerStatsKey = sdk_1.getUserStatsAccountPublicKey(this.driftClient.program.programId, taker.authority);
27
- for (const order of taker.orders) {
28
- if (!sdk_1.isVariant(order.status, 'open')) {
29
- continue;
30
- }
31
- if (!sdk_1.hasAuctionPrice(order, slot)) {
32
- continue;
33
- }
34
- if (this.userFilter) {
35
- if (this.userFilter(taker, takerKeyString, order)) {
36
- return;
37
- }
38
- }
39
- const orderSignature = this.getOrderSignatures(takerKeyString, order.orderId);
40
- if (this.onGoingAuctions.has(orderSignature)) {
41
- continue;
42
- }
43
- if (sdk_1.isVariant(order.marketType, 'perp')) {
44
- if (!this.perpParams.has(order.marketIndex)) {
45
- return;
46
- }
47
- const perpMarketAccount = this.driftClient.getPerpMarketAccount(order.marketIndex);
48
- if (order.baseAssetAmount
49
- .sub(order.baseAssetAmountFilled)
50
- .lte(perpMarketAccount.amm.minOrderSize)) {
51
- return;
52
- }
53
- const promise = this.createTryFill(taker, takerKey, takerStatsKey, order, orderSignature).bind(this)();
54
- this.onGoingAuctions.set(orderSignature, promise);
55
- }
56
- else {
57
- if (!this.spotParams.has(order.marketIndex)) {
58
- return;
59
- }
60
- const spotMarketAccount = this.driftClient.getSpotMarketAccount(order.marketIndex);
61
- if (order.baseAssetAmount
62
- .sub(order.baseAssetAmountFilled)
63
- .lte(spotMarketAccount.minOrderSize)) {
64
- return;
65
- }
66
- const promise = this.createTryFill(taker, takerKey, takerStatsKey, order, orderSignature).bind(this)();
67
- this.onGoingAuctions.set(orderSignature, promise);
68
- }
69
- }
70
- });
71
- }
72
- createTryFill(taker, takerKey, takerStatsKey, order, orderSignature) {
73
- throw new Error('Not implemented');
74
- }
75
- getOrderSignatures(takerKey, orderId) {
76
- return `${takerKey}-${orderId}`;
77
- }
78
- updatePerpParams(marketIndex, params) {
79
- this.perpParams.set(marketIndex, params);
80
- }
81
- updateSpotParams(marketIndex, params) {
82
- this.spotParams.set(marketIndex, params);
83
- }
84
- setUserFilter(userFilter) {
85
- this.userFilter = userFilter;
86
- }
87
- }
88
- exports.BaseJitter = BaseJitter;
@@ -1,23 +0,0 @@
1
- /// <reference types="bn.js" />
2
- import { JitProxyClient, PriceType } from '../jitProxyClient';
3
- import { PublicKey } from '@solana/web3.js';
4
- import { AuctionSubscriber, BN, DriftClient, Order, UserAccount, UserStatsMap } from '@drift-labs/sdk';
5
- import { BaseJitter } from './baseJitter';
6
- export declare type UserFilter = (userAccount: UserAccount, userKey: string, order: Order) => boolean;
7
- export declare type JitParams = {
8
- bid: BN;
9
- ask: BN;
10
- minPosition: BN;
11
- maxPosition: any;
12
- priceType: PriceType;
13
- subAccountId?: number;
14
- };
15
- export declare class JitterShotgun extends BaseJitter {
16
- constructor({ auctionSubscriber, jitProxyClient, driftClient, userStatsMap, }: {
17
- driftClient: DriftClient;
18
- auctionSubscriber: AuctionSubscriber;
19
- jitProxyClient: JitProxyClient;
20
- userStatsMap?: UserStatsMap;
21
- });
22
- createTryFill(taker: UserAccount, takerKey: PublicKey, takerStatsKey: PublicKey, order: Order, orderSignature: string): () => Promise<void>;
23
- }
@@ -1,69 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JitterShotgun = void 0;
4
- const baseJitter_1 = require("./baseJitter");
5
- class JitterShotgun extends baseJitter_1.BaseJitter {
6
- constructor({ auctionSubscriber, jitProxyClient, driftClient, userStatsMap, }) {
7
- super({
8
- auctionSubscriber,
9
- jitProxyClient,
10
- driftClient,
11
- userStatsMap,
12
- });
13
- }
14
- createTryFill(taker, takerKey, takerStatsKey, order, orderSignature) {
15
- return async () => {
16
- let i = 0;
17
- while (i < 10) {
18
- const params = this.perpParams.get(order.marketIndex);
19
- if (!params) {
20
- this.onGoingAuctions.delete(orderSignature);
21
- return;
22
- }
23
- const takerStats = await this.userStatsMap.mustGet(taker.authority.toString());
24
- const referrerInfo = takerStats.getReferrerInfo();
25
- console.log(`Trying to fill ${orderSignature}`);
26
- try {
27
- const { txSig } = await this.jitProxyClient.jit({
28
- takerKey,
29
- takerStatsKey,
30
- taker,
31
- takerOrderId: order.orderId,
32
- maxPosition: params.maxPosition,
33
- minPosition: params.minPosition,
34
- bid: params.bid,
35
- ask: params.ask,
36
- postOnly: null,
37
- priceType: params.priceType,
38
- referrerInfo,
39
- subAccountId: params.subAccountId,
40
- });
41
- console.log(`Filled ${orderSignature} txSig ${txSig}`);
42
- await sleep(10000);
43
- this.onGoingAuctions.delete(orderSignature);
44
- return;
45
- }
46
- catch (e) {
47
- console.error(`Failed to fill ${orderSignature}`);
48
- if (e.message.includes('0x1770') || e.message.includes('0x1771')) {
49
- console.log('Order does not cross params yet, retrying');
50
- }
51
- else if (e.message.includes('0x1793')) {
52
- console.log('Oracle invalid, retrying');
53
- }
54
- else {
55
- await sleep(10000);
56
- this.onGoingAuctions.delete(orderSignature);
57
- return;
58
- }
59
- }
60
- i++;
61
- }
62
- this.onGoingAuctions.delete(orderSignature);
63
- };
64
- }
65
- }
66
- exports.JitterShotgun = JitterShotgun;
67
- function sleep(ms) {
68
- return new Promise((resolve) => setTimeout(resolve, ms));
69
- }
@@ -1,32 +0,0 @@
1
- import { JitProxyClient } from '../jitProxyClient';
2
- import { PublicKey } from '@solana/web3.js';
3
- import { AuctionSubscriber, DriftClient, OraclePriceData, Order, SlotSubscriber, UserAccount, UserStatsMap } from '@drift-labs/sdk';
4
- import { BaseJitter } from './baseJitter';
5
- declare type AuctionAndOrderDetails = {
6
- slotsTilCross: number;
7
- willCross: boolean;
8
- bid: number;
9
- ask: number;
10
- auctionStartPrice: number;
11
- auctionEndPrice: number;
12
- stepSize: number;
13
- oraclePrice: OraclePriceData;
14
- };
15
- export declare class JitterSniper extends BaseJitter {
16
- slotSubscriber: SlotSubscriber;
17
- userStatsMap: UserStatsMap;
18
- constructor({ auctionSubscriber, slotSubscriber, jitProxyClient, driftClient, userStatsMap, }: {
19
- driftClient: DriftClient;
20
- slotSubscriber: SlotSubscriber;
21
- auctionSubscriber: AuctionSubscriber;
22
- jitProxyClient: JitProxyClient;
23
- userStatsMap?: UserStatsMap;
24
- });
25
- createTryFill(taker: UserAccount, takerKey: PublicKey, takerStatsKey: PublicKey, order: Order, orderSignature: string): () => Promise<void>;
26
- getAuctionAndOrderDetails(order: Order): AuctionAndOrderDetails;
27
- waitForSlotOrCrossOrExpiry(targetSlot: number, order: Order, initialDetails: AuctionAndOrderDetails): Promise<{
28
- slot: number;
29
- updatedDetails: AuctionAndOrderDetails;
30
- }>;
31
- }
32
- export {};
@@ -1,201 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.JitterSniper = void 0;
4
- const sdk_1 = require("@drift-labs/sdk");
5
- const baseJitter_1 = require("./baseJitter");
6
- class JitterSniper extends baseJitter_1.BaseJitter {
7
- constructor({ auctionSubscriber, slotSubscriber, jitProxyClient, driftClient, userStatsMap, }) {
8
- super({
9
- auctionSubscriber,
10
- jitProxyClient,
11
- driftClient,
12
- userStatsMap,
13
- });
14
- this.slotSubscriber = slotSubscriber;
15
- }
16
- createTryFill(taker, takerKey, takerStatsKey, order, orderSignature) {
17
- return async () => {
18
- const params = this.perpParams.get(order.marketIndex);
19
- if (!params) {
20
- this.onGoingAuctions.delete(orderSignature);
21
- return;
22
- }
23
- const takerStats = await this.userStatsMap.mustGet(taker.authority.toString());
24
- const referrerInfo = takerStats.getReferrerInfo();
25
- const { slotsTilCross, willCross, bid, ask, auctionStartPrice, auctionEndPrice, stepSize, oraclePrice, } = this.getAuctionAndOrderDetails(order);
26
- console.log(`
27
- Taker wants to ${JSON.stringify(order.direction)}, order slot is ${order.slot.toNumber()},
28
- My market: ${bid}@${ask},
29
- Auction: ${auctionStartPrice} -> ${auctionEndPrice}, step size ${stepSize}
30
- Current slot: ${this.slotSubscriber.currentSlot}, Order slot: ${order.slot.toNumber()},
31
- Will cross?: ${willCross}
32
- Slots to wait: ${slotsTilCross}. Target slot = ${order.slot.toNumber() + slotsTilCross}
33
- `);
34
- this.waitForSlotOrCrossOrExpiry(willCross
35
- ? order.slot.toNumber() + slotsTilCross
36
- : order.slot.toNumber() + order.auctionDuration + 1, order, {
37
- slotsTilCross,
38
- willCross,
39
- bid,
40
- ask,
41
- auctionStartPrice,
42
- auctionEndPrice,
43
- stepSize,
44
- oraclePrice,
45
- }).then(async ({ slot, updatedDetails }) => {
46
- if (slot === -1) {
47
- console.log('Auction expired without crossing');
48
- this.onGoingAuctions.delete(orderSignature);
49
- return;
50
- }
51
- const params = sdk_1.isVariant(order.marketType, 'perp')
52
- ? this.perpParams.get(order.marketIndex)
53
- : this.spotParams.get(order.marketIndex);
54
- const bid = sdk_1.isVariant(params.priceType, 'oracle')
55
- ? sdk_1.convertToNumber(oraclePrice.price.add(params.bid), sdk_1.PRICE_PRECISION)
56
- : sdk_1.convertToNumber(params.bid, sdk_1.PRICE_PRECISION);
57
- const ask = sdk_1.isVariant(params.priceType, 'oracle')
58
- ? sdk_1.convertToNumber(oraclePrice.price.add(params.ask), sdk_1.PRICE_PRECISION)
59
- : sdk_1.convertToNumber(params.ask, sdk_1.PRICE_PRECISION);
60
- const auctionPrice = sdk_1.convertToNumber(sdk_1.getAuctionPrice(order, slot, updatedDetails.oraclePrice.price), sdk_1.PRICE_PRECISION);
61
- console.log(`
62
- Expected auction price: ${auctionStartPrice + slotsTilCross * stepSize}
63
- Actual auction price: ${auctionPrice}
64
- -----------------
65
- Looking for slot ${order.slot.toNumber() + slotsTilCross}
66
- Got slot ${slot}
67
- `);
68
- console.log(`Trying to fill ${orderSignature} with:
69
- market: ${bid}@${ask}
70
- auction price: ${auctionPrice}
71
- submitting" ${sdk_1.convertToNumber(params.bid, sdk_1.PRICE_PRECISION)}@${sdk_1.convertToNumber(params.ask, sdk_1.PRICE_PRECISION)}
72
- `);
73
- let i = 0;
74
- while (i < 3) {
75
- try {
76
- const { txSig } = await this.jitProxyClient.jit({
77
- takerKey,
78
- takerStatsKey,
79
- taker,
80
- takerOrderId: order.orderId,
81
- maxPosition: params.maxPosition,
82
- minPosition: params.minPosition,
83
- bid: params.bid,
84
- ask: params.ask,
85
- postOnly: null,
86
- priceType: params.priceType,
87
- referrerInfo,
88
- subAccountId: params.subAccountId,
89
- });
90
- console.log(`Filled ${orderSignature} txSig ${txSig}`);
91
- await sleep(3000);
92
- this.onGoingAuctions.delete(orderSignature);
93
- return;
94
- }
95
- catch (e) {
96
- console.error(`Failed to fill ${orderSignature}`);
97
- if (e.message.includes('0x1770') || e.message.includes('0x1771')) {
98
- console.log('Order does not cross params yet');
99
- }
100
- else if (e.message.includes('0x1793')) {
101
- console.log('Oracle invalid');
102
- }
103
- else {
104
- await sleep(3000);
105
- this.onGoingAuctions.delete(orderSignature);
106
- return;
107
- }
108
- }
109
- await sleep(50);
110
- i++;
111
- }
112
- });
113
- this.onGoingAuctions.delete(orderSignature);
114
- };
115
- }
116
- getAuctionAndOrderDetails(order) {
117
- // Find number of slots until the order is expected to be in cross
118
- const params = sdk_1.isVariant(order.marketType, 'perp')
119
- ? this.perpParams.get(order.marketIndex)
120
- : this.spotParams.get(order.marketIndex);
121
- const oraclePrice = sdk_1.isVariant(order.marketType, 'perp')
122
- ? this.driftClient.getOracleDataForPerpMarket(order.marketIndex)
123
- : this.driftClient.getOracleDataForSpotMarket(order.marketIndex);
124
- const makerOrderDir = sdk_1.isVariant(order.direction, 'long') ? 'sell' : 'buy';
125
- const auctionStartPrice = sdk_1.convertToNumber(sdk_1.isVariant(order.orderType, 'oracle')
126
- ? sdk_1.getAuctionPriceForOracleOffsetAuction(order, order.slot.toNumber(), oraclePrice.price)
127
- : order.auctionStartPrice, sdk_1.PRICE_PRECISION);
128
- const auctionEndPrice = sdk_1.convertToNumber(sdk_1.isVariant(order.orderType, 'oracle')
129
- ? sdk_1.getAuctionPriceForOracleOffsetAuction(order, order.slot.toNumber() + order.auctionDuration - 1, oraclePrice.price)
130
- : order.auctionEndPrice, sdk_1.PRICE_PRECISION);
131
- const bid = sdk_1.isVariant(params.priceType, 'oracle')
132
- ? sdk_1.convertToNumber(oraclePrice.price.add(params.bid), sdk_1.PRICE_PRECISION)
133
- : sdk_1.convertToNumber(params.bid, sdk_1.PRICE_PRECISION);
134
- const ask = sdk_1.isVariant(params.priceType, 'oracle')
135
- ? sdk_1.convertToNumber(oraclePrice.price.add(params.ask), sdk_1.PRICE_PRECISION)
136
- : sdk_1.convertToNumber(params.ask, sdk_1.PRICE_PRECISION);
137
- let slotsTilCross = 0;
138
- let willCross = false;
139
- const stepSize = (auctionEndPrice - auctionStartPrice) / (order.auctionDuration - 1);
140
- while (slotsTilCross < order.auctionDuration) {
141
- if (makerOrderDir === 'buy') {
142
- if (sdk_1.convertToNumber(sdk_1.getAuctionPrice(order, order.slot.toNumber() + slotsTilCross, oraclePrice.price), sdk_1.PRICE_PRECISION) <= bid) {
143
- willCross = true;
144
- break;
145
- }
146
- }
147
- else {
148
- if (sdk_1.convertToNumber(sdk_1.getAuctionPrice(order, order.slot.toNumber() + slotsTilCross, oraclePrice.price), sdk_1.PRICE_PRECISION) >= ask) {
149
- willCross = true;
150
- break;
151
- }
152
- }
153
- slotsTilCross++;
154
- }
155
- return {
156
- slotsTilCross,
157
- willCross,
158
- bid,
159
- ask,
160
- auctionStartPrice,
161
- auctionEndPrice,
162
- stepSize,
163
- oraclePrice,
164
- };
165
- }
166
- async waitForSlotOrCrossOrExpiry(targetSlot, order, initialDetails) {
167
- const auctionEndSlot = order.auctionDuration + order.slot.toNumber();
168
- let currentDetails = initialDetails;
169
- let willCross = initialDetails.willCross;
170
- if (this.slotSubscriber.currentSlot > auctionEndSlot) {
171
- return new Promise((resolve) => resolve({ slot: -1, updatedDetails: currentDetails }));
172
- }
173
- return new Promise((resolve) => {
174
- // Immediately return if we are past target slot
175
- const slotListener = (slot) => {
176
- if (slot >= targetSlot && willCross) {
177
- resolve({ slot, updatedDetails: currentDetails });
178
- }
179
- };
180
- // Otherwise listen for new slots in case we hit the target slot and we're gonna cross
181
- this.slotSubscriber.eventEmitter.once('newSlot', slotListener);
182
- // Update target slot as the bid/ask and the oracle changes
183
- const intervalId = setInterval(async () => {
184
- if (this.slotSubscriber.currentSlot >= auctionEndSlot) {
185
- this.slotSubscriber.eventEmitter.removeListener('newSlot', slotListener);
186
- clearInterval(intervalId);
187
- resolve({ slot: -1, updatedDetails: currentDetails });
188
- }
189
- currentDetails = this.getAuctionAndOrderDetails(order);
190
- willCross = currentDetails.willCross;
191
- if (willCross) {
192
- targetSlot = order.slot.toNumber() + currentDetails.slotsTilCross;
193
- }
194
- }, 50);
195
- });
196
- }
197
- }
198
- exports.JitterSniper = JitterSniper;
199
- function sleep(ms) {
200
- return new Promise((resolve) => setTimeout(resolve, ms));
201
- }
package/lib/jitter.d.ts DELETED
@@ -1,35 +0,0 @@
1
- /// <reference types="bn.js" />
2
- import { JitProxyClient, PriceType } from './jitProxyClient';
3
- import { PublicKey } from '@solana/web3.js';
4
- import { AuctionSubscriber, BN, DriftClient, Order, UserAccount, UserStatsMap } from '@drift-labs/sdk';
5
- export declare type UserFilter = (userAccount: UserAccount, userKey: string, order: Order) => boolean;
6
- export declare type JitParams = {
7
- bid: BN;
8
- ask: BN;
9
- minPosition: BN;
10
- maxPosition: any;
11
- priceType: PriceType;
12
- subAccountId?: number;
13
- };
14
- export declare class Jitter {
15
- auctionSubscriber: AuctionSubscriber;
16
- driftClient: DriftClient;
17
- jitProxyClient: JitProxyClient;
18
- userStatsMap: UserStatsMap;
19
- perpParams: Map<number, JitParams>;
20
- spotParams: Map<number, JitParams>;
21
- onGoingAuctions: Map<string, Promise<void>>;
22
- userFilter: UserFilter;
23
- constructor({ auctionSubscriber, jitProxyClient, driftClient, userStatsMap, }: {
24
- driftClient: DriftClient;
25
- auctionSubscriber: AuctionSubscriber;
26
- jitProxyClient: JitProxyClient;
27
- userStatsMap?: UserStatsMap;
28
- });
29
- subscribe(): Promise<void>;
30
- createTryFill(taker: UserAccount, takerKey: PublicKey, takerStatsKey: PublicKey, order: Order, orderSignature: string): () => Promise<void>;
31
- getOrderSignatures(takerKey: string, orderId: number): string;
32
- updatePerpParams(marketIndex: number, params: JitParams): void;
33
- updateSpotParams(marketIndex: number, params: JitParams): void;
34
- setUserFilter(userFilter: UserFilter | undefined): void;
35
- }
package/lib/jitter.js DELETED
@@ -1,125 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Jitter = void 0;
4
- const sdk_1 = require("@drift-labs/sdk");
5
- class Jitter {
6
- constructor({ auctionSubscriber, jitProxyClient, driftClient, userStatsMap, }) {
7
- this.perpParams = new Map();
8
- this.spotParams = new Map();
9
- this.onGoingAuctions = new Map();
10
- this.auctionSubscriber = auctionSubscriber;
11
- this.driftClient = driftClient;
12
- this.jitProxyClient = jitProxyClient;
13
- this.userStatsMap = userStatsMap || new sdk_1.UserStatsMap(this.driftClient, {
14
- type: 'polling',
15
- accountLoader: new sdk_1.BulkAccountLoader(this.driftClient.connection, 'confirmed', 0),
16
- });
17
- }
18
- async subscribe() {
19
- await this.driftClient.subscribe();
20
- await this.userStatsMap.subscribe();
21
- await this.auctionSubscriber.subscribe();
22
- this.auctionSubscriber.eventEmitter.on('onAccountUpdate', async (taker, takerKey, slot) => {
23
- const takerKeyString = takerKey.toBase58();
24
- const takerStatsKey = sdk_1.getUserStatsAccountPublicKey(this.driftClient.program.programId, taker.authority);
25
- for (const order of taker.orders) {
26
- if (!sdk_1.isVariant(order.status, 'open')) {
27
- continue;
28
- }
29
- if (!sdk_1.hasAuctionPrice(order, slot)) {
30
- continue;
31
- }
32
- if (this.userFilter) {
33
- if (this.userFilter(taker, takerKeyString, order)) {
34
- return;
35
- }
36
- }
37
- const orderSignature = this.getOrderSignatures(takerKeyString, order.orderId);
38
- if (this.onGoingAuctions.has(orderSignature)) {
39
- continue;
40
- }
41
- if (sdk_1.isVariant(order.marketType, 'perp')) {
42
- if (!this.perpParams.has(order.marketIndex)) {
43
- return;
44
- }
45
- const promise = this.createTryFill(taker, takerKey, takerStatsKey, order, orderSignature).bind(this)();
46
- this.onGoingAuctions.set(orderSignature, promise);
47
- }
48
- else {
49
- if (!this.spotParams.has(order.marketIndex)) {
50
- return;
51
- }
52
- const promise = this.createTryFill(taker, takerKey, takerStatsKey, order, orderSignature).bind(this)();
53
- this.onGoingAuctions.set(orderSignature, promise);
54
- }
55
- }
56
- });
57
- }
58
- createTryFill(taker, takerKey, takerStatsKey, order, orderSignature) {
59
- return async () => {
60
- let i = 0;
61
- while (i < 10) {
62
- const params = this.perpParams.get(order.marketIndex);
63
- if (!params) {
64
- this.onGoingAuctions.delete(orderSignature);
65
- return;
66
- }
67
- const takerStats = await this.userStatsMap.mustGet(taker.authority.toString());
68
- const referrerInfo = takerStats.getReferrerInfo();
69
- console.log(`Trying to fill ${orderSignature}`);
70
- try {
71
- const { txSig } = await this.jitProxyClient.jit({
72
- takerKey,
73
- takerStatsKey,
74
- taker,
75
- takerOrderId: order.orderId,
76
- maxPosition: params.maxPosition,
77
- minPosition: params.minPosition,
78
- bid: params.bid,
79
- ask: params.ask,
80
- postOnly: null,
81
- priceType: params.priceType,
82
- referrerInfo,
83
- subAccountId: params.subAccountId,
84
- });
85
- console.log(`Filled ${orderSignature} txSig ${txSig}`);
86
- await sleep(10000);
87
- this.onGoingAuctions.delete(orderSignature);
88
- return;
89
- }
90
- catch (e) {
91
- console.error(`Failed to fill ${orderSignature}`);
92
- if (e.message.includes('0x1770') || e.message.includes('0x1771')) {
93
- console.log('Order does not cross params yet, retrying');
94
- }
95
- else if (e.message.includes('0x1793')) {
96
- console.log('Oracle invalid, retrying');
97
- }
98
- else {
99
- await sleep(10000);
100
- this.onGoingAuctions.delete(orderSignature);
101
- return;
102
- }
103
- }
104
- i++;
105
- }
106
- this.onGoingAuctions.delete(orderSignature);
107
- };
108
- }
109
- getOrderSignatures(takerKey, orderId) {
110
- return `${takerKey}-${orderId}`;
111
- }
112
- updatePerpParams(marketIndex, params) {
113
- this.perpParams.set(marketIndex, params);
114
- }
115
- updateSpotParams(marketIndex, params) {
116
- this.spotParams.set(marketIndex, params);
117
- }
118
- setUserFilter(userFilter) {
119
- this.userFilter = userFilter;
120
- }
121
- }
122
- exports.Jitter = Jitter;
123
- function sleep(ms) {
124
- return new Promise((resolve) => setTimeout(resolve, ms));
125
- }
@@ -1,147 +0,0 @@
1
- export declare type JitProxy = {
2
- version: '0.1.0';
3
- name: 'jit_proxy';
4
- instructions: [
5
- {
6
- name: 'jit';
7
- accounts: [
8
- {
9
- name: 'state';
10
- isMut: false;
11
- isSigner: false;
12
- },
13
- {
14
- name: 'user';
15
- isMut: true;
16
- isSigner: false;
17
- },
18
- {
19
- name: 'userStats';
20
- isMut: true;
21
- isSigner: false;
22
- },
23
- {
24
- name: 'taker';
25
- isMut: true;
26
- isSigner: false;
27
- },
28
- {
29
- name: 'takerStats';
30
- isMut: true;
31
- isSigner: false;
32
- },
33
- {
34
- name: 'authority';
35
- isMut: false;
36
- isSigner: true;
37
- },
38
- {
39
- name: 'driftProgram';
40
- isMut: false;
41
- isSigner: false;
42
- }
43
- ];
44
- args: [
45
- {
46
- name: 'params';
47
- type: {
48
- defined: 'JitParams';
49
- };
50
- }
51
- ];
52
- }
53
- ];
54
- types: [
55
- {
56
- name: 'JitParams';
57
- type: {
58
- kind: 'struct';
59
- fields: [
60
- {
61
- name: 'takerOrderId';
62
- type: 'u32';
63
- },
64
- {
65
- name: 'maxPosition';
66
- type: 'i64';
67
- },
68
- {
69
- name: 'minPosition';
70
- type: 'i64';
71
- },
72
- {
73
- name: 'bid';
74
- type: 'i64';
75
- },
76
- {
77
- name: 'ask';
78
- type: 'i64';
79
- },
80
- {
81
- name: 'priceType';
82
- type: {
83
- defined: 'PriceType';
84
- };
85
- },
86
- {
87
- name: 'postOnly';
88
- type: {
89
- option: {
90
- defined: 'PostOnlyParam';
91
- };
92
- };
93
- }
94
- ];
95
- };
96
- },
97
- {
98
- name: 'PostOnlyParam';
99
- type: {
100
- kind: 'enum';
101
- variants: [
102
- {
103
- name: 'None';
104
- },
105
- {
106
- name: 'MustPostOnly';
107
- },
108
- {
109
- name: 'TryPostOnly';
110
- }
111
- ];
112
- };
113
- },
114
- {
115
- name: 'PriceType';
116
- type: {
117
- kind: 'enum';
118
- variants: [
119
- {
120
- name: 'Limit';
121
- },
122
- {
123
- name: 'Oracle';
124
- }
125
- ];
126
- };
127
- }
128
- ];
129
- errors: [
130
- {
131
- code: 6000;
132
- name: 'BidNotCrossed';
133
- msg: 'BidNotCrossed';
134
- },
135
- {
136
- code: 6001;
137
- name: 'AskNotCrossed';
138
- msg: 'AskNotCrossed';
139
- },
140
- {
141
- code: 6002;
142
- name: 'TakerOrderNotFound';
143
- msg: 'TakerOrderNotFound';
144
- }
145
- ];
146
- };
147
- export declare const IDL: JitProxy;
@@ -1,149 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IDL = void 0;
4
- exports.IDL = {
5
- version: '0.1.0',
6
- name: 'jit_proxy',
7
- instructions: [
8
- {
9
- name: 'jit',
10
- accounts: [
11
- {
12
- name: 'state',
13
- isMut: false,
14
- isSigner: false,
15
- },
16
- {
17
- name: 'user',
18
- isMut: true,
19
- isSigner: false,
20
- },
21
- {
22
- name: 'userStats',
23
- isMut: true,
24
- isSigner: false,
25
- },
26
- {
27
- name: 'taker',
28
- isMut: true,
29
- isSigner: false,
30
- },
31
- {
32
- name: 'takerStats',
33
- isMut: true,
34
- isSigner: false,
35
- },
36
- {
37
- name: 'authority',
38
- isMut: false,
39
- isSigner: true,
40
- },
41
- {
42
- name: 'driftProgram',
43
- isMut: false,
44
- isSigner: false,
45
- },
46
- ],
47
- args: [
48
- {
49
- name: 'params',
50
- type: {
51
- defined: 'JitParams',
52
- },
53
- },
54
- ],
55
- },
56
- ],
57
- types: [
58
- {
59
- name: 'JitParams',
60
- type: {
61
- kind: 'struct',
62
- fields: [
63
- {
64
- name: 'takerOrderId',
65
- type: 'u32',
66
- },
67
- {
68
- name: 'maxPosition',
69
- type: 'i64',
70
- },
71
- {
72
- name: 'minPosition',
73
- type: 'i64',
74
- },
75
- {
76
- name: 'bid',
77
- type: 'i64',
78
- },
79
- {
80
- name: 'ask',
81
- type: 'i64',
82
- },
83
- {
84
- name: 'priceType',
85
- type: {
86
- defined: 'PriceType',
87
- },
88
- },
89
- {
90
- name: 'postOnly',
91
- type: {
92
- option: {
93
- defined: 'PostOnlyParam',
94
- },
95
- },
96
- },
97
- ],
98
- },
99
- },
100
- {
101
- name: 'PostOnlyParam',
102
- type: {
103
- kind: 'enum',
104
- variants: [
105
- {
106
- name: 'None',
107
- },
108
- {
109
- name: 'MustPostOnly',
110
- },
111
- {
112
- name: 'TryPostOnly',
113
- },
114
- ],
115
- },
116
- },
117
- {
118
- name: 'PriceType',
119
- type: {
120
- kind: 'enum',
121
- variants: [
122
- {
123
- name: 'Limit',
124
- },
125
- {
126
- name: 'Oracle',
127
- },
128
- ],
129
- },
130
- },
131
- ],
132
- errors: [
133
- {
134
- code: 6000,
135
- name: 'BidNotCrossed',
136
- msg: 'BidNotCrossed',
137
- },
138
- {
139
- code: 6001,
140
- name: 'AskNotCrossed',
141
- msg: 'AskNotCrossed',
142
- },
143
- {
144
- code: 6002,
145
- name: 'TakerOrderNotFound',
146
- msg: 'TakerOrderNotFound',
147
- },
148
- ],
149
- };