@drift-labs/sdk 2.41.0-beta.0 → 2.41.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/README.md +0 -1
- package/VERSION +1 -1
- package/bun.lockb +0 -0
- package/lib/accounts/types.d.ts +5 -1
- package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -1
- package/lib/accounts/webSocketAccountSubscriber.js +28 -2
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +2 -1
- package/lib/accounts/webSocketDriftClientAccountSubscriber.js +6 -5
- package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +32 -0
- package/lib/accounts/webSocketProgramAccountSubscriber.js +99 -0
- package/lib/accounts/webSocketUserAccountSubscriber.d.ts +2 -1
- package/lib/accounts/webSocketUserAccountSubscriber.js +3 -2
- package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +2 -1
- package/lib/accounts/webSocketUserStatsAccountSubsriber.js +3 -2
- package/lib/addresses/pda.d.ts +1 -0
- package/lib/addresses/pda.js +5 -1
- package/lib/adminClient.d.ts +2 -0
- package/lib/adminClient.js +20 -0
- package/lib/auctionSubscriber/auctionSubscriber.d.ts +3 -2
- package/lib/auctionSubscriber/auctionSubscriber.js +15 -7
- package/lib/auctionSubscriber/types.d.ts +1 -0
- package/lib/dlob/DLOB.d.ts +2 -6
- package/lib/dlob/DLOB.js +9 -11
- package/lib/dlob/DLOBSubscriber.js +4 -7
- package/lib/dlob/orderBookLevels.d.ts +4 -2
- package/lib/dlob/orderBookLevels.js +79 -16
- package/lib/driftClient.d.ts +2 -1
- package/lib/driftClient.js +13 -14
- package/lib/driftClientConfig.d.ts +1 -0
- package/lib/factory/bigNum.js +4 -2
- package/lib/idl/drift.json +222 -2
- package/lib/jupiter/jupiterClient.d.ts +4 -1
- package/lib/jupiter/jupiterClient.js +4 -1
- package/lib/math/amm.js +7 -2
- package/lib/math/auction.d.ts +12 -1
- package/lib/math/auction.js +22 -1
- package/lib/math/market.js +2 -4
- package/lib/math/superStake.d.ts +51 -4
- package/lib/math/superStake.js +173 -21
- package/lib/math/trade.js +2 -4
- package/lib/math/utils.d.ts +1 -0
- package/lib/math/utils.js +10 -1
- package/lib/orderSubscriber/OrderSubscriber.d.ts +1 -3
- package/lib/orderSubscriber/OrderSubscriber.js +4 -3
- package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -2
- package/lib/orderSubscriber/WebsocketSubscription.js +15 -12
- package/lib/orderSubscriber/types.d.ts +1 -0
- package/lib/user.d.ts +2 -2
- package/lib/user.js +5 -5
- package/package.json +2 -1
- package/src/accounts/types.ts +8 -1
- package/src/accounts/webSocketAccountSubscriber.ts +36 -2
- package/src/accounts/webSocketDriftClientAccountSubscriber.ts +15 -5
- package/src/accounts/webSocketProgramAccountSubscriber.ts +152 -0
- package/src/accounts/webSocketUserAccountSubscriber.ts +10 -2
- package/src/accounts/webSocketUserStatsAccountSubsriber.ts +10 -2
- package/src/addresses/pda.ts +9 -0
- package/src/adminClient.ts +32 -0
- package/src/auctionSubscriber/auctionSubscriber.ts +30 -21
- package/src/auctionSubscriber/types.ts +1 -0
- package/src/dlob/DLOB.ts +9 -32
- package/src/dlob/DLOBSubscriber.ts +8 -7
- package/src/dlob/orderBookLevels.ts +133 -32
- package/src/driftClient.ts +14 -16
- package/src/driftClientConfig.ts +1 -0
- package/src/factory/bigNum.ts +2 -0
- package/src/idl/drift.json +222 -2
- package/src/jupiter/jupiterClient.ts +6 -0
- package/src/math/amm.ts +9 -2
- package/src/math/auction.ts +36 -2
- package/src/math/market.ts +4 -9
- package/src/math/superStake.ts +247 -23
- package/src/math/trade.ts +3 -11
- package/src/math/utils.ts +12 -0
- package/src/orderSubscriber/OrderSubscriber.ts +12 -7
- package/src/orderSubscriber/WebsocketSubscription.ts +34 -23
- package/src/orderSubscriber/types.ts +1 -0
- package/src/user.ts +7 -5
- package/tests/amm/test.ts +402 -0
- package/tests/auctions/test.ts +66 -0
- package/tests/dlob/test.ts +1 -73
|
@@ -275,16 +275,20 @@ export class JupiterClient {
|
|
|
275
275
|
inputMint,
|
|
276
276
|
outputMint,
|
|
277
277
|
amount,
|
|
278
|
+
maxAccounts = 50, // 50 is an estimated amount with buffer
|
|
278
279
|
slippageBps = 50,
|
|
279
280
|
swapMode = 'ExactIn',
|
|
280
281
|
onlyDirectRoutes = false,
|
|
282
|
+
excludeDexes = [],
|
|
281
283
|
}: {
|
|
282
284
|
inputMint: PublicKey;
|
|
283
285
|
outputMint: PublicKey;
|
|
284
286
|
amount: BN;
|
|
287
|
+
maxAccounts?: number;
|
|
285
288
|
slippageBps?: number;
|
|
286
289
|
swapMode?: SwapMode;
|
|
287
290
|
onlyDirectRoutes?: boolean;
|
|
291
|
+
excludeDexes?: string[];
|
|
288
292
|
}): Promise<QuoteResponse> {
|
|
289
293
|
const params = new URLSearchParams({
|
|
290
294
|
inputMint: inputMint.toString(),
|
|
@@ -293,6 +297,8 @@ export class JupiterClient {
|
|
|
293
297
|
slippageBps: slippageBps.toString(),
|
|
294
298
|
swapMode,
|
|
295
299
|
onlyDirectRoutes: onlyDirectRoutes.toString(),
|
|
300
|
+
maxAccounts: maxAccounts.toString(),
|
|
301
|
+
excludeDexes: excludeDexes.join(','),
|
|
296
302
|
}).toString();
|
|
297
303
|
const quote = await (await fetch(`${this.url}/v6/quote?${params}`)).json();
|
|
298
304
|
return quote;
|
package/src/math/amm.ts
CHANGED
|
@@ -471,12 +471,19 @@ export function calculateVolSpreadBN(
|
|
|
471
471
|
clampMax
|
|
472
472
|
);
|
|
473
473
|
|
|
474
|
+
// only consider confidence interval at full value when above 25 bps
|
|
475
|
+
let confComponent = lastOracleConfPct;
|
|
476
|
+
|
|
477
|
+
if (lastOracleConfPct.lte(PRICE_PRECISION.div(new BN(400)))) {
|
|
478
|
+
confComponent = lastOracleConfPct.div(new BN(10));
|
|
479
|
+
}
|
|
480
|
+
|
|
474
481
|
const longVolSpread = BN.max(
|
|
475
|
-
|
|
482
|
+
confComponent,
|
|
476
483
|
volSpread.mul(longVolSpreadFactor).div(PERCENTAGE_PRECISION)
|
|
477
484
|
);
|
|
478
485
|
const shortVolSpread = BN.max(
|
|
479
|
-
|
|
486
|
+
confComponent,
|
|
480
487
|
volSpread.mul(shortVolSpreadFactor).div(PERCENTAGE_PRECISION)
|
|
481
488
|
);
|
|
482
489
|
|
package/src/math/auction.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isOneOfVariant, isVariant, Order } from '../types';
|
|
2
|
-
import { BN, ZERO } from '../.';
|
|
1
|
+
import { isOneOfVariant, isVariant, Order, PositionDirection } from '../types';
|
|
2
|
+
import { BN, ONE, ZERO } from '../.';
|
|
3
3
|
|
|
4
4
|
export function isAuctionComplete(order: Order, slot: number): boolean {
|
|
5
5
|
if (order.auctionDuration === 0) {
|
|
@@ -104,3 +104,37 @@ export function getAuctionPriceForOracleOffsetAuction(
|
|
|
104
104
|
|
|
105
105
|
return oraclePrice.add(priceOffset);
|
|
106
106
|
}
|
|
107
|
+
|
|
108
|
+
export function deriveOracleAuctionParams({
|
|
109
|
+
direction,
|
|
110
|
+
oraclePrice,
|
|
111
|
+
auctionStartPrice,
|
|
112
|
+
auctionEndPrice,
|
|
113
|
+
limitPrice,
|
|
114
|
+
}: {
|
|
115
|
+
direction: PositionDirection;
|
|
116
|
+
oraclePrice: BN;
|
|
117
|
+
auctionStartPrice: BN;
|
|
118
|
+
auctionEndPrice: BN;
|
|
119
|
+
limitPrice: BN;
|
|
120
|
+
}): { auctionStartPrice: BN; auctionEndPrice: BN; oraclePriceOffset: number } {
|
|
121
|
+
let oraclePriceOffset = limitPrice.sub(oraclePrice);
|
|
122
|
+
if (oraclePriceOffset.eq(ZERO)) {
|
|
123
|
+
oraclePriceOffset = isVariant(direction, 'long')
|
|
124
|
+
? auctionEndPrice.sub(oraclePrice).add(ONE)
|
|
125
|
+
: auctionEndPrice.sub(oraclePrice).sub(ONE);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let oraclePriceOffsetNum;
|
|
129
|
+
try {
|
|
130
|
+
oraclePriceOffsetNum = oraclePriceOffset.toNumber();
|
|
131
|
+
} catch (e) {
|
|
132
|
+
oraclePriceOffsetNum = 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
auctionStartPrice: auctionStartPrice.sub(oraclePrice),
|
|
137
|
+
auctionEndPrice: auctionEndPrice.sub(oraclePrice),
|
|
138
|
+
oraclePriceOffset: oraclePriceOffsetNum,
|
|
139
|
+
};
|
|
140
|
+
}
|
package/src/math/market.ts
CHANGED
|
@@ -279,27 +279,22 @@ export function calculateAvailablePerpLiquidity(
|
|
|
279
279
|
|
|
280
280
|
asks = asks.abs();
|
|
281
281
|
|
|
282
|
-
const
|
|
283
|
-
const askPrice = calculateAskPrice(market, oraclePriceData);
|
|
284
|
-
|
|
285
|
-
for (const bid of dlob.getMakerLimitBids(
|
|
282
|
+
for (const bid of dlob.getRestingLimitBids(
|
|
286
283
|
market.marketIndex,
|
|
287
284
|
slot,
|
|
288
285
|
MarketType.PERP,
|
|
289
|
-
oraclePriceData
|
|
290
|
-
askPrice
|
|
286
|
+
oraclePriceData
|
|
291
287
|
)) {
|
|
292
288
|
bids = bids.add(
|
|
293
289
|
bid.order.baseAssetAmount.sub(bid.order.baseAssetAmountFilled)
|
|
294
290
|
);
|
|
295
291
|
}
|
|
296
292
|
|
|
297
|
-
for (const ask of dlob.
|
|
293
|
+
for (const ask of dlob.getRestingLimitAsks(
|
|
298
294
|
market.marketIndex,
|
|
299
295
|
slot,
|
|
300
296
|
MarketType.PERP,
|
|
301
|
-
oraclePriceData
|
|
302
|
-
bidPrice
|
|
297
|
+
oraclePriceData
|
|
303
298
|
)) {
|
|
304
299
|
asks = asks.add(
|
|
305
300
|
ask.order.baseAssetAmount.sub(ask.order.baseAssetAmountFilled)
|
package/src/math/superStake.ts
CHANGED
|
@@ -12,20 +12,23 @@ import { User } from '../user';
|
|
|
12
12
|
import { DepositRecord, isVariant } from '../types';
|
|
13
13
|
import { LAMPORTS_PRECISION, ZERO } from '../constants/numericConstants';
|
|
14
14
|
import fetch from 'node-fetch';
|
|
15
|
+
import { checkSameDate } from './utils';
|
|
15
16
|
|
|
16
17
|
export async function findBestSuperStakeIxs({
|
|
18
|
+
marketIndex,
|
|
17
19
|
amount,
|
|
18
20
|
jupiterClient,
|
|
19
21
|
driftClient,
|
|
20
22
|
userAccountPublicKey,
|
|
21
|
-
|
|
23
|
+
price,
|
|
22
24
|
forceMarinade,
|
|
23
25
|
onlyDirectRoutes,
|
|
24
26
|
}: {
|
|
27
|
+
marketIndex: number;
|
|
25
28
|
amount: BN;
|
|
26
29
|
jupiterClient: JupiterClient;
|
|
27
30
|
driftClient: DriftClient;
|
|
28
|
-
|
|
31
|
+
price?: number;
|
|
29
32
|
userAccountPublicKey?: PublicKey;
|
|
30
33
|
forceMarinade?: boolean;
|
|
31
34
|
onlyDirectRoutes?: boolean;
|
|
@@ -35,9 +38,54 @@ export async function findBestSuperStakeIxs({
|
|
|
35
38
|
method: 'jupiter' | 'marinade';
|
|
36
39
|
price: number;
|
|
37
40
|
}> {
|
|
38
|
-
if (
|
|
41
|
+
if (marketIndex === 2) {
|
|
42
|
+
return findBestMSolSuperStakeIxs({
|
|
43
|
+
amount,
|
|
44
|
+
jupiterClient,
|
|
45
|
+
driftClient,
|
|
46
|
+
userAccountPublicKey,
|
|
47
|
+
price,
|
|
48
|
+
forceMarinade,
|
|
49
|
+
onlyDirectRoutes,
|
|
50
|
+
});
|
|
51
|
+
} else if (marketIndex === 6) {
|
|
52
|
+
return findBestJitoSolSuperStakeIxs({
|
|
53
|
+
amount,
|
|
54
|
+
jupiterClient,
|
|
55
|
+
driftClient,
|
|
56
|
+
userAccountPublicKey,
|
|
57
|
+
onlyDirectRoutes,
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
throw new Error(`Unsupported superstake market index: ${marketIndex}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function findBestMSolSuperStakeIxs({
|
|
65
|
+
amount,
|
|
66
|
+
jupiterClient,
|
|
67
|
+
driftClient,
|
|
68
|
+
userAccountPublicKey,
|
|
69
|
+
price,
|
|
70
|
+
forceMarinade,
|
|
71
|
+
onlyDirectRoutes,
|
|
72
|
+
}: {
|
|
73
|
+
amount: BN;
|
|
74
|
+
jupiterClient: JupiterClient;
|
|
75
|
+
driftClient: DriftClient;
|
|
76
|
+
price?: number;
|
|
77
|
+
userAccountPublicKey?: PublicKey;
|
|
78
|
+
forceMarinade?: boolean;
|
|
79
|
+
onlyDirectRoutes?: boolean;
|
|
80
|
+
}): Promise<{
|
|
81
|
+
ixs: TransactionInstruction[];
|
|
82
|
+
lookupTables: AddressLookupTableAccount[];
|
|
83
|
+
method: 'jupiter' | 'marinade';
|
|
84
|
+
price: number;
|
|
85
|
+
}> {
|
|
86
|
+
if (!price) {
|
|
39
87
|
const marinadeProgram = getMarinadeFinanceProgram(driftClient.provider);
|
|
40
|
-
|
|
88
|
+
price = await getMarinadeMSolPrice(marinadeProgram);
|
|
41
89
|
}
|
|
42
90
|
|
|
43
91
|
const solMint = driftClient.getSpotMarketAccount(1).mint;
|
|
@@ -59,7 +107,7 @@ export async function findBestSuperStakeIxs({
|
|
|
59
107
|
console.error('Error getting jupiter price', e);
|
|
60
108
|
}
|
|
61
109
|
|
|
62
|
-
if (!jupiterPrice ||
|
|
110
|
+
if (!jupiterPrice || price <= jupiterPrice || forceMarinade) {
|
|
63
111
|
const ixs = await driftClient.getStakeForMSOLIx({
|
|
64
112
|
amount,
|
|
65
113
|
userAccountPublicKey,
|
|
@@ -68,7 +116,7 @@ export async function findBestSuperStakeIxs({
|
|
|
68
116
|
method: 'marinade',
|
|
69
117
|
ixs,
|
|
70
118
|
lookupTables: [],
|
|
71
|
-
price:
|
|
119
|
+
price: price,
|
|
72
120
|
};
|
|
73
121
|
} else {
|
|
74
122
|
const { ixs, lookupTables } = await driftClient.getJupiterSwapIx({
|
|
@@ -88,10 +136,170 @@ export async function findBestSuperStakeIxs({
|
|
|
88
136
|
}
|
|
89
137
|
}
|
|
90
138
|
|
|
139
|
+
export async function findBestJitoSolSuperStakeIxs({
|
|
140
|
+
amount,
|
|
141
|
+
jupiterClient,
|
|
142
|
+
driftClient,
|
|
143
|
+
userAccountPublicKey,
|
|
144
|
+
onlyDirectRoutes,
|
|
145
|
+
}: {
|
|
146
|
+
amount: BN;
|
|
147
|
+
jupiterClient: JupiterClient;
|
|
148
|
+
driftClient: DriftClient;
|
|
149
|
+
userAccountPublicKey?: PublicKey;
|
|
150
|
+
onlyDirectRoutes?: boolean;
|
|
151
|
+
}): Promise<{
|
|
152
|
+
ixs: TransactionInstruction[];
|
|
153
|
+
lookupTables: AddressLookupTableAccount[];
|
|
154
|
+
method: 'jupiter' | 'marinade';
|
|
155
|
+
price: number;
|
|
156
|
+
}> {
|
|
157
|
+
const solMint = driftClient.getSpotMarketAccount(1).mint;
|
|
158
|
+
const JitoSolMint = driftClient.getSpotMarketAccount(6).mint;
|
|
159
|
+
|
|
160
|
+
let jupiterPrice;
|
|
161
|
+
let bestRoute;
|
|
162
|
+
try {
|
|
163
|
+
const jupiterRoutes = await jupiterClient.getRoutes({
|
|
164
|
+
inputMint: solMint,
|
|
165
|
+
outputMint: JitoSolMint,
|
|
166
|
+
amount,
|
|
167
|
+
onlyDirectRoutes,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
bestRoute = jupiterRoutes[0];
|
|
171
|
+
jupiterPrice = bestRoute.inAmount / bestRoute.outAmount;
|
|
172
|
+
} catch (e) {
|
|
173
|
+
console.error('Error getting jupiter price', e);
|
|
174
|
+
throw e;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const { ixs, lookupTables } = await driftClient.getJupiterSwapIx({
|
|
178
|
+
inMarketIndex: 1,
|
|
179
|
+
outMarketIndex: 6,
|
|
180
|
+
route: bestRoute,
|
|
181
|
+
jupiterClient,
|
|
182
|
+
amount,
|
|
183
|
+
userAccountPublicKey,
|
|
184
|
+
});
|
|
185
|
+
return {
|
|
186
|
+
method: 'jupiter',
|
|
187
|
+
ixs,
|
|
188
|
+
lookupTables,
|
|
189
|
+
price: jupiterPrice,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export type JITO_SOL_METRICS_ENDPOINT_RESPONSE = {
|
|
194
|
+
data: {
|
|
195
|
+
getStakePoolStats: {
|
|
196
|
+
tvl: {
|
|
197
|
+
// TVL in SOL, BN
|
|
198
|
+
data: number;
|
|
199
|
+
date: string;
|
|
200
|
+
}[];
|
|
201
|
+
supply: {
|
|
202
|
+
// jitoSOL supply
|
|
203
|
+
data: number;
|
|
204
|
+
date: string;
|
|
205
|
+
}[];
|
|
206
|
+
apy: {
|
|
207
|
+
data: number;
|
|
208
|
+
date: string;
|
|
209
|
+
}[];
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const JITO_SOL_START_DATE = '2022-10-31T00:00:00Z';
|
|
215
|
+
|
|
216
|
+
export async function fetchJitoSolMetrics() {
|
|
217
|
+
const res = await fetch('https://kobe.mainnet.jito.network/', {
|
|
218
|
+
body: JSON.stringify({
|
|
219
|
+
operationName: 'QueryRoot',
|
|
220
|
+
variables: {
|
|
221
|
+
request: {
|
|
222
|
+
bucketType: 'DAILY',
|
|
223
|
+
rangeFilter: {
|
|
224
|
+
start: JITO_SOL_START_DATE,
|
|
225
|
+
end: new Date().toISOString(),
|
|
226
|
+
},
|
|
227
|
+
sortBy: {
|
|
228
|
+
order: 'ASC',
|
|
229
|
+
field: 'BLOCK_TIME',
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
query: `
|
|
234
|
+
query QueryRoot($request: GetStakePoolStatsRequest!) {
|
|
235
|
+
getStakePoolStats(req: $request) {
|
|
236
|
+
tvl {
|
|
237
|
+
data
|
|
238
|
+
date
|
|
239
|
+
}
|
|
240
|
+
apy {
|
|
241
|
+
data
|
|
242
|
+
date
|
|
243
|
+
}
|
|
244
|
+
supply {
|
|
245
|
+
data
|
|
246
|
+
date
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
`,
|
|
251
|
+
}),
|
|
252
|
+
method: 'POST',
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const data: JITO_SOL_METRICS_ENDPOINT_RESPONSE = await res.json();
|
|
256
|
+
|
|
257
|
+
return data;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const getJitoSolHistoricalPriceMap = async (timestamps: number[]) => {
|
|
261
|
+
try {
|
|
262
|
+
const data = await fetchJitoSolMetrics();
|
|
263
|
+
const jitoSolHistoricalPriceMap = new Map<number, number>();
|
|
264
|
+
const jitoSolHistoricalPriceInSol = [];
|
|
265
|
+
|
|
266
|
+
for (let i = 0; i < data.data.getStakePoolStats.supply.length; i++) {
|
|
267
|
+
const priceInSol =
|
|
268
|
+
data.data.getStakePoolStats.tvl[i].data /
|
|
269
|
+
10 ** 9 /
|
|
270
|
+
data.data.getStakePoolStats.supply[i].data;
|
|
271
|
+
jitoSolHistoricalPriceInSol.push({
|
|
272
|
+
price: priceInSol,
|
|
273
|
+
ts: data.data.getStakePoolStats.tvl[i].date,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
for (const timestamp of timestamps) {
|
|
278
|
+
const date = new Date(timestamp * 1000);
|
|
279
|
+
const dateString = date.toISOString();
|
|
280
|
+
|
|
281
|
+
const price = jitoSolHistoricalPriceInSol.find((p) =>
|
|
282
|
+
checkSameDate(p.ts, dateString)
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
if (price) {
|
|
286
|
+
jitoSolHistoricalPriceMap.set(timestamp, price.price);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return jitoSolHistoricalPriceMap;
|
|
291
|
+
} catch (err) {
|
|
292
|
+
console.error(err);
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
91
297
|
export async function calculateSolEarned({
|
|
298
|
+
marketIndex,
|
|
92
299
|
user,
|
|
93
300
|
depositRecords,
|
|
94
301
|
}: {
|
|
302
|
+
marketIndex: number;
|
|
95
303
|
user: User;
|
|
96
304
|
depositRecords: DepositRecord[];
|
|
97
305
|
}): Promise<BN> {
|
|
@@ -101,20 +309,24 @@ export async function calculateSolEarned({
|
|
|
101
309
|
...depositRecords.map((r) => r.ts.toNumber()),
|
|
102
310
|
];
|
|
103
311
|
|
|
104
|
-
|
|
312
|
+
let lstRatios = new Map<number, number>();
|
|
105
313
|
|
|
106
|
-
const
|
|
314
|
+
const getMsolPrice = async (timestamp) => {
|
|
107
315
|
const date = new Date(timestamp * 1000); // Convert Unix timestamp to milliseconds
|
|
108
316
|
const swaggerApiDateTime = date.toISOString(); // Format date as swagger API date-time
|
|
109
317
|
const url = `https://api.marinade.finance/msol/price_sol?time=${swaggerApiDateTime}`;
|
|
110
318
|
const response = await fetch(url);
|
|
111
319
|
if (response.status === 200) {
|
|
112
320
|
const data = await response.json();
|
|
113
|
-
|
|
321
|
+
lstRatios.set(timestamp, data);
|
|
114
322
|
}
|
|
115
323
|
};
|
|
116
324
|
|
|
117
|
-
|
|
325
|
+
if (marketIndex === 2) {
|
|
326
|
+
await Promise.all(timestamps.map(getMsolPrice));
|
|
327
|
+
} else if (marketIndex === 6) {
|
|
328
|
+
lstRatios = await getJitoSolHistoricalPriceMap(timestamps);
|
|
329
|
+
}
|
|
118
330
|
|
|
119
331
|
let solEarned = ZERO;
|
|
120
332
|
for (const record of depositRecords) {
|
|
@@ -125,7 +337,7 @@ export async function calculateSolEarned({
|
|
|
125
337
|
solEarned = solEarned.add(record.amount);
|
|
126
338
|
}
|
|
127
339
|
} else if (record.marketIndex === 2) {
|
|
128
|
-
const msolRatio =
|
|
340
|
+
const msolRatio = lstRatios.get(record.ts.toNumber());
|
|
129
341
|
const msolRatioBN = new BN(msolRatio * LAMPORTS_PER_SOL);
|
|
130
342
|
|
|
131
343
|
const solAmount = record.amount.mul(msolRatioBN).div(LAMPORTS_PRECISION);
|
|
@@ -134,34 +346,46 @@ export async function calculateSolEarned({
|
|
|
134
346
|
} else {
|
|
135
347
|
solEarned = solEarned.add(solAmount);
|
|
136
348
|
}
|
|
349
|
+
} else if (record.marketIndex === 6) {
|
|
350
|
+
const jitoSolRatio = lstRatios.get(record.ts.toNumber());
|
|
351
|
+
const jitoSolRatioBN = new BN(jitoSolRatio * LAMPORTS_PER_SOL);
|
|
352
|
+
|
|
353
|
+
const solAmount = record.amount
|
|
354
|
+
.mul(jitoSolRatioBN)
|
|
355
|
+
.div(LAMPORTS_PRECISION);
|
|
356
|
+
if (isVariant(record.direction, 'deposit')) {
|
|
357
|
+
solEarned = solEarned.sub(solAmount);
|
|
358
|
+
} else {
|
|
359
|
+
solEarned = solEarned.add(solAmount);
|
|
360
|
+
}
|
|
137
361
|
}
|
|
138
362
|
}
|
|
139
363
|
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
const currentMSOLRatio = msolRatios.get(now);
|
|
144
|
-
const currentMSOLRatioBN = new BN(currentMSOLRatio * LAMPORTS_PER_SOL);
|
|
364
|
+
const currentLstTokenAmount = await user.getTokenAmount(marketIndex);
|
|
365
|
+
const currentLstRatio = lstRatios.get(now);
|
|
366
|
+
const currentLstRatioBN = new BN(currentLstRatio * LAMPORTS_PER_SOL);
|
|
145
367
|
|
|
146
368
|
solEarned = solEarned.add(
|
|
147
|
-
|
|
369
|
+
currentLstTokenAmount.mul(currentLstRatioBN).div(LAMPORTS_PRECISION)
|
|
148
370
|
);
|
|
371
|
+
|
|
372
|
+
const currentSOLTokenAmount = await user.getTokenAmount(1);
|
|
149
373
|
solEarned = solEarned.add(currentSOLTokenAmount);
|
|
150
374
|
|
|
151
375
|
return solEarned;
|
|
152
376
|
}
|
|
153
377
|
|
|
154
|
-
// calculate estimated liquidation price (in
|
|
378
|
+
// calculate estimated liquidation price (in LST/SOL) based on target amounts
|
|
155
379
|
export function calculateEstimatedSuperStakeLiquidationPrice(
|
|
156
|
-
|
|
157
|
-
|
|
380
|
+
lstDepositAmount: number,
|
|
381
|
+
lstMaintenanceAssetWeight: number,
|
|
158
382
|
solBorrowAmount: number,
|
|
159
383
|
solMaintenanceLiabilityWeight: number,
|
|
160
|
-
|
|
384
|
+
lstPriceRatio: number
|
|
161
385
|
): number {
|
|
162
386
|
const liquidationDivergence =
|
|
163
387
|
(solMaintenanceLiabilityWeight * solBorrowAmount) /
|
|
164
|
-
(
|
|
165
|
-
const liquidationPrice =
|
|
388
|
+
(lstMaintenanceAssetWeight * lstDepositAmount * lstPriceRatio);
|
|
389
|
+
const liquidationPrice = lstPriceRatio * liquidationDivergence;
|
|
166
390
|
return liquidationPrice;
|
|
167
391
|
}
|
package/src/math/trade.ts
CHANGED
|
@@ -411,16 +411,8 @@ export function calculateEstimatedPerpEntryPrice(
|
|
|
411
411
|
|
|
412
412
|
const takerIsLong = isVariant(direction, 'long');
|
|
413
413
|
const limitOrders = dlob[
|
|
414
|
-
takerIsLong ? '
|
|
415
|
-
](
|
|
416
|
-
market.marketIndex,
|
|
417
|
-
slot,
|
|
418
|
-
MarketType.PERP,
|
|
419
|
-
oraclePriceData,
|
|
420
|
-
takerIsLong
|
|
421
|
-
? calculateBidPrice(market, oraclePriceData)
|
|
422
|
-
: calculateAskPrice(market, oraclePriceData)
|
|
423
|
-
);
|
|
414
|
+
takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'
|
|
415
|
+
](market.marketIndex, slot, MarketType.PERP, oraclePriceData);
|
|
424
416
|
|
|
425
417
|
const swapDirection = getSwapDirection(assetType, direction);
|
|
426
418
|
|
|
@@ -726,7 +718,7 @@ export function calculateEstimatedSpotEntryPrice(
|
|
|
726
718
|
|
|
727
719
|
const takerIsLong = isVariant(direction, 'long');
|
|
728
720
|
const dlobLimitOrders = dlob[
|
|
729
|
-
takerIsLong ? '
|
|
721
|
+
takerIsLong ? 'getRestingLimitAsks' : 'getRestingLimitBids'
|
|
730
722
|
](market.marketIndex, slot, MarketType.SPOT, oraclePriceData);
|
|
731
723
|
const serumLimitOrders = takerIsLong
|
|
732
724
|
? serumAsks.getL2(100)
|
package/src/math/utils.ts
CHANGED
|
@@ -83,3 +83,15 @@ export function timeRemainingUntilUpdate(
|
|
|
83
83
|
|
|
84
84
|
return timeRemainingUntilUpdate;
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
export const checkSameDate = (dateString1: string, dateString2: string) => {
|
|
88
|
+
const date1 = new Date(dateString1);
|
|
89
|
+
const date2 = new Date(dateString2);
|
|
90
|
+
|
|
91
|
+
const isSameDate =
|
|
92
|
+
date1.getDate() === date2.getDate() &&
|
|
93
|
+
date1.getMonth() === date2.getMonth() &&
|
|
94
|
+
date1.getFullYear() === date2.getFullYear();
|
|
95
|
+
|
|
96
|
+
return isSameDate;
|
|
97
|
+
};
|
|
@@ -30,6 +30,7 @@ export class OrderSubscriber {
|
|
|
30
30
|
this.subscription = new WebsocketSubscription({
|
|
31
31
|
orderSubscriber: this,
|
|
32
32
|
skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
|
|
33
|
+
resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
this.eventEmitter = new EventEmitter();
|
|
@@ -86,7 +87,12 @@ export class OrderSubscriber {
|
|
|
86
87
|
programAccount.account.data[1]
|
|
87
88
|
);
|
|
88
89
|
programAccountSet.add(key);
|
|
89
|
-
|
|
90
|
+
const userAccount =
|
|
91
|
+
this.driftClient.program.account.user.coder.accounts.decode(
|
|
92
|
+
'User',
|
|
93
|
+
buffer
|
|
94
|
+
) as UserAccount;
|
|
95
|
+
this.tryUpdateUserAccount(key, userAccount, slot);
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
for (const key of this.usersAccounts.keys()) {
|
|
@@ -102,14 +108,13 @@ export class OrderSubscriber {
|
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
|
|
105
|
-
tryUpdateUserAccount(
|
|
111
|
+
tryUpdateUserAccount(
|
|
112
|
+
key: string,
|
|
113
|
+
userAccount: UserAccount,
|
|
114
|
+
slot: number
|
|
115
|
+
): void {
|
|
106
116
|
const slotAndUserAccount = this.usersAccounts.get(key);
|
|
107
117
|
if (!slotAndUserAccount || slotAndUserAccount.slot < slot) {
|
|
108
|
-
const userAccount =
|
|
109
|
-
this.driftClient.program.account.user.coder.accounts.decode(
|
|
110
|
-
'User',
|
|
111
|
-
buffer
|
|
112
|
-
) as UserAccount;
|
|
113
118
|
const newOrders = userAccount.orders.filter(
|
|
114
119
|
(order) =>
|
|
115
120
|
order.slot.toNumber() > (slotAndUserAccount?.slot ?? 0) &&
|
|
@@ -1,42 +1,57 @@
|
|
|
1
1
|
import { OrderSubscriber } from './OrderSubscriber';
|
|
2
2
|
import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
|
|
3
|
+
import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber';
|
|
4
|
+
import { UserAccount } from '../types';
|
|
5
|
+
import { Context, PublicKey } from '@solana/web3.js';
|
|
3
6
|
|
|
4
7
|
export class WebsocketSubscription {
|
|
5
8
|
private orderSubscriber: OrderSubscriber;
|
|
6
9
|
private skipInitialLoad: boolean;
|
|
10
|
+
private resubTimeoutMs?: number;
|
|
7
11
|
|
|
8
|
-
private
|
|
12
|
+
private subscriber: WebSocketProgramAccountSubscriber<UserAccount>;
|
|
9
13
|
|
|
10
14
|
constructor({
|
|
11
15
|
orderSubscriber,
|
|
12
16
|
skipInitialLoad = false,
|
|
17
|
+
resubTimeoutMs,
|
|
13
18
|
}: {
|
|
14
19
|
orderSubscriber: OrderSubscriber;
|
|
15
20
|
skipInitialLoad?: boolean;
|
|
21
|
+
resubTimeoutMs?: number;
|
|
16
22
|
}) {
|
|
17
23
|
this.orderSubscriber = orderSubscriber;
|
|
18
24
|
this.skipInitialLoad = skipInitialLoad;
|
|
25
|
+
this.resubTimeoutMs = resubTimeoutMs;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
28
|
public async subscribe(): Promise<void> {
|
|
22
|
-
if (this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
keyAccountInfo.accountInfo.data,
|
|
34
|
-
context.slot
|
|
35
|
-
);
|
|
29
|
+
if (!this.subscriber) {
|
|
30
|
+
this.subscriber = new WebSocketProgramAccountSubscriber<UserAccount>(
|
|
31
|
+
'OrderSubscriber',
|
|
32
|
+
'User',
|
|
33
|
+
this.orderSubscriber.driftClient.program,
|
|
34
|
+
this.orderSubscriber.driftClient.program.account.user.coder.accounts.decode.bind(
|
|
35
|
+
this.orderSubscriber.driftClient.program.account.user.coder.accounts
|
|
36
|
+
),
|
|
37
|
+
{
|
|
38
|
+
filters: [getUserFilter(), getNonIdleUserFilter()],
|
|
39
|
+
commitment: this.orderSubscriber.driftClient.opts.commitment,
|
|
36
40
|
},
|
|
37
|
-
this.
|
|
38
|
-
[getUserFilter(), getNonIdleUserFilter()]
|
|
41
|
+
this.resubTimeoutMs
|
|
39
42
|
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
await this.subscriber.subscribe(
|
|
46
|
+
(accountId: PublicKey, account: UserAccount, context: Context) => {
|
|
47
|
+
const userKey = accountId.toBase58();
|
|
48
|
+
this.orderSubscriber.tryUpdateUserAccount(
|
|
49
|
+
userKey,
|
|
50
|
+
account,
|
|
51
|
+
context.slot
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
);
|
|
40
55
|
|
|
41
56
|
if (!this.skipInitialLoad) {
|
|
42
57
|
await this.orderSubscriber.fetch();
|
|
@@ -44,11 +59,7 @@ export class WebsocketSubscription {
|
|
|
44
59
|
}
|
|
45
60
|
|
|
46
61
|
public async unsubscribe(): Promise<void> {
|
|
47
|
-
if (this.
|
|
48
|
-
|
|
49
|
-
this.websocketId
|
|
50
|
-
);
|
|
51
|
-
this.websocketId = undefined;
|
|
52
|
-
}
|
|
62
|
+
if (!this.subscriber) return;
|
|
63
|
+
this.subscriber.unsubscribe();
|
|
53
64
|
}
|
|
54
65
|
}
|