UNPKG

fbonds-core

Version:

Banx protocol sdk

188 lines (187 loc) • 9.49 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.calculateTokensPerCollateralFloat = exports.calcMaxMultiplier = exports.calculateTokensPerCollateral = void 0; exports.createTokenBorrowOffers = createTokenBorrowOffers; exports.createTokenBorrowOffersWithCustomLTV = createTokenBorrowOffersWithCustomLTV; exports.createEscrowBalanceByLender = createEscrowBalanceByLender; exports.filterSimpleOffersByVaultBalance = filterSimpleOffersByVaultBalance; const constants_1 = require("../constants"); const helpers_1 = require("../helpers"); const offer_1 = require("./offer"); const anchor_1 = require("@coral-xyz/anchor"); const lodash_1 = require("lodash"); /** * * @param bondOffers BondOfferV3[] * @param userVaults UserVault[] * @param collateralsPerTokenLimit Slice offers according to collateralsPerTokenLimit * Set to undefined to disable slicing * @param tokenPrice token(SOL|USDC) price in collaterals. * F.e. 1 USDC === 6.273 BANX * tokenPrice = 6.273 * 1e9(banx token decimals) = 6273*1e6 * @param tokenDecimals token decimals number * F.e. for USDC = 6; for SOL = 9 * @returns BorrowOffer[] */ function createTokenBorrowOffers({ bondOffers, userVaults, collateralsPerTokenLimit, tokenPrice, tokenDecimals, }) { return ((0, lodash_1.chain)(bondOffers) .map(offer_1.convertToSimpleOfferV2) .compact() .shuffle() .sort((a, b) => (0, helpers_1.sortCompareBN)(a.collateralsPerToken, b.collateralsPerToken, 'asc')) .thru((offers) => collateralsPerTokenLimit ? offers.filter((offer) => offer.collateralsPerToken.gte(collateralsPerTokenLimit)) : offers) //? Filter offers accoring to escrow vault balance .thru((offers) => filterSimpleOffersByVaultBalance(offers, userVaults)) .map((offer) => { const ltv = (0, offer_1.calcOfferLtv)({ collateralsPerToken: offer.collateralsPerToken, tokenPrice, }); const maxCollateralToReceive = (0, offer_1.calcMaxCollateralToReceive)({ maxTokenToGet: offer.maxTokenToGet, tokenDecimals, collateralsPerToken: offer.collateralsPerToken, }); return Object.assign(Object.assign({}, offer), { ltv, maxCollateralToReceive }); }) .value()); } /** * * @param bondOffers BondOfferV3[] * @param userVaults UserVault[] * @param userCollateralsPerToken Allows to use offers with custom LTV: * Filters offers with collateralsPerToken <= userCollateralsPerToken * and sets userCollateralsPerToken in collateralsPerToken field. * Pass undefined to avoid custom LTV usage. * @param tokenPrice token(SOL|USDC) price in collaterals. * F.e. 1 USDC === 6.273 BANX * tokenPrice = 6.273 * 1e9(banx token decimals) = 6273*1e6 * @param tokenDecimals token decimals number * F.e. for USDC = 6; for SOL = 9 * @returns BorrowOffer[] */ function createTokenBorrowOffersWithCustomLTV({ bondOffers, userVaults, userCollateralsPerToken, tokenPrice, tokenDecimals, }) { return ((0, lodash_1.chain)(bondOffers) .map(offer_1.convertToSimpleOfferV2) .compact() .shuffle() .sort((a, b) => (0, helpers_1.sortCompareBN)(a.collateralsPerToken, b.collateralsPerToken, 'asc')) .thru((offers) => { if (!userCollateralsPerToken) return offers; //? F.e. user sets LTV 70% //? 1) filter offers with ltv < 70% //? 2) map offers that are greater than 70% to 70% return (0, lodash_1.chain)(offers) .filter((offer) => offer.collateralsPerToken.lte(userCollateralsPerToken)) .map((offer) => (Object.assign(Object.assign({}, offer), { collateralsPerToken: userCollateralsPerToken }))) .value(); }) //? Filter offers accoring to escrow vault balance .thru((offers) => filterSimpleOffersByVaultBalance(offers, userVaults)) .map((offer) => { const ltv = (0, offer_1.calcOfferLtv)({ collateralsPerToken: offer.collateralsPerToken, tokenPrice, }); const maxCollateralToReceive = (0, offer_1.calcMaxCollateralToReceive)({ maxTokenToGet: offer.maxTokenToGet, tokenDecimals, collateralsPerToken: offer.collateralsPerToken, }); return Object.assign(Object.assign({}, offer), { ltv, maxCollateralToReceive }); }) .value()); } function createEscrowBalanceByLender(userVaults) { return (0, lodash_1.chain)(userVaults) .map((userVault) => [userVault.user.toBase58(), userVault.offerLiquidityAmount]) .fromPairs() .value(); } function filterSimpleOffersByVaultBalance(offers, userVaults) { const escrowBalanceByLender = createEscrowBalanceByLender(userVaults); const { filteredOffers } = (0, lodash_1.reduce)(offers, (acc, offer) => { var _a; const { lender, maxTokenToGet } = offer; const escrowBalance = (_a = acc.escrowBalanceByLender[lender.toBase58()]) !== null && _a !== void 0 ? _a : new anchor_1.BN(0); //? mutate offer to allow partial funding if (escrowBalance.lt(maxTokenToGet) && !escrowBalance.eq(new anchor_1.BN(0))) { return { filteredOffers: [...acc.filteredOffers, Object.assign(Object.assign({}, offer), { maxTokenToGet: escrowBalance })], escrowBalanceByLender: Object.assign(Object.assign({}, acc.escrowBalanceByLender), { [lender.toBase58()]: new anchor_1.BN(0) }), }; } if (escrowBalance.gte(maxTokenToGet)) { return { filteredOffers: [...acc.filteredOffers, offer], escrowBalanceByLender: Object.assign(Object.assign({}, acc.escrowBalanceByLender), { [lender.toBase58()]: escrowBalance.sub(maxTokenToGet) }), }; } return acc; }, { filteredOffers: [], escrowBalanceByLender }); return filteredOffers; } /** * Calculates the number of tokens per collateral unit. * @param {BN} collateralsPerToken - The amount of collateral per token. (1 human token = collateral decimals) * @param {number} collateralDecimals - The number of decimal places used by the collateral token. * @param {number} lendingTokenDecimals - The number of decimal places used by the lending token. * @returns {BN} - tokensPerCollateral (1 humanCollateral = token decimals) */ const calculateTokensPerCollateral = (collateralsPerToken, collateralDecimals, lendingTokenDecimals) => { if (!collateralsPerToken || collateralsPerToken.eq(constants_1.ZERO_BN)) { return constants_1.ZERO_BN; } const adjustedScale = collateralDecimals + lendingTokenDecimals; const scaledValue = new anchor_1.BN(10).pow(new anchor_1.BN(adjustedScale)); const tokensPerCollateral = scaledValue.div(collateralsPerToken); return tokensPerCollateral; }; exports.calculateTokensPerCollateral = calculateTokensPerCollateral; /** * @param {SimpleOffer} offer - SimpleOffer * @param {BN} maxCollateralToReceive - max amount of collateral that can be borrowed from offer * @param {number} tokenPriceInCollateral - how much collateral is 1 token worth. F.e. 1 USDC = 0.2339 JLP. tokenPriceInCollateral=0.2339 * @param {number} collateralDecimals - decimals of collateral. F.e. 9 * @param {number} tokenDecimals - decimals of token. F.e. 9 * @returns Max multiplier in percent. F.e. 50.5 === 50.5% */ const calcMaxMultiplier = ({ offer, maxCollateralToReceive, tokenPriceInCollateral, //? collateral conversionRate collateralDecimals, tokenDecimals, }) => { const maxTokenToGet = offer.maxTokenToGet; const tokenPerCollateral = (0, exports.calculateTokensPerCollateral)(offer.collateralsPerToken, collateralDecimals, tokenDecimals) //TODO Remove division adjustment .mul(new anchor_1.BN(995)) .div(new anchor_1.BN(1000)); if (tokenPerCollateral.isZero()) return 0; const tokenPriceInCollateralBN = new anchor_1.BN(tokenPriceInCollateral * Math.pow(10, collateralDecimals)); const numerator = maxTokenToGet.mul(new anchor_1.BN(10).pow(new anchor_1.BN(collateralDecimals + tokenDecimals)).sub(tokenPriceInCollateralBN.mul(tokenPerCollateral))); const maxBorrowerCollateralAmount = numerator.div(tokenPerCollateral); const multiplier = maxCollateralToReceive.mul(new anchor_1.BN(Math.pow(10, tokenDecimals))).div(maxBorrowerCollateralAmount); return multiplier.toNumber(); }; exports.calcMaxMultiplier = calcMaxMultiplier; /** * Calculates the number of tokens per collateral unit. * @param {number} collateralsPerToken - The amount of collateral per token. (1 human token = collateral decimals) * @param {number} collateralDecimals - The number of decimal places used by the collateral token. * @param {number} lendingTokenDecimals - The number of decimal places used by the lending token. * @returns {number} - tokensPerCollateral (1 humanCollateral = token decimals) */ const calculateTokensPerCollateralFloat = (collateralsPerToken, collateralDecimals, lendingTokenDecimals) => { if (!collateralsPerToken || collateralsPerToken === 0) { return 0; } const adjustedScale = collateralDecimals + lendingTokenDecimals; const scaledValue = Math.pow(10, adjustedScale); const tokensPerCollateral = scaledValue / collateralsPerToken; return tokensPerCollateral; }; exports.calculateTokensPerCollateralFloat = calculateTokensPerCollateralFloat;