@drift-labs/common
Version:
Common functions for Drift
224 lines • 11.8 kB
JavaScript
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
;