@drift-labs/sdk 2.40.0-beta.8 → 2.40.0-beta.9

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.40.0-beta.8
1
+ 2.40.0-beta.9
@@ -100,7 +100,7 @@ exports.MainnetSpotMarkets = [
100
100
  serumMarket: new web3_js_1.PublicKey('B2na8Awyd7cpC59iEU43FagJAPLigr3AP3s38KM982bu'),
101
101
  },
102
102
  {
103
- symbol: 'jitoSOL',
103
+ symbol: 'JitoSOL',
104
104
  marketIndex: 6,
105
105
  oracle: new web3_js_1.PublicKey('7yyaeuJ1GGtVBLT2z2xub5ZWYKaNhF28mj1RdV4VDFVk'),
106
106
  oracleSource: __1.OracleSource.PYTH,
@@ -4,11 +4,12 @@ import { DriftClient } from '../driftClient';
4
4
  import { BN } from '@coral-xyz/anchor';
5
5
  import { User } from '../user';
6
6
  import { DepositRecord } from '../types';
7
- export declare function findBestSuperStakeIxs({ amount, jupiterClient, driftClient, userAccountPublicKey, marinadePrice, forceMarinade, onlyDirectRoutes, }: {
7
+ export declare function findBestSuperStakeIxs({ marketIndex, amount, jupiterClient, driftClient, userAccountPublicKey, price, forceMarinade, onlyDirectRoutes, }: {
8
+ marketIndex: number;
8
9
  amount: BN;
9
10
  jupiterClient: JupiterClient;
10
11
  driftClient: DriftClient;
11
- marinadePrice?: number;
12
+ price?: number;
12
13
  userAccountPublicKey?: PublicKey;
13
14
  forceMarinade?: boolean;
14
15
  onlyDirectRoutes?: boolean;
@@ -18,8 +19,54 @@ export declare function findBestSuperStakeIxs({ amount, jupiterClient, driftClie
18
19
  method: 'jupiter' | 'marinade';
19
20
  price: number;
20
21
  }>;
21
- export declare function calculateSolEarned({ user, depositRecords, }: {
22
+ export declare function findBestMSolSuperStakeIxs({ amount, jupiterClient, driftClient, userAccountPublicKey, price, forceMarinade, onlyDirectRoutes, }: {
23
+ amount: BN;
24
+ jupiterClient: JupiterClient;
25
+ driftClient: DriftClient;
26
+ price?: number;
27
+ userAccountPublicKey?: PublicKey;
28
+ forceMarinade?: boolean;
29
+ onlyDirectRoutes?: boolean;
30
+ }): Promise<{
31
+ ixs: TransactionInstruction[];
32
+ lookupTables: AddressLookupTableAccount[];
33
+ method: 'jupiter' | 'marinade';
34
+ price: number;
35
+ }>;
36
+ export declare function findBestJitoSolSuperStakeIxs({ amount, jupiterClient, driftClient, userAccountPublicKey, onlyDirectRoutes, }: {
37
+ amount: BN;
38
+ jupiterClient: JupiterClient;
39
+ driftClient: DriftClient;
40
+ userAccountPublicKey?: PublicKey;
41
+ onlyDirectRoutes?: boolean;
42
+ }): Promise<{
43
+ ixs: TransactionInstruction[];
44
+ lookupTables: AddressLookupTableAccount[];
45
+ method: 'jupiter' | 'marinade';
46
+ price: number;
47
+ }>;
48
+ export type JITO_SOL_METRICS_ENDPOINT_RESPONSE = {
49
+ data: {
50
+ getStakePoolStats: {
51
+ tvl: {
52
+ data: number;
53
+ date: string;
54
+ }[];
55
+ supply: {
56
+ data: number;
57
+ date: string;
58
+ }[];
59
+ apy: {
60
+ data: number;
61
+ date: string;
62
+ }[];
63
+ };
64
+ };
65
+ };
66
+ export declare function fetchJitoSolMetrics(): Promise<JITO_SOL_METRICS_ENDPOINT_RESPONSE>;
67
+ export declare function calculateSolEarned({ marketIndex, user, depositRecords, }: {
68
+ marketIndex: number;
22
69
  user: User;
23
70
  depositRecords: DepositRecord[];
24
71
  }): Promise<BN>;
25
- export declare function calculateEstimatedSuperStakeLiquidationPrice(msolDepositAmount: number, msolMaintenanceAssetWeight: number, solBorrowAmount: number, solMaintenanceLiabilityWeight: number, msolPriceRatio: number): number;
72
+ export declare function calculateEstimatedSuperStakeLiquidationPrice(lstDepositAmount: number, lstMaintenanceAssetWeight: number, solBorrowAmount: number, solMaintenanceLiabilityWeight: number, lstPriceRatio: number): number;
@@ -3,17 +3,44 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.calculateEstimatedSuperStakeLiquidationPrice = exports.calculateSolEarned = exports.findBestSuperStakeIxs = void 0;
6
+ exports.calculateEstimatedSuperStakeLiquidationPrice = exports.calculateSolEarned = exports.fetchJitoSolMetrics = exports.findBestJitoSolSuperStakeIxs = exports.findBestMSolSuperStakeIxs = exports.findBestSuperStakeIxs = void 0;
7
7
  const web3_js_1 = require("@solana/web3.js");
8
8
  const marinade_1 = require("../marinade");
9
9
  const anchor_1 = require("@coral-xyz/anchor");
10
10
  const types_1 = require("../types");
11
11
  const numericConstants_1 = require("../constants/numericConstants");
12
12
  const node_fetch_1 = __importDefault(require("node-fetch"));
13
- async function findBestSuperStakeIxs({ amount, jupiterClient, driftClient, userAccountPublicKey, marinadePrice, forceMarinade, onlyDirectRoutes, }) {
14
- if (!marinadePrice) {
13
+ const utils_1 = require("./utils");
14
+ async function findBestSuperStakeIxs({ marketIndex, amount, jupiterClient, driftClient, userAccountPublicKey, price, forceMarinade, onlyDirectRoutes, }) {
15
+ if (marketIndex === 2) {
16
+ return findBestMSolSuperStakeIxs({
17
+ amount,
18
+ jupiterClient,
19
+ driftClient,
20
+ userAccountPublicKey,
21
+ price,
22
+ forceMarinade,
23
+ onlyDirectRoutes,
24
+ });
25
+ }
26
+ else if (marketIndex === 6) {
27
+ return findBestJitoSolSuperStakeIxs({
28
+ amount,
29
+ jupiterClient,
30
+ driftClient,
31
+ userAccountPublicKey,
32
+ onlyDirectRoutes,
33
+ });
34
+ }
35
+ else {
36
+ throw new Error(`Unsupported superstake market index: ${marketIndex}`);
37
+ }
38
+ }
39
+ exports.findBestSuperStakeIxs = findBestSuperStakeIxs;
40
+ async function findBestMSolSuperStakeIxs({ amount, jupiterClient, driftClient, userAccountPublicKey, price, forceMarinade, onlyDirectRoutes, }) {
41
+ if (!price) {
15
42
  const marinadeProgram = (0, marinade_1.getMarinadeFinanceProgram)(driftClient.provider);
16
- marinadePrice = await (0, marinade_1.getMarinadeMSolPrice)(marinadeProgram);
43
+ price = await (0, marinade_1.getMarinadeMSolPrice)(marinadeProgram);
17
44
  }
18
45
  const solMint = driftClient.getSpotMarketAccount(1).mint;
19
46
  const mSOLMint = driftClient.getSpotMarketAccount(2).mint;
@@ -32,7 +59,7 @@ async function findBestSuperStakeIxs({ amount, jupiterClient, driftClient, userA
32
59
  catch (e) {
33
60
  console.error('Error getting jupiter price', e);
34
61
  }
35
- if (!jupiterPrice || marinadePrice <= jupiterPrice || forceMarinade) {
62
+ if (!jupiterPrice || price <= jupiterPrice || forceMarinade) {
36
63
  const ixs = await driftClient.getStakeForMSOLIx({
37
64
  amount,
38
65
  userAccountPublicKey,
@@ -41,7 +68,7 @@ async function findBestSuperStakeIxs({ amount, jupiterClient, driftClient, userA
41
68
  method: 'marinade',
42
69
  ixs,
43
70
  lookupTables: [],
44
- price: marinadePrice,
71
+ price: price,
45
72
  };
46
73
  }
47
74
  else {
@@ -61,25 +88,137 @@ async function findBestSuperStakeIxs({ amount, jupiterClient, driftClient, userA
61
88
  };
62
89
  }
63
90
  }
64
- exports.findBestSuperStakeIxs = findBestSuperStakeIxs;
65
- async function calculateSolEarned({ user, depositRecords, }) {
91
+ exports.findBestMSolSuperStakeIxs = findBestMSolSuperStakeIxs;
92
+ async function findBestJitoSolSuperStakeIxs({ amount, jupiterClient, driftClient, userAccountPublicKey, onlyDirectRoutes, }) {
93
+ const solMint = driftClient.getSpotMarketAccount(1).mint;
94
+ const JitoSolMint = driftClient.getSpotMarketAccount(6).mint;
95
+ let jupiterPrice;
96
+ let bestRoute;
97
+ try {
98
+ const jupiterRoutes = await jupiterClient.getRoutes({
99
+ inputMint: solMint,
100
+ outputMint: JitoSolMint,
101
+ amount,
102
+ onlyDirectRoutes,
103
+ });
104
+ bestRoute = jupiterRoutes[0];
105
+ jupiterPrice = bestRoute.inAmount / bestRoute.outAmount;
106
+ }
107
+ catch (e) {
108
+ console.error('Error getting jupiter price', e);
109
+ throw e;
110
+ }
111
+ const { ixs, lookupTables } = await driftClient.getJupiterSwapIx({
112
+ inMarketIndex: 1,
113
+ outMarketIndex: 6,
114
+ route: bestRoute,
115
+ jupiterClient,
116
+ amount,
117
+ userAccountPublicKey,
118
+ });
119
+ return {
120
+ method: 'jupiter',
121
+ ixs,
122
+ lookupTables,
123
+ price: jupiterPrice,
124
+ };
125
+ }
126
+ exports.findBestJitoSolSuperStakeIxs = findBestJitoSolSuperStakeIxs;
127
+ const JITO_SOL_START_DATE = '2022-10-31T00:00:00Z';
128
+ async function fetchJitoSolMetrics() {
129
+ const res = await (0, node_fetch_1.default)('https://kobe.mainnet.jito.network/', {
130
+ body: JSON.stringify({
131
+ operationName: 'QueryRoot',
132
+ variables: {
133
+ request: {
134
+ bucketType: 'DAILY',
135
+ rangeFilter: {
136
+ start: JITO_SOL_START_DATE,
137
+ end: new Date().toISOString(),
138
+ },
139
+ sortBy: {
140
+ order: 'ASC',
141
+ field: 'BLOCK_TIME',
142
+ },
143
+ },
144
+ },
145
+ query: `
146
+ query QueryRoot($request: GetStakePoolStatsRequest!) {
147
+ getStakePoolStats(req: $request) {
148
+ tvl {
149
+ data
150
+ date
151
+ }
152
+ apy {
153
+ data
154
+ date
155
+ }
156
+ supply {
157
+ data
158
+ date
159
+ }
160
+ }
161
+ }
162
+ `,
163
+ }),
164
+ method: 'POST',
165
+ });
166
+ const data = await res.json();
167
+ return data;
168
+ }
169
+ exports.fetchJitoSolMetrics = fetchJitoSolMetrics;
170
+ const getJitoSolHistoricalPriceMap = async (timestamps) => {
171
+ try {
172
+ const data = await fetchJitoSolMetrics();
173
+ const jitoSolHistoricalPriceMap = new Map();
174
+ const jitoSolHistoricalPriceInSol = [];
175
+ for (let i = 0; i < data.data.getStakePoolStats.supply.length; i++) {
176
+ const priceInSol = data.data.getStakePoolStats.tvl[i].data /
177
+ new anchor_1.BN(10).pow(numericConstants_1.NINE) /
178
+ data.data.getStakePoolStats.supply[i].data;
179
+ jitoSolHistoricalPriceInSol.push({
180
+ price: priceInSol,
181
+ ts: data.data.getStakePoolStats.tvl[i].date,
182
+ });
183
+ }
184
+ for (const timestamp of timestamps) {
185
+ const date = new Date(timestamp * 1000);
186
+ const dateString = date.toISOString();
187
+ const price = jitoSolHistoricalPriceInSol.find((p) => (0, utils_1.checkSameDate)(p.ts, dateString));
188
+ if (price) {
189
+ jitoSolHistoricalPriceMap.set(timestamp, price.price);
190
+ }
191
+ }
192
+ return jitoSolHistoricalPriceMap;
193
+ }
194
+ catch (err) {
195
+ console.error(err);
196
+ return undefined;
197
+ }
198
+ };
199
+ async function calculateSolEarned({ marketIndex, user, depositRecords, }) {
66
200
  const now = Date.now() / 1000;
67
201
  const timestamps = [
68
202
  now,
69
203
  ...depositRecords.map((r) => r.ts.toNumber()),
70
204
  ];
71
- const msolRatios = new Map();
72
- const getPrice = async (timestamp) => {
205
+ let lstRatios = new Map();
206
+ const getMsolPrice = async (timestamp) => {
73
207
  const date = new Date(timestamp * 1000); // Convert Unix timestamp to milliseconds
74
208
  const swaggerApiDateTime = date.toISOString(); // Format date as swagger API date-time
75
209
  const url = `https://api.marinade.finance/msol/price_sol?time=${swaggerApiDateTime}`;
76
210
  const response = await (0, node_fetch_1.default)(url);
77
211
  if (response.status === 200) {
78
212
  const data = await response.json();
79
- msolRatios.set(timestamp, data);
213
+ lstRatios.set(timestamp, data);
80
214
  }
81
215
  };
82
- await Promise.all(timestamps.map(getPrice));
216
+ if (marketIndex === 2) {
217
+ await Promise.all(timestamps.map(getMsolPrice));
218
+ }
219
+ else if (marketIndex === 6) {
220
+ lstRatios = await getJitoSolHistoricalPriceMap(timestamps);
221
+ }
83
222
  let solEarned = numericConstants_1.ZERO;
84
223
  for (const record of depositRecords) {
85
224
  if (record.marketIndex === 1) {
@@ -91,7 +230,7 @@ async function calculateSolEarned({ user, depositRecords, }) {
91
230
  }
92
231
  }
93
232
  else if (record.marketIndex === 2) {
94
- const msolRatio = msolRatios.get(record.ts.toNumber());
233
+ const msolRatio = lstRatios.get(record.ts.toNumber());
95
234
  const msolRatioBN = new anchor_1.BN(msolRatio * web3_js_1.LAMPORTS_PER_SOL);
96
235
  const solAmount = record.amount.mul(msolRatioBN).div(numericConstants_1.LAMPORTS_PRECISION);
97
236
  if ((0, types_1.isVariant)(record.direction, 'deposit')) {
@@ -101,21 +240,34 @@ async function calculateSolEarned({ user, depositRecords, }) {
101
240
  solEarned = solEarned.add(solAmount);
102
241
  }
103
242
  }
243
+ else if (record.marketIndex === 6) {
244
+ const jitoSolRatio = lstRatios.get(record.ts.toNumber());
245
+ const jitoSolRatioBN = new anchor_1.BN(jitoSolRatio * web3_js_1.LAMPORTS_PER_SOL);
246
+ const solAmount = record.amount
247
+ .mul(jitoSolRatioBN)
248
+ .div(numericConstants_1.LAMPORTS_PRECISION);
249
+ if ((0, types_1.isVariant)(record.direction, 'deposit')) {
250
+ solEarned = solEarned.sub(solAmount);
251
+ }
252
+ else {
253
+ solEarned = solEarned.add(solAmount);
254
+ }
255
+ }
104
256
  }
105
- const currentMSOLTokenAmount = await user.getTokenAmount(2);
257
+ const currentLstTokenAmount = await user.getTokenAmount(marketIndex);
258
+ const currentLstRatio = lstRatios.get(now);
259
+ const currentLstRatioBN = new anchor_1.BN(currentLstRatio * web3_js_1.LAMPORTS_PER_SOL);
260
+ solEarned = solEarned.add(currentLstTokenAmount.mul(currentLstRatioBN).div(numericConstants_1.LAMPORTS_PRECISION));
106
261
  const currentSOLTokenAmount = await user.getTokenAmount(1);
107
- const currentMSOLRatio = msolRatios.get(now);
108
- const currentMSOLRatioBN = new anchor_1.BN(currentMSOLRatio * web3_js_1.LAMPORTS_PER_SOL);
109
- solEarned = solEarned.add(currentMSOLTokenAmount.mul(currentMSOLRatioBN).div(numericConstants_1.LAMPORTS_PRECISION));
110
262
  solEarned = solEarned.add(currentSOLTokenAmount);
111
263
  return solEarned;
112
264
  }
113
265
  exports.calculateSolEarned = calculateSolEarned;
114
- // calculate estimated liquidation price (in mSOL/SOL) based on target amounts
115
- function calculateEstimatedSuperStakeLiquidationPrice(msolDepositAmount, msolMaintenanceAssetWeight, solBorrowAmount, solMaintenanceLiabilityWeight, msolPriceRatio) {
266
+ // calculate estimated liquidation price (in LST/SOL) based on target amounts
267
+ function calculateEstimatedSuperStakeLiquidationPrice(lstDepositAmount, lstMaintenanceAssetWeight, solBorrowAmount, solMaintenanceLiabilityWeight, lstPriceRatio) {
116
268
  const liquidationDivergence = (solMaintenanceLiabilityWeight * solBorrowAmount) /
117
- (msolMaintenanceAssetWeight * msolDepositAmount * msolPriceRatio);
118
- const liquidationPrice = msolPriceRatio * liquidationDivergence;
269
+ (lstMaintenanceAssetWeight * lstDepositAmount * lstPriceRatio);
270
+ const liquidationPrice = lstPriceRatio * liquidationDivergence;
119
271
  return liquidationPrice;
120
272
  }
121
273
  exports.calculateEstimatedSuperStakeLiquidationPrice = calculateEstimatedSuperStakeLiquidationPrice;
@@ -12,3 +12,4 @@ export declare const sigNum: (x: BN) => BN;
12
12
  * @returns: timeRemainingUntilUpdate (in seconds)
13
13
  */
14
14
  export declare function timeRemainingUntilUpdate(now: BN, lastUpdateTs: BN, updatePeriod: BN): BN;
15
+ export declare const checkSameDate: (dateString1: string, dateString2: string) => boolean;
package/lib/math/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.timeRemainingUntilUpdate = exports.sigNum = exports.divCeil = exports.squareRootBN = exports.clampBN = void 0;
3
+ exports.checkSameDate = exports.timeRemainingUntilUpdate = exports.sigNum = exports.divCeil = exports.squareRootBN = exports.clampBN = void 0;
4
4
  const __1 = require("../");
5
5
  function clampBN(x, min, max) {
6
6
  return __1.BN.max(min, __1.BN.min(x, max));
@@ -76,3 +76,12 @@ function timeRemainingUntilUpdate(now, lastUpdateTs, updatePeriod) {
76
76
  return timeRemainingUntilUpdate;
77
77
  }
78
78
  exports.timeRemainingUntilUpdate = timeRemainingUntilUpdate;
79
+ const checkSameDate = (dateString1, dateString2) => {
80
+ const date1 = new Date(dateString1);
81
+ const date2 = new Date(dateString2);
82
+ const isSameDate = date1.getDate() === date2.getDate() &&
83
+ date1.getMonth() === date2.getMonth() &&
84
+ date1.getFullYear() === date2.getFullYear();
85
+ return isSameDate;
86
+ };
87
+ exports.checkSameDate = checkSameDate;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.40.0-beta.8",
3
+ "version": "2.40.0-beta.9",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -128,7 +128,7 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
128
128
  serumMarket: new PublicKey('B2na8Awyd7cpC59iEU43FagJAPLigr3AP3s38KM982bu'),
129
129
  },
130
130
  {
131
- symbol: 'jitoSOL',
131
+ symbol: 'JitoSOL',
132
132
  marketIndex: 6,
133
133
  oracle: new PublicKey('7yyaeuJ1GGtVBLT2z2xub5ZWYKaNhF28mj1RdV4VDFVk'),
134
134
  oracleSource: OracleSource.PYTH,
@@ -10,22 +10,25 @@ import { getMarinadeFinanceProgram, getMarinadeMSolPrice } from '../marinade';
10
10
  import { BN } from '@coral-xyz/anchor';
11
11
  import { User } from '../user';
12
12
  import { DepositRecord, isVariant } from '../types';
13
- import { LAMPORTS_PRECISION, ZERO } from '../constants/numericConstants';
13
+ import { LAMPORTS_PRECISION, ZERO, NINE } 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
- marinadePrice,
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
- marinadePrice?: number;
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 (!marinadePrice) {
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
- marinadePrice = await getMarinadeMSolPrice(marinadeProgram);
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 || marinadePrice <= jupiterPrice || forceMarinade) {
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: marinadePrice,
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
+ new BN(10).pow(NINE) /
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
- const msolRatios = new Map<number, number>();
312
+ let lstRatios = new Map<number, number>();
105
313
 
106
- const getPrice = async (timestamp) => {
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
- msolRatios.set(timestamp, data);
321
+ lstRatios.set(timestamp, data);
114
322
  }
115
323
  };
116
324
 
117
- await Promise.all(timestamps.map(getPrice));
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 = msolRatios.get(record.ts.toNumber());
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 currentMSOLTokenAmount = await user.getTokenAmount(2);
141
- const currentSOLTokenAmount = await user.getTokenAmount(1);
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
- currentMSOLTokenAmount.mul(currentMSOLRatioBN).div(LAMPORTS_PRECISION)
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 mSOL/SOL) based on target amounts
378
+ // calculate estimated liquidation price (in LST/SOL) based on target amounts
155
379
  export function calculateEstimatedSuperStakeLiquidationPrice(
156
- msolDepositAmount: number,
157
- msolMaintenanceAssetWeight: number,
380
+ lstDepositAmount: number,
381
+ lstMaintenanceAssetWeight: number,
158
382
  solBorrowAmount: number,
159
383
  solMaintenanceLiabilityWeight: number,
160
- msolPriceRatio: number
384
+ lstPriceRatio: number
161
385
  ): number {
162
386
  const liquidationDivergence =
163
387
  (solMaintenanceLiabilityWeight * solBorrowAmount) /
164
- (msolMaintenanceAssetWeight * msolDepositAmount * msolPriceRatio);
165
- const liquidationPrice = msolPriceRatio * liquidationDivergence;
388
+ (lstMaintenanceAssetWeight * lstDepositAmount * lstPriceRatio);
389
+ const liquidationPrice = lstPriceRatio * liquidationDivergence;
166
390
  return liquidationPrice;
167
391
  }
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
+ };