UNPKG

@drift-labs/common

Version:

Common functions for Drift

224 lines 11.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.fetchLstMetrics = exports.getSuperstakeEstimatedLiquidationRatio = exports.getSuperstakeEstimatedApr = exports.getMaxLeverageForLst = void 0; const sdk_1 = require("@drift-labs/sdk"); const utils_1 = require("../utils"); const web3_js_1 = require("@solana/web3.js"); const constants_1 = require("../constants"); // Default value for estimated APR const DEFAULT_VALUE = { solBorrowRate: 0, lstDepositRate: 0, lstApy: 0, leveragedBorrowRate: 0, leveragedDepositRate: 0, leveragedLstApr: 0, leveragedEmissionsApr: 0, lstNetProjectedApr: 0, totalNetProjectedApr: 0, solBorrowAmount: 0, projectedLiqRatio: 0, unleveragedApr: 0, loaded: false, }; /* * Returns amount of SOL that will be borrowed to achieve the desired leverage on the active LST */ const getSuperstakeEstimatedApr = ({ lstSpotMarket, lstMetrics, initialLstDeposit, lstAmount, solAmount, driftClient, driftClientIsReady, driftEnv, includeBorrowRateDelta = false, }) => { const solSpotMarket = ((driftEnv && sdk_1.SpotMarkets[driftEnv]) || sdk_1.SpotMarkets['mainnet-beta']).find((market) => market.symbol === 'SOL'); if (!lstMetrics.loaded || !driftClient || !driftClientIsReady || !lstSpotMarket || !solSpotMarket) return DEFAULT_VALUE; let lstSpotMarketAccount, solSpotMarketAccount; try { lstSpotMarketAccount = driftClient.getSpotMarketAccount(lstSpotMarket.marketIndex); solSpotMarketAccount = driftClient.getSpotMarketAccount(solSpotMarket.marketIndex); } catch (err) { console.log(err); } if (!lstSpotMarketAccount || !solSpotMarketAccount) { return DEFAULT_VALUE; } const lstDepositRate = sdk_1.BigNum.from((0, sdk_1.calculateDepositRate)(lstSpotMarketAccount), sdk_1.PERCENTAGE_PRECISION_EXP); const solBorrowRate = sdk_1.BigNum.from((0, sdk_1.calculateInterestRate)(solSpotMarketAccount, includeBorrowRateDelta ? new sdk_1.BN(Math.min(solAmount * web3_js_1.LAMPORTS_PER_SOL, Number.MAX_SAFE_INTEGER)).neg() : sdk_1.ZERO), sdk_1.PERCENTAGE_PRECISION_EXP); const superStakeLstDeposit = lstAmount; const solBorrowAmount = solAmount; if (isNaN(solBorrowAmount)) return DEFAULT_VALUE; const lstAprFromApy = (0, utils_1.aprFromApy)(lstMetrics.lstPriceApy30d, Math.floor(365 / 2)) / 100; const solInFromStaking = superStakeLstDeposit * lstAprFromApy * lstMetrics.priceInSol; const solInFromDeposit = superStakeLstDeposit * lstDepositRate.toNum() * lstMetrics.priceInSol; const leveragedLstApr = solInFromStaking / (initialLstDeposit * lstMetrics.priceInSol); const solOut = solBorrowAmount * solBorrowRate.toNum(); const solRewardsPerYear = solInFromStaking + solInFromDeposit - solOut; const lstNetProjectedApr = solRewardsPerYear / (initialLstDeposit * lstMetrics.priceInSol); const leveragedBorrowRate = solOut / (initialLstDeposit * lstMetrics.priceInSol); const leveragedDepositRate = solInFromDeposit / (initialLstDeposit * lstMetrics.priceInSol); // Add emissions APR on top because they are airdropped separately // Not subject to the borrow rate const emissionsAprFromApy = lstMetrics.emissionsApy ? (0, utils_1.aprFromApy)(lstMetrics.emissionsApy, Math.floor(365 / 2)) / 100 : 0; // 365 / 2 ~= number of epochs per year const solInFromEmissions = superStakeLstDeposit * emissionsAprFromApy; const leveragedEmissionsApr = solInFromEmissions / (initialLstDeposit * lstMetrics.priceInSol); const totalNetProjectedApr = lstNetProjectedApr + (isNaN(leveragedEmissionsApr) ? 0 : leveragedEmissionsApr); return { solBorrowRate: solBorrowRate.toNum() * 100, lstDepositRate: lstDepositRate.toNum() * 100, lstApy: lstMetrics.lstPriceApy30d, // independant lst yield apr incl. leverage leveragedLstApr: leveragedLstApr * 100, // independent emissions apr incl. leverage leveragedEmissionsApr: leveragedEmissionsApr * 100, // Sol borro ratew denominated in % of user's collateral value in sol // Helps to understand the apr breakdown where other values incl. leverage leveragedBorrowRate: leveragedBorrowRate * 100, // Deposit rate of total LST value in SOL denominated in % of user's collateral value in sol leveragedDepositRate: leveragedDepositRate * 100, // net apr of borrow rate + lst deposit rate + lst yield incl. leverage lstNetProjectedApr: lstNetProjectedApr * 100, // total net apr of lstNetProjectedApr and emissions apr together totalNetProjectedApr: totalNetProjectedApr * 100, solBorrowAmount, unleveragedApr: lstDepositRate.toNum() * 100 + lstAprFromApy * 100 + emissionsAprFromApy * 100, loaded: true, }; }; exports.getSuperstakeEstimatedApr = getSuperstakeEstimatedApr; const SOL_PRECISION_EXP = sdk_1.NINE; /* * Returns current liquidation ratio at given LST / sol balances */ const getSuperstakeEstimatedLiquidationRatio = ({ driftEnv, driftClient, driftClientIsReady, lstAmount, lstMetrics, lstSpotMarket, solAmount, }) => { const solSpotMarket = ((driftEnv && sdk_1.SpotMarkets[driftEnv]) || sdk_1.SpotMarkets['mainnet-beta']).find((market) => market.symbol === 'SOL'); if (!lstMetrics.loaded || !driftClient || !driftClientIsReady || !lstSpotMarket || !solSpotMarket) return 0; let lstSpotMarketAccount, solSpotMarketAccount; try { lstSpotMarketAccount = driftClient.getSpotMarketAccount(lstSpotMarket.marketIndex); solSpotMarketAccount = driftClient.getSpotMarketAccount(solSpotMarket.marketIndex); } catch (err) { console.log(err); } if (!lstSpotMarketAccount || !solSpotMarketAccount) { return 0; } if (isNaN(solAmount) || isNaN(lstAmount) || `${lstAmount}${solAmount}`.includes('e-')) return 0; const lstAmountBigNum = sdk_1.BigNum.fromPrint(`${lstAmount}`, lstSpotMarket.precisionExp); const solAmountBigNum = sdk_1.BigNum.fromPrint(`${solAmount}`, SOL_PRECISION_EXP); const maintenanceWeight = (0, sdk_1.calculateSizeDiscountAssetWeight)(lstAmountBigNum.val, new sdk_1.BN(lstSpotMarketAccount.imfFactor), new sdk_1.BN(lstSpotMarketAccount.maintenanceAssetWeight)); const liabilityWeight = (0, sdk_1.calculateSizePremiumLiabilityWeight)(solAmountBigNum.val, new sdk_1.BN(solSpotMarketAccount.imfFactor), new sdk_1.BN(solSpotMarketAccount.maintenanceLiabilityWeight), sdk_1.SPOT_MARKET_WEIGHT_PRECISION); const projectedLiqRatio = (0, sdk_1.calculateEstimatedSuperStakeLiquidationPrice)(lstAmount, (0, sdk_1.convertToNumber)(maintenanceWeight, sdk_1.SPOT_MARKET_WEIGHT_PRECISION), solAmount, (0, sdk_1.convertToNumber)(liabilityWeight, sdk_1.SPOT_MARKET_WEIGHT_PRECISION), lstMetrics.priceInSol); return projectedLiqRatio; }; exports.getSuperstakeEstimatedLiquidationRatio = getSuperstakeEstimatedLiquidationRatio; /** * Fetches LST Metrics with a consistent return type for either msol, bsol, or jitosol * * @param lstSpotMarket * @returns */ const fetchLstMetrics = async (lstSpotMarket) => { var _a, _b, _c, _d, _e, _f, _g, _h; if (lstSpotMarket.symbol.toLowerCase() === constants_1.M_SOL.symbol.toLowerCase()) { const mSolMetrics = await (0, sdk_1.fetchMSolMetrics)(); return { lstPriceApy30d: (_a = mSolMetrics.msol_price_apy_30d) !== null && _a !== void 0 ? _a : 0, priceInSol: (_b = mSolMetrics.m_sol_price) !== null && _b !== void 0 ? _b : 0, loaded: true, }; } else if (lstSpotMarket.symbol.toLowerCase() === constants_1.JITO_SOL.symbol.toLowerCase()) { const data = await (0, sdk_1.fetchJitoSolMetrics)(); const past30DaysApyAvg = (data.apy.slice(-30).reduce((a, b) => a + b.data, 0) / 30) * 100; const priceInSol = data.tvl.slice(-1)[0].data / constants_1.JITO_SOL.spotMarket.precision.toNumber() / data.supply.slice(-1)[0].data; return { lstPriceApy30d: past30DaysApyAvg !== null && past30DaysApyAvg !== void 0 ? past30DaysApyAvg : 0, priceInSol: priceInSol !== null && priceInSol !== void 0 ? priceInSol : 0, loaded: true, }; } else if (lstSpotMarket.symbol.toLowerCase() === constants_1.B_SOL.symbol.toLowerCase()) { let baseApy; let blzeApy; let lendingMultiplier; let priceInSol; let driftEmissions; const statsResponse = await (0, sdk_1.fetchBSolMetrics)(); if (statsResponse.status === 200) { const data = (await statsResponse.json()); priceInSol = (_d = (_c = data === null || data === void 0 ? void 0 : data.stats) === null || _c === void 0 ? void 0 : _c.conversion) === null || _d === void 0 ? void 0 : _d.bsol_to_sol; baseApy = (_e = data === null || data === void 0 ? void 0 : data.stats) === null || _e === void 0 ? void 0 : _e.apy.base; lendingMultiplier = (_f = data === null || data === void 0 ? void 0 : data.stats) === null || _f === void 0 ? void 0 : _f.apy.lending; blzeApy = ((_g = data === null || data === void 0 ? void 0 : data.stats) === null || _g === void 0 ? void 0 : _g.apy.blze) * lendingMultiplier; } const driftEmissionsResponse = await (0, sdk_1.fetchBSolDriftEmissions)(); if (driftEmissionsResponse.status === 200) { const data = await driftEmissionsResponse.json(); driftEmissions = (_h = data === null || data === void 0 ? void 0 : data.emissions) === null || _h === void 0 ? void 0 : _h.lend; } return { loaded: true, lstPriceApy30d: baseApy !== null && baseApy !== void 0 ? baseApy : 0, priceInSol: priceInSol !== null && priceInSol !== void 0 ? priceInSol : 0, emissionsApy: blzeApy, driftEmissions, }; } }; exports.fetchLstMetrics = fetchLstMetrics; /** * Returns estimated max spot leverage for a particular lst * * @param lst * @param driftClient * @param driftClientIsReady * @returns */ const getMaxLeverageForLst = ({ lstSpotMarket, solSpotMarket, driftClient, driftClientIsReady, }) => { if (!driftClient || !driftClientIsReady) { return { maxLeverage: 1, loaded: false, }; } const lstSpotMarketAccount = driftClient.getSpotMarketAccount(lstSpotMarket.marketIndex); const solSpotMarketAccount = driftClient.getSpotMarketAccount(solSpotMarket.marketIndex); const spotWeightPrecisionExp = sdk_1.SPOT_MARKET_WEIGHT_PRECISION.toString().length - 1; const lstOraclePriceData = driftClient.getOracleDataForSpotMarket(lstSpotMarket.marketIndex); const lstOraclePriceBigNum = sdk_1.BigNum.from(lstOraclePriceData.price, sdk_1.QUOTE_PRECISION_EXP); const lstInitialAssetWeight = sdk_1.BigNum.from((0, sdk_1.calculateScaledInitialAssetWeight)(lstSpotMarketAccount, lstOraclePriceBigNum.val), spotWeightPrecisionExp).toNum(); const solInitialLiabilityWeight = sdk_1.BigNum.from(solSpotMarketAccount.initialLiabilityWeight, spotWeightPrecisionExp).toNum(); // make sure to under estimate const unroundedMaxLeverage = lstInitialAssetWeight / (solInitialLiabilityWeight - lstInitialAssetWeight) + 1; const maxLeverage = Math.floor(10 * Math.min(3, Math.max(1, unroundedMaxLeverage))) / 10; return { maxLeverage, loaded: true, }; }; exports.getMaxLeverageForLst = getMaxLeverageForLst; //# sourceMappingURL=superstake.js.map