UNPKG

@hubbleprotocol/hubble-sdk

Version:
515 lines 20 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.convertTokenLamportsToDecimal = exports.addCollateralAmounts = exports.calculateStabilityProvided = exports.decimalToNumWithdrawalCap = exports.decimalToNumCollateralWithdrawalCap = exports.decimalToNumSupportedCollateral = exports.replaceBigNumberWithDecimal = exports.lamportsTokenToDecimal = exports.lamportsToDecimal = void 0; exports.calculateTotalDebt = calculateTotalDebt; exports.calculateTotalCollateral = calculateTotalCollateral; exports.sub = sub; exports.isZero = isZero; exports.zeroCollateral = zeroCollateral; exports.zeroExtraCollateral = zeroExtraCollateral; exports.mulFrac = mulFrac; exports.calculatePendingGains = calculatePendingGains; const constants_1 = require("../constants"); const anchor_1 = require("@coral-xyz/anchor"); const decimal_js_1 = __importDefault(require("decimal.js")); /** * Divide all collateral amounts to convert from lamports to decimals */ const lamportsToDecimal = (collateral) => { return { sol: collateral.sol.div(constants_1.LAMPORTS_PER_SOL), btc: collateral.btc.div(constants_1.DECIMALS_BTC), msol: collateral.msol.div(constants_1.LAMPORTS_PER_MSOL), ray: collateral.ray.div(constants_1.DECIMALS_RAY), ftt: collateral.ftt.div(constants_1.DECIMALS_FTT), eth: collateral.eth.div(constants_1.DECIMALS_ETH), srm: collateral.srm.div(constants_1.DECIMALS_SRM), extraCollaterals: collateral.extraCollaterals .filter((x) => constants_1.ExtraCollateralMap.some((y) => x.tokenId.eq(y.id))) .map((coll) => { return { amount: (0, exports.convertTokenLamportsToDecimal)(coll.amount, (0, constants_1.getExtraCollateralTokenById)(coll.tokenId).name), tokenId: coll.tokenId, }; }), }; }; exports.lamportsToDecimal = lamportsToDecimal; /** * Divide all token map big amounts to convert from lamports to decimals */ const lamportsTokenToDecimal = (collateral) => { return { sol: collateral.sol.div(constants_1.LAMPORTS_PER_SOL), btc: collateral.btc.div(constants_1.DECIMALS_BTC), msol: collateral.msol.div(constants_1.LAMPORTS_PER_MSOL), ray: collateral.ray.div(constants_1.DECIMALS_RAY), ftt: collateral.ftt.div(constants_1.DECIMALS_FTT), eth: collateral.eth.div(constants_1.DECIMALS_ETH), srm: collateral.srm.div(constants_1.DECIMALS_SRM), hbb: collateral.hbb.div(constants_1.HBB_DECIMALS), }; }; exports.lamportsTokenToDecimal = lamportsTokenToDecimal; /** * Replace all big numbers ({@link BN} with {@link Decimal}) of an object * We use this because Anchor deserializes everything to BN, but it doesn't support decimals. * @param obj Object of type T */ const replaceBigNumberWithDecimal = (obj) => { for (let [key, value] of Object.entries(obj)) { if (value instanceof anchor_1.BN) { // @ts-ignore obj[key] = new decimal_js_1.default(value.toString()); } } return obj; }; exports.replaceBigNumberWithDecimal = replaceBigNumberWithDecimal; const decimalToNumSupportedCollateral = (supportedCollateral) => { return { token: supportedCollateral.token.toNumber(), tokenCap: supportedCollateral.tokenCap, }; }; exports.decimalToNumSupportedCollateral = decimalToNumSupportedCollateral; const decimalToNumCollateralWithdrawalCap = (cap) => { return { token: cap.token.toNumber(), tokenCap: cap.tokenCap, }; }; exports.decimalToNumCollateralWithdrawalCap = decimalToNumCollateralWithdrawalCap; const decimalToNumWithdrawalCap = (cap) => { return { configCapacity: cap.configCapacity, currentTotal: cap.currentTotal, lastIntervalStartTimestamp: cap.lastIntervalStartTimestamp.toNumber(), configIntervalLengthSeconds: cap.configIntervalLengthSeconds.toNumber(), }; }; exports.decimalToNumWithdrawalCap = decimalToNumWithdrawalCap; /** * Calculate stability provider's actual stability provided * @param stabilityPoolState * @param stabilityProviderState */ const calculateStabilityProvided = (stabilityPoolState, stabilityProviderState) => { if (stabilityProviderState.depositedStablecoin.isZero() || !stabilityProviderState.userDepositSnapshot.enabled) { return new decimal_js_1.default(0); } if (stabilityProviderState.userDepositSnapshot.epoch < stabilityPoolState.currentEpoch) { return new decimal_js_1.default(0); } const scaleDiff = stabilityPoolState.currentScale.minus(stabilityProviderState.userDepositSnapshot.scale); if (scaleDiff.isZero()) { return stabilityProviderState.depositedStablecoin .mul(stabilityPoolState.p) .dividedBy(stabilityProviderState.userDepositSnapshot.product); } return stabilityProviderState.depositedStablecoin .mul(stabilityPoolState.p) .dividedBy(stabilityProviderState.userDepositSnapshot.product.dividedBy(constants_1.SCALE_FACTOR)); }; exports.calculateStabilityProvided = calculateStabilityProvided; /** * Add all collateral amounts to the second one * @param first * @param second */ const addCollateralAmounts = (first, second) => { const leftExtra = first.extraCollaterals ? first.extraCollaterals : zeroExtraCollateral(); const rightExtra = second.extraCollaterals ? second.extraCollaterals : zeroExtraCollateral(); const extraCollaterals = [ ...new Set([...leftExtra.map((x) => x.tokenId.toNumber()), ...rightExtra.map((x) => x.tokenId.toNumber())]), ]; return { sol: first.sol.add(second.sol), eth: first.eth.add(second.eth), ftt: first.ftt.add(second.ftt), ray: first.ray.add(second.ray), btc: first.btc.add(second.btc), srm: first.srm.add(second.srm), msol: first.msol.add(second.msol), extraCollaterals: extraCollaterals.map((id) => { const left = leftExtra.find((x) => x.tokenId.eq(id))?.amount ?? new decimal_js_1.default(0); const right = rightExtra.find((x) => x.tokenId.eq(id))?.amount ?? new decimal_js_1.default(0); return { amount: left.plus(right), tokenId: new decimal_js_1.default(id), }; }), }; }; exports.addCollateralAmounts = addCollateralAmounts; /** * Calculate pending rewards debt * @param market * @param user */ const getPendingDebt = (market, user) => { const diffStableRpt = market.stablecoinRewardPerToken.minus(user.userStablecoinRewardPerToken); return user.status !== 1 || diffStableRpt.isZero() ? new decimal_js_1.default(0) : diffStableRpt.mul(user.userStake).div(constants_1.DECIMAL_FACTOR); }; /** * Calculate pending rewards debt * @param market * @param user */ const getPendingCollateral = (market, user) => { const diffCollRpt = sub(market.collateralRewardPerToken, user.userCollateralRewardPerToken); return user.status !== 1 || isZero(diffCollRpt) ? zeroCollateral() : mulFrac(diffCollRpt, user.userStake, constants_1.DECIMAL_FACTOR); }; /** * Calculate the user's total USDH debt (borrowed stablecoin + pending rewards) * @param user * @param market */ function calculateTotalDebt(user, market) { const pendingDebt = getPendingDebt(market, user); return user.borrowedStablecoin.add(pendingDebt).dividedBy(constants_1.STABLECOIN_DECIMALS); } /** * Calculate the user's total collateral debt (collateral + pending rewards) * @param user * @param market */ function calculateTotalCollateral(user, market) { const pendingCollateral = getPendingCollateral(market, user); const collateral = (0, exports.addCollateralAmounts)((0, exports.lamportsToDecimal)(user.depositedCollateral), (0, exports.lamportsToDecimal)(user.inactiveCollateral)); return (0, exports.addCollateralAmounts)(pendingCollateral, collateral); } /** * Subtract collateral amounts * @param left * @param right */ function sub(left, right) { const leftExtra = left.extraCollaterals ? left.extraCollaterals : zeroExtraCollateral(); const rightExtra = right.extraCollaterals ? right.extraCollaterals : zeroExtraCollateral(); return { sol: left.sol.minus(right.sol), btc: left.btc.minus(right.btc), eth: left.eth.minus(right.eth), ftt: left.ftt.minus(right.ftt), ray: left.ray.minus(right.ray), srm: left.srm.minus(right.srm), msol: left.msol.minus(right.msol), extraCollaterals: leftExtra.map((coll) => { return { amount: coll.amount.minus(rightExtra.find((x) => x.tokenId.eq(coll.tokenId))?.amount ?? 0), tokenId: coll.tokenId, }; }), }; } /** * Returns true if all collateral amounts equal zero * @param coll */ function isZero(coll) { return (coll.sol.isZero() && coll.btc.isZero() && coll.eth.isZero() && coll.ftt.isZero() && coll.ray.isZero() && coll.srm.isZero() && coll.msol.isZero() && coll.extraCollaterals.every((x) => x.amount.isZero())); } /** * Create new collateral amounts with 0 collateral */ function zeroCollateral() { return { sol: new decimal_js_1.default(0), btc: new decimal_js_1.default(0), eth: new decimal_js_1.default(0), ftt: new decimal_js_1.default(0), ray: new decimal_js_1.default(0), srm: new decimal_js_1.default(0), msol: new decimal_js_1.default(0), extraCollaterals: zeroExtraCollateral(), }; } function zeroExtraCollateral() { const extra = []; for (let i = 0; i < constants_1.ExtraCollateralMap.length; i++) { extra.push({ tokenId: new decimal_js_1.default(constants_1.ExtraCollateralMap[i].id), amount: new decimal_js_1.default(0) }); } return extra; } /** Transforms collateral amounts multiplying by the fraction * @param coll * @param numerator * @param denominator */ function mulFrac(coll, numerator, denominator) { return { sol: new decimal_js_1.default(coll.sol).div(denominator).mul(numerator), btc: new decimal_js_1.default(coll.btc).div(denominator).mul(numerator), eth: new decimal_js_1.default(coll.eth).div(denominator).mul(numerator), ftt: new decimal_js_1.default(coll.ftt).div(denominator).mul(numerator), ray: new decimal_js_1.default(coll.ray).div(denominator).mul(numerator), srm: new decimal_js_1.default(coll.srm).div(denominator).mul(numerator), msol: new decimal_js_1.default(coll.msol).div(denominator).mul(numerator), extraCollaterals: coll.extraCollaterals.map((coll) => { return { amount: coll.amount.div(denominator).mul(numerator), tokenId: coll.tokenId, }; }), }; } function deserializeEpoch(data) { const hmap = []; const numEpochs = data[1].toNumber(); let currentCursor = 1; for (let i = 0; i < numEpochs; i++) { const scale = []; currentCursor += 1; const scaleLength = data[currentCursor].toNumber(); for (let j = 0; j < scaleLength; j++) { const tokenMap = { sol: new decimal_js_1.default(data[currentCursor + 1]), eth: new decimal_js_1.default(data[currentCursor + 2]), btc: new decimal_js_1.default(data[currentCursor + 3]), srm: new decimal_js_1.default(data[currentCursor + 4]), ray: new decimal_js_1.default(data[currentCursor + 5]), ftt: new decimal_js_1.default(data[currentCursor + 6]), hbb: new decimal_js_1.default(data[currentCursor + 7]), msol: new decimal_js_1.default(data[currentCursor + 8]), }; scale.push(tokenMap); currentCursor += constants_1.EPOCH_TO_SCALE_TO_SUM_TOKENS; } hmap.push(scale); } return hmap; } function calculatePendingGains(stabilityPoolState, stabilityProviderState, epochToScaleToSum) { const deserializedEpochToScaleToSum = deserializeEpoch(epochToScaleToSum); const pendingGains = getPendingGains(stabilityProviderState, deserializedEpochToScaleToSum); return (0, exports.lamportsTokenToDecimal)(pendingGains); } function getPendingGains(stabilityProviderState, epochToScaleToSum) { const oldPendingGain = stabilityProviderState.pendingGainsPerUser; const oldPendingGainBig = { sol: new decimal_js_1.default(oldPendingGain.sol), eth: new decimal_js_1.default(oldPendingGain.eth), btc: new decimal_js_1.default(oldPendingGain.btc), ftt: new decimal_js_1.default(oldPendingGain.ftt), ray: new decimal_js_1.default(oldPendingGain.ray), srm: new decimal_js_1.default(oldPendingGain.srm), hbb: new decimal_js_1.default(oldPendingGain.hbb), msol: new decimal_js_1.default(oldPendingGain.msol), }; const newPendingGains = getDepositorPendingGain(stabilityProviderState, epochToScaleToSum); return add(oldPendingGainBig, newPendingGains); } function add(left, right) { return { sol: left.sol.add(right.sol), eth: left.eth.add(right.eth), btc: left.btc.add(right.btc), srm: left.srm.add(right.srm), ray: left.ray.add(right.ray), ftt: left.ftt.add(right.ftt), hbb: left.hbb.add(right.hbb), msol: left.msol.add(right.msol), }; } function subBig(left, right) { return { sol: left.sol.sub(right.sol), eth: left.eth.sub(right.eth), btc: left.btc.sub(right.btc), srm: left.srm.sub(right.srm), ray: left.ray.sub(right.ray), ftt: left.ftt.sub(right.ftt), hbb: left.hbb.sub(right.hbb), msol: left.msol.sub(right.msol), }; } function getDepositorPendingGain(stabilityProviderState, epochToScaleToSum) { const initialDeposit = stabilityProviderState.depositedStablecoin; if (initialDeposit.isZero()) { return { sol: new decimal_js_1.default(0), eth: new decimal_js_1.default(0), btc: new decimal_js_1.default(0), srm: new decimal_js_1.default(0), ray: new decimal_js_1.default(0), ftt: new decimal_js_1.default(0), hbb: new decimal_js_1.default(0), msol: new decimal_js_1.default(0), }; } const depositSnapshot = stabilityProviderState.userDepositSnapshot; const epochSnapshot = depositSnapshot.epoch; const scaleSnapshot = depositSnapshot.scale; const sumSnapshot = depositSnapshot.sum; const productSnapshot = depositSnapshot.product; const sSnapshotBig = { sol: new decimal_js_1.default(sumSnapshot.sol), eth: new decimal_js_1.default(sumSnapshot.eth), btc: new decimal_js_1.default(sumSnapshot.btc), srm: new decimal_js_1.default(sumSnapshot.srm), ray: new decimal_js_1.default(sumSnapshot.ray), ftt: new decimal_js_1.default(sumSnapshot.ftt), hbb: new decimal_js_1.default(sumSnapshot.hbb), msol: new decimal_js_1.default(sumSnapshot.msol), }; const firstPortion = subBig(getSum(epochToScaleToSum, epochSnapshot.toNumber(), scaleSnapshot.toNumber()), sSnapshotBig); const secondPortion = div(getSum(epochToScaleToSum, epochSnapshot.toNumber(), scaleSnapshot.add(1).toNumber()), new decimal_js_1.default(constants_1.SCALE_FACTOR)); return divb(div(mul(add(firstPortion, secondPortion), initialDeposit), productSnapshot), constants_1.DECIMAL_FACTOR); } function getSum(epochToScaletoSum, epoch, scale) { if (epoch < epochToScaletoSum.length) { if (scale < epochToScaletoSum[epoch].length) { return epochToScaletoSum[epoch][scale]; } } return { sol: new decimal_js_1.default(0), eth: new decimal_js_1.default(0), btc: new decimal_js_1.default(0), srm: new decimal_js_1.default(0), ray: new decimal_js_1.default(0), ftt: new decimal_js_1.default(0), hbb: new decimal_js_1.default(0), msol: new decimal_js_1.default(0), }; } function div(left, right) { return { sol: left.sol.div(right), eth: left.eth.div(right), btc: left.btc.div(right), srm: left.srm.div(right), ray: left.ray.div(right), ftt: left.ftt.div(right), hbb: left.hbb.div(right), msol: left.msol.div(right), }; } function divb(left, right) { return { sol: left.sol.dividedBy(right), eth: left.eth.dividedBy(right), btc: left.btc.dividedBy(right), srm: left.srm.dividedBy(right), ray: left.ray.dividedBy(right), ftt: left.ftt.dividedBy(right), hbb: left.hbb.dividedBy(right), msol: left.msol.dividedBy(right), }; } function mul(left, right) { return { sol: left.sol.mul(right), eth: left.eth.mul(right), btc: left.btc.mul(right), srm: left.srm.mul(right), ray: left.ray.mul(right), ftt: left.ftt.mul(right), hbb: left.hbb.mul(right), msol: left.msol.mul(right), }; } const convertTokenLamportsToDecimal = (lamports, tokenName) => { let factor = constants_1.LAMPORTS_PER_SOL; switch (tokenName) { case 'SOL': case 'scnSOL': case 'daoSOL': case 'STSOL': case 'MSOL': factor = constants_1.LAMPORTS_PER_SOL; break; case 'ETH': case 'wstETH': case 'LDO': factor = constants_1.DECIMALS_ETH; break; case 'BTC': factor = constants_1.DECIMALS_BTC; break; case 'SRM': factor = constants_1.DECIMALS_SRM; break; case 'RAY': factor = constants_1.DECIMALS_RAY; break; case 'FTT': factor = constants_1.DECIMALS_FTT; break; case 'JSOL': factor = constants_1.DECIMALS_JSOL; break; case 'USDT': factor = constants_1.DECIMALS_USDT; break; case 'CSOL': factor = constants_1.DECIMALS_CSOL; break; case 'CETH': factor = constants_1.DECIMALS_CETH; break; case 'CBTC': factor = constants_1.DECIMALS_CBTC; break; case 'CMSOL': factor = constants_1.DECIMALS_CMSOL; break; case 'CUSDC': factor = constants_1.DECIMALS_CUSDC; break; case 'CSRM': factor = constants_1.DECIMALS_CSRM; break; case 'CRAY': factor = constants_1.DECIMALS_CRAY; break; case 'CFTT': factor = constants_1.DECIMALS_CFTT; break; case 'CSTSOL': factor = constants_1.DECIMALS_CSTSOL; break; case 'CSLND': factor = constants_1.DECIMALS_CSLND; break; case 'CORCA': factor = constants_1.DECIMALS_CORCA; break; case 'KUSDHUSDCORCA': factor = constants_1.DECIMALS_KUSDHUSDCORCA; break; case 'KUSDCUSDTORCA': factor = constants_1.DECIMALS_KUSDCUSDTORCA; break; case 'KSTSOLSOLORCA': factor = constants_1.DECIMALS_KSTSOLSOLORCA; break; case 'KUSHUSDCORCA': factor = constants_1.DECIMALS_KUSHUSDCORCA; break; default: throw Error(`${tokenName} not supported yet`); } if (lamports.isZero()) { return lamports; } return lamports.dividedBy(factor); }; exports.convertTokenLamportsToDecimal = convertTokenLamportsToDecimal; //# sourceMappingURL=mathUtils.js.map