fbonds-core
Version:
Banx protocol sdk
188 lines (187 loc) • 9.49 kB
JavaScript
;
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;