@ajna-finance/sdk
Version:
A typescript SDK that can be used to create Dapps in Ajna ecosystem.
697 lines • 34.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Pool = void 0;
const tslib_1 = require("tslib");
const ethcall_1 = require("ethcall");
const ethers_1 = require("ethers");
const constants_1 = require("../constants");
const common_1 = require("../contracts/common");
const erc20_1 = require("../contracts/erc20");
const erc20_pool_1 = require("../contracts/erc20-pool");
const pool_1 = require("../contracts/pool");
const pool_info_utils_1 = require("../contracts/pool-info-utils");
const position_manager_1 = require("../contracts/position-manager");
const types_1 = require("../types");
const numeric_1 = require("../utils/numeric");
const pricing_1 = require("../utils/pricing");
const ClaimableReserveAuction_1 = require("./ClaimableReserveAuction");
const PoolUtils_1 = require("./PoolUtils");
const Liquidation_1 = require("./Liquidation");
const time_1 = require("../utils/time");
const Bucket_1 = require("./Bucket");
const erc721_pool_factory_1 = require("../contracts/erc721-pool-factory");
const lender_helper_1 = require("../contracts/lender-helper");
/**
* Abstract baseclass used for pools, regardless of collateral type.
*/
class Pool {
constructor(provider, poolAddress, ajnaAddress, contract, contractMulti) {
this.provider = provider;
this.poolAddress = poolAddress;
this.poolInfoContractUtils = (0, pool_info_utils_1.getPoolInfoUtilsContract)(provider);
this.contractUtilsMulti = (0, pool_info_utils_1.getPoolInfoUtilsContractMulti)();
this.utils = new PoolUtils_1.PoolUtils(provider);
this.ajnaAddress = ajnaAddress;
this.name = 'pool';
this.ethcallProvider = {};
this.contract = contract;
this.contractMulti = contractMulti;
this.quoteAddress = ethers_1.constants.AddressZero;
this.collateralAddress = ethers_1.constants.AddressZero;
this.lenderHelper = (0, lender_helper_1.getLenderHelperContract)(this.provider);
}
initialize() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
this.ethcallProvider = new ethcall_1.Provider();
yield this.ethcallProvider.init(this.provider);
const [quoteAddressResponse, collateralAddressResponse] = yield Promise.all([
(0, pool_1.quoteTokenAddress)(this.contract),
(0, pool_1.collateralAddress)(this.contract),
]);
this.quoteAddress = quoteAddressResponse;
this.collateralAddress = collateralAddressResponse;
try {
const quoteToken = (0, erc20_1.getErc20Contract)(this.quoteAddress, this.provider);
this.quoteSymbol = (yield quoteToken.symbol()).replace(/"+/g, '');
}
catch (e) {
const quoteToken = (0, erc20_1.getDSTokenContract)(this.quoteAddress, this.provider);
this.quoteSymbol = ethers_1.utils.parseBytes32String(yield quoteToken.symbol()).replace(/"+/g, '');
}
});
}
/**
* Approve this pool to manage Ajna token.
* @param signer pool user
* @param allowance approval amount (or MaxUint256)
* @returns promise to transaction
*/
ajnaApprove(signer, allowance) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return (0, erc20_pool_1.approve)(signer, this.poolAddress, this.ajnaAddress, allowance);
});
}
/**
* Approve this pool to manage quote token.
* @param signer pool user
* @param allowance normalized approval amount (or MaxUint256)
* @returns promise to transaction
*/
quoteApprove(signer, allowance) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const denormalizedAllowance = allowance.eq(ethers_1.constants.MaxUint256)
? allowance
: allowance.div(yield (0, pool_1.quoteTokenScale)(this.contract));
return (0, erc20_pool_1.approve)(signer, this.poolAddress, this.quoteAddress, denormalizedAllowance);
});
}
/**
* Retrieves pool reference prices.
* @returns {@link PriceInfo}
*/
getPrices() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const [hpb, hpbIndex, htp, htpIndex, lup, lupIndex] = yield (0, pool_info_utils_1.poolPricesInfo)(this.poolInfoContractUtils, this.poolAddress);
const [, , liquidationDebt] = yield (0, pool_1.debtInfo)(this.contract);
const llbIndex = yield (0, pool_1.depositIndex)(this.contract, liquidationDebt);
const llb = (0, pricing_1.indexToPrice)(llbIndex);
return {
hpb,
hpbIndex: +hpbIndex,
htp,
htpIndex: +htpIndex,
lup,
lupIndex: +lupIndex,
llb,
llbIndex: +llbIndex,
};
});
}
/**
* Retrieves pool statistics.
* @returns {@link Stats}
*/
getStats() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
// PoolInfoUtils multicall
const poolLoansInfoCall = this.contractUtilsMulti.poolLoansInfo(this.poolAddress);
const poolUtilizationInfoCall = this.contractUtilsMulti.poolUtilizationInfo(this.poolAddress);
const poolReservesInfo = this.contractUtilsMulti.poolReservesInfo(this.poolAddress);
const utilsData = yield this.ethcallProvider.all([
poolLoansInfoCall,
poolUtilizationInfoCall,
poolReservesInfo,
]);
const [poolSize, loansCount, , pendingInflator] = utilsData[0];
const [minDebtAmount, collateralization, actualUtilization, targetUtilization] = utilsData[1];
const [reserves, claimableReserves, claimableReservesRemaining, auctionPrice] = utilsData[2];
// Pool multicall
const poolData = yield this.ethcallProvider.all([
this.contractMulti.debtInfo(),
this.contractMulti.interestRateInfo(),
]);
const [debt, , liquidationDebt] = poolData[0];
const rateInfo = poolData[1];
return {
poolSize: ethers_1.BigNumber.from(poolSize),
debt,
liquidationDebt,
loansCount: +loansCount,
minDebtAmount: ethers_1.BigNumber.from(minDebtAmount),
collateralization: ethers_1.BigNumber.from(collateralization),
actualUtilization: ethers_1.BigNumber.from(actualUtilization),
targetUtilization: ethers_1.BigNumber.from(targetUtilization),
reserves: ethers_1.BigNumber.from(reserves),
claimableReserves: ethers_1.BigNumber.from(claimableReserves),
claimableReservesRemaining: ethers_1.BigNumber.from(claimableReservesRemaining),
reserveAuctionPrice: ethers_1.BigNumber.from(auctionPrice),
borrowRate: rateInfo[0],
interestRateLastUpdated: new Date(+rateInfo[1] * 1000),
pendingInflator: ethers_1.BigNumber.from(pendingInflator),
};
});
}
/**
* Measuring from highest price bucket with liquidity, determines index at which all liquidity in
* the book has been utilized by specified debt; useful for estimating LUP.
* @param debtAmount pool debt to be applied to liquidity
* @returns fenwick index
*/
depositIndex(debtAmount) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return yield (0, pool_1.depositIndex)(this.contract, debtAmount);
});
}
/**
* Enables signer to bundle transactions together atomically in a single request.
* @param signer consumer initiating transactions
* @param callData array of transactions to sign and submit
* @returns promise to transaction
*/
multicall(signer, callData) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const contractPoolWithSigner = this.contract.connect(signer);
return (0, common_1.multicall)(contractPoolWithSigner, callData);
});
}
lpAllowance(index, spender, owner) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return yield (0, pool_1.lpAllowance)(this.contract, index, spender, owner);
});
}
increaseLPAllowance(signer, indexes, amounts) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (indexes.length !== amounts.length) {
throw new types_1.SdkError('indexes and amounts must be same length');
}
const poolWithSigner = this.contract.connect(signer);
const spender = (0, position_manager_1.getPositionManagerContract)(signer).address;
return (0, pool_1.increaseLPAllowance)(poolWithSigner, spender, indexes, amounts);
});
}
/**
* Checks if LP allowances are sufficient to memorialize position.
* @param signer Consumer initiating transactions.
* @param indices Fenwick index of the desired bucket.
* @returns `true` if LP allowances are sufficient to memorialize position otherwise `false`.
*/
areLPAllowancesSufficient(signer, indices) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const spender = (0, position_manager_1.getPositionManagerContract)(signer).address;
const signerAddress = yield signer.getAddress();
const allowancePromises = indices.map(index => this.contractMulti.lpAllowance(index, spender, signerAddress));
const allowances = yield this.ethcallProvider.all(allowancePromises);
const balancePromises = indices.map(index => (0, pool_1.lenderInfo)(this.contract, signerAddress, index));
const balances = yield Promise.all(balancePromises);
for (let i = 0; i < allowances.length; ++i) {
const allowance = allowances[i];
const balance = balances[i][0];
if (allowance.lt(balance)) {
return false;
}
}
return true;
});
}
/**
* @param minPrice lowest desired price
* @param maxPrice highest desired price
* @returns array of {@link Bucket}s between specified prices
*/
getBucketsByPriceRange(minPrice, maxPrice) {
if (minPrice.gt(maxPrice))
throw new types_1.SdkError('maxPrice must exceed minPrice');
const buckets = new Array();
for (let index = (0, pricing_1.priceToIndex)(maxPrice); index <= (0, pricing_1.priceToIndex)(minPrice); index++) {
buckets.push(new Bucket_1.Bucket(this.provider, this, index));
}
return buckets;
}
/**
Retrieves origination fee rate for this pool; multiply by new debt to get fee.
@returns origination fee rate, in WAD precision
*/
getOriginationFeeRate() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return yield this.poolInfoContractUtils.borrowFeeRate(this.poolAddress);
});
}
/**
* Retrieve information for a specific loan.
* @param borrowerAddress identifies the loan
* @returns {@link Loan}
*/
getLoan(borrowerAddress) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const poolPricesInfoCall = this.contractUtilsMulti.poolPricesInfo(this.poolAddress);
const poolLoansInfoCall = this.contractUtilsMulti.poolLoansInfo(this.poolAddress);
const borrowerInfoCall = this.contractUtilsMulti.borrowerInfo(this.poolAddress, borrowerAddress);
const auctionStatusCall = this.contractUtilsMulti.auctionStatus(this.poolAddress, borrowerAddress);
const poolBorrowerInfoCall = this.contractMulti.borrowerInfo(borrowerAddress);
const response = yield this.ethcallProvider.all([
poolPricesInfoCall,
poolLoansInfoCall,
borrowerInfoCall,
auctionStatusCall,
poolBorrowerInfoCall,
]);
const [, , , , lup] = response[0];
const [, , , pendingInflator] = response[1];
const [debt, collateral, t0np, tp] = response[2];
const [kickTimestamp] = response[3];
const [, , npTpRatio] = response[4];
const collateralization = debt.gt(0)
? (0, numeric_1.wdiv)((0, numeric_1.wmul)(collateral, lup), (0, numeric_1.wmul)(debt, constants_1.COLLATERALIZATION_FACTOR))
: (0, numeric_1.toWad)(1);
const np = (0, numeric_1.wmul)(t0np, pendingInflator);
return {
collateralization,
debt,
collateral,
thresholdPrice: tp,
neutralPrice: np,
liquidationBond: this.calculateLiquidationBond(npTpRatio, debt),
isKicked: !kickTimestamp.eq(ethers_1.BigNumber.from(0)),
};
});
}
/**
* Retrieve information for a list of loans.
* @param borrowerAddresses identifies the loans
* @returns map of Loans, indexed by borrowerAddress
*/
getLoans(borrowerAddresses) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const calls = [];
// push pool-level requests followed by request for each loan
calls.push(this.contractUtilsMulti.poolPricesInfo(this.poolAddress));
calls.push(this.contractUtilsMulti.poolLoansInfo(this.poolAddress));
for (const loan of borrowerAddresses) {
calls.push(this.contractUtilsMulti.borrowerInfo(this.poolAddress, loan));
calls.push(this.contractUtilsMulti.auctionStatus(this.poolAddress, loan));
calls.push(this.contractMulti.borrowerInfo(loan));
}
// perform the multicall
const response = yield this.ethcallProvider.all(calls);
// since loan details depend upon pool-level data, parse pool data first
const retval = new Map();
let i = 0;
const [, , , , lup] = response[i];
const [, , , pendingInflator] = response[++i];
// calculate npTpRatio, needed for calculating liquidation bonds
// iterate through borrower info, offset by the 3 pool-level requests
let borrowerIndex = 0;
while (borrowerIndex < borrowerAddresses.length) {
const [debt, collateral, t0np, tp] = response[++i];
const kickTimestamp = response[++i][0];
const [, , npTpRatio] = response[++i];
const collateralization = debt.gt(0)
? (0, numeric_1.wdiv)((0, numeric_1.wmul)(collateral, lup), (0, numeric_1.wmul)(debt, constants_1.COLLATERALIZATION_FACTOR))
: (0, numeric_1.toWad)(1);
const np = (0, numeric_1.wmul)(t0np, pendingInflator);
retval.set(borrowerAddresses[borrowerIndex++], {
collateralization,
debt,
collateral,
thresholdPrice: tp,
neutralPrice: np,
liquidationBond: this.calculateLiquidationBond(npTpRatio, debt),
isKicked: !kickTimestamp.eq(ethers_1.BigNumber.from(0)),
});
}
return retval;
});
}
/**
* @param borrowerAddress identifies the loan under liquidation
* @returns {@link Liquidation} models liquidation of a specific loan
*/
getLiquidation(borrowerAddress) {
return new Liquidation_1.Liquidation(this.provider, this.contract, borrowerAddress);
}
/**
* Retrieve statuses for multiple liquidations from the PoolInfoUtils contract.
* @param borrowerAddresses identifies loans under liquidation
* @returns map of AuctionStatuses, indexed by borrower address
*/
getLiquidationStatuses(borrowerAddresses) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
// assemble calldata for requests
const calls = [];
for (const loan of borrowerAddresses) {
calls.push(this.contractUtilsMulti.auctionStatus(this.poolAddress, loan));
}
// perform the multicall
const response = yield this.ethcallProvider.all(calls);
// prepare return value
const retval = new Map();
for (let i = 0; i < response.length; ++i) {
const [kickTimestamp, collateral, debtToCover, isCollateralized, price, neutralPrice] = response[i];
retval.set(borrowerAddresses[i], Liquidation_1.Liquidation._prepareAuctionStatus(yield (0, time_1.getBlockTime)(this.provider), kickTimestamp, collateral, debtToCover, isCollateralized, price, neutralPrice));
}
return retval;
});
}
/**
* Calculates bond required to liquidate a borrower.
* @param npTpRatio relationship between neutral price and threshold price
* @param borrowerDebt loan debt
* @returns required liquidation bond, in WAD precision
*/
calculateLiquidationBond(npTpRatio, borrowerDebt) {
// bondFactor = min((NP-to-TP-ratio - 1)/10, 0.03)
const bondFactor = (0, numeric_1.min)((0, numeric_1.wdiv)(npTpRatio.sub((0, numeric_1.toWad)(1)), (0, numeric_1.toWad)(10)), (0, numeric_1.toWad)(0.03));
// bondSize = bondFactor * borrowerDebt
return (0, numeric_1.wmul)(bondFactor, borrowerDebt);
}
/**
* Calculate a loan's neutral price.
* @param thresholdPrice loan debt * 1.04 / pledged collateral
* @param npTpRatio relationship between neutral price and threshold price
* @returns neutral price, in WAD precision
*/
calculateNeutralPrice(thresholdPrice, npTpRatio) {
return (0, numeric_1.min)((0, numeric_1.wmul)(thresholdPrice, npTpRatio), constants_1.MAX_INFLATED_PRICE_WAD);
}
/**
* Calculate neutral price to threshold price approx. "ratio".
* @param rate current pool "borrower" interest rate
* @returns relationship between the neutral price and threshold price
*/
calculateNpTpRatio(rate) {
return (0, numeric_1.toWad)(1).add((0, numeric_1.wdiv)((0, numeric_1.wsqrt)(rate), (0, numeric_1.toWad)(2)));
}
/**
* Initiates a liquidation of a loan.
* @param signer kicker
* @param borrowerAddress identifies the loan to liquidate
* @param limitIndex reverts if neutral price of loan drops below this bucket before TX processed
* @returns promise to transaction
*/
kick(signer_1, borrowerAddress_1) {
return tslib_1.__awaiter(this, arguments, void 0, function* (signer, borrowerAddress, limitIndex = constants_1.MAX_FENWICK_INDEX) {
const contractPoolWithSigner = this.contract.connect(signer);
return (0, pool_1.kick)(contractPoolWithSigner, borrowerAddress, limitIndex);
});
}
/**
* Checks whether threshold price of a loan is currently above the LUP;
* does NOT estimate whether it would be profitable to liquidate the loan.
* @param borrowerAddress identifies the loan to check
* @returns true if loan may be liquidated, otherwise false
*/
isKickable(borrowerAddress) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const poolPricesInfoCall = this.contractUtilsMulti.poolPricesInfo(this.poolAddress);
const borrowerInfoCall = this.contractUtilsMulti.borrowerInfo(this.poolAddress, borrowerAddress);
const response = yield this.ethcallProvider.all([
poolPricesInfoCall,
borrowerInfoCall,
]);
const [, , , , lup] = response[0];
const [debt, collateral] = response[1];
const tp = collateral.gt(0) ? debt.div(collateral) : ethers_1.BigNumber.from(0);
return lup.lte((0, numeric_1.toWad)(tp));
});
}
/**
* Retrieves status of an auction kicker's liquidation bond.
* @param kickerAddress identifies the actor who kicked liquidations
*/
kickerInfo(kickerAddress) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const [claimable, locked] = yield (0, pool_1.kickerInfo)(this.contract, kickerAddress);
return {
claimable,
locked,
};
});
}
/**
* Called by kickers to withdraw liquidation bond from one or more auctions kicked.
* @param signer kicker
* @param maxAmount optional amount of bond to withdraw; defaults to all
* @returns promise to transaction
*/
withdrawBonds(signer_1) {
return tslib_1.__awaiter(this, arguments, void 0, function* (signer, maxAmount = ethers_1.constants.MaxUint256) {
const contractPoolWithSigner = this.contract.connect(signer);
const recipient = yield signer.getAddress();
return (0, pool_1.withdrawBonds)(contractPoolWithSigner, recipient, maxAmount);
});
}
/**
* Estimates how drawing more debt and/or pledging more collateral would impact loan.
* @param borrowerAddress identifies the loan
* @param debtAmount additional amount of debt to draw (or 0)
* @param collateralAmount additional amount of collateral to pledge (or 0)
* @returns {@link LoanEstimate}
*/
estimateLoan(borrowerAddress, debtAmount, collateralAmount) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
// obtain the borrower debt, and origination fee
const borrowerInfoCall = this.contractUtilsMulti.borrowerInfo(this.poolAddress, borrowerAddress);
const origFeeCall = this.contractUtilsMulti.borrowFeeRate(this.poolAddress);
let response = yield this.ethcallProvider.all([
borrowerInfoCall,
origFeeCall,
]);
const [borrowerDebt, collateral] = response[0];
const originationFeeRate = ethers_1.BigNumber.from(response[1]);
debtAmount = debtAmount.add((0, numeric_1.wmul)(debtAmount, originationFeeRate));
// determine pool debt
let [poolDebt] = yield (0, pool_1.debtInfo)(this.contract);
// determine where this would push the LUP, the current interest rate, and loan count
poolDebt = poolDebt.add(debtAmount);
const lupIndexCall = this.contractMulti.depositIndex(poolDebt);
const rateCall = this.contractMulti.interestRateInfo();
const loansInfoCall = this.contractMulti.loansInfo();
const totalAuctionsInPoolCall = this.contractMulti.totalAuctionsInPool();
response = yield this.ethcallProvider.all([
lupIndexCall,
rateCall,
loansInfoCall,
totalAuctionsInPoolCall,
]);
const lupIndex = +response[0];
const rate = ethers_1.BigNumber.from(response[1][0]);
let noOfLoans = +response[2][2];
const noOfAuctions = +response[3];
noOfLoans += noOfAuctions;
const lup = (0, pricing_1.indexToPrice)(lupIndex);
// calculate the new amount of debt and collateralization
const newDebt = borrowerDebt.add(debtAmount);
const newCollateral = collateral.add(collateralAmount);
const zero = ethers_1.constants.Zero;
const thresholdPrice = newCollateral.eq(zero)
? zero
: (0, numeric_1.wmul)((0, numeric_1.wdiv)(newDebt, newCollateral), constants_1.COLLATERALIZATION_FACTOR);
const encumbered = lup.eq(zero) ? zero : (0, numeric_1.wmul)((0, numeric_1.wdiv)(newDebt, lup), constants_1.COLLATERALIZATION_FACTOR);
const collateralization = encumbered.eq(zero) ? (0, numeric_1.toWad)(1) : (0, numeric_1.wdiv)(newCollateral, encumbered);
// calculate the hypothetical neutral price
const npTpRatio = this.calculateNpTpRatio(rate);
const neutralPrice = this.calculateNeutralPrice(thresholdPrice, npTpRatio);
return {
collateralization,
debt: newDebt,
collateral: newCollateral,
thresholdPrice,
neutralPrice,
isKicked: collateralization.lt(ethers_1.constants.One),
liquidationBond: this.calculateLiquidationBond(npTpRatio, newDebt),
lup: (0, pricing_1.indexToPrice)(lupIndex),
lupIndex: lupIndex,
};
});
}
/**
* Estimates how repaying debt and/or pulling collateral would impact loan.
* @param borrowerAddress identifies the loan
* @param debtAmount amount of debt to repay (or 0)
* @param collateralAmount amount of collateral to pull (or 0)
* @returns {@link LoanEstimate}
*/
estimateRepay(borrowerAddress, debtAmount, collateralAmount) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
// obtain the borrower debt
const utilsContract = (0, pool_info_utils_1.getPoolInfoUtilsContract)(this.provider);
const [borrowerDebt, collateral] = yield (0, pool_info_utils_1.borrowerInfo)(utilsContract, this.poolAddress, borrowerAddress);
// determine pool debt
const [poolDebt, ,] = yield (0, pool_1.debtInfo)(this.contract);
// determine where this would push the LUP, the current interest rate, and loan count
const lupIndexCall = this.contractMulti.depositIndex(poolDebt.sub(debtAmount));
const rateCall = this.contractMulti.interestRateInfo();
const loansInfoCall = this.contractMulti.loansInfo();
const totalAuctionsInPoolCall = this.contractMulti.totalAuctionsInPool();
const response = yield this.ethcallProvider.all([
lupIndexCall,
rateCall,
loansInfoCall,
totalAuctionsInPoolCall,
]);
const lupIndex = +response[0];
const rate = ethers_1.BigNumber.from(response[1][0]);
let noOfLoans = +response[1][2];
const noOfAuctions = +response[2];
noOfLoans += noOfAuctions;
const lup = (0, pricing_1.indexToPrice)(lupIndex);
const zero = ethers_1.constants.Zero;
// calculate the new amount of debt and collateralization
const newDebt = borrowerDebt.sub(debtAmount).isNegative() ? zero : borrowerDebt.sub(debtAmount);
const newCollateral = collateral.sub(collateralAmount).isNegative()
? zero
: collateral.sub(collateralAmount);
const thresholdPrice = newCollateral.eq(zero)
? zero
: (0, numeric_1.wdiv)((0, numeric_1.wmul)(newDebt, constants_1.COLLATERALIZATION_FACTOR), newCollateral);
const encumbered = lup.eq(zero) ? zero : (0, numeric_1.wdiv)((0, numeric_1.wmul)(newDebt, constants_1.COLLATERALIZATION_FACTOR), lup);
const collateralization = encumbered.eq(zero) ? (0, numeric_1.toWad)(1) : (0, numeric_1.wdiv)(newCollateral, encumbered);
// calculate the hypothetical neutral price
const npTpRatio = this.calculateNpTpRatio(rate);
const neutralPrice = this.calculateNeutralPrice(thresholdPrice, npTpRatio);
return {
collateralization,
debt: newDebt,
collateral: newCollateral,
thresholdPrice,
neutralPrice,
isKicked: collateralization.lt(ethers_1.constants.One),
liquidationBond: this.calculateLiquidationBond(npTpRatio, newDebt),
lup: (0, pricing_1.indexToPrice)(lupIndex),
lupIndex: lupIndex,
};
});
}
/**
* Determines whether interest rate will increase, decrease, or remain the same as a result of
* updating the interest rate, without regard to the 12-hour rate update interval.
* @param poolStats pool statistics obtained from @link{Pool.getStats}
*/
estimateUpdateInterest(poolStats) {
// calculating as numbers because squaring a WAD is complicated
const mau = +(0, numeric_1.fromWad)(poolStats.actualUtilization);
const mau102 = mau * 1.02;
const tu = +(0, numeric_1.fromWad)(poolStats.targetUtilization);
if (4 * (tu - mau102) < Math.pow((tu + mau102 - 1), 2) - 1) {
// raise rates
return (0, numeric_1.wmul)(poolStats.borrowRate, (0, numeric_1.toWad)('1.1'));
}
else if (4 * (tu - mau) > 1 - Math.pow((tu + mau - 1), 2)) {
// lower rates
return (0, numeric_1.wmul)(poolStats.borrowRate, (0, numeric_1.toWad)('0.9'));
}
else {
// rates remain unchanged
return poolStats.borrowRate;
}
}
/**
* May be called periodically by actors to adjust interest rate if no other TXes have occurred
* in the past 12 hours.
* @param signer actor who wants to update the interest rate
* @returns transaction
*/
updateInterest(signer) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const contractPoolWithSigner = this.contract.connect(signer);
return (0, pool_1.updateInterest)(contractPoolWithSigner);
});
}
/**
* Updates the neutral price of a borrower's own loan, often useful after partial repayment.
* @param borrower borrower who wishes to stamp their own loan
*/
stampLoan(signer) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const contractPoolWithSigner = this.contract.connect(signer);
return (0, pool_1.stampLoan)(contractPoolWithSigner);
});
}
/**
* Returns `Claimable Reserve Auction` (`CRA`) wrapper object.
* @returns CRA wrapper object
*/
getClaimableReserveAuction() {
return new ClaimableReserveAuction_1.ClaimableReserveAuction(this.provider, this.contract, this.poolInfoContractUtils, this.poolAddress);
}
/**
* Create a new empty LP token for the purpose of memorializing lender position(s).
* @param signer lender
* @param subset optional subset of NFT ids in pool
* @returns promise to transaction
*/
mintLPToken(signer, subset) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const subsetHash = subset
? (0, erc721_pool_factory_1.getSubsetHash)(subset.map(val => ethers_1.BigNumber.from(val)))
: constants_1.ERC20_NON_SUBSET_HASH;
return (0, position_manager_1.mint)(signer, yield signer.getAddress(), this.poolAddress, subsetHash);
});
}
/**
* Burn an empty LP token which has already been redeemed for LP in all buckets.
* @param signer LP token holder
* @param tokenId identifies the empty token to burn
* @returns promise to transaction
*/
burnLPToken(signer, tokenId) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return (0, position_manager_1.burn)(signer, tokenId, this.poolAddress);
});
}
approvePositionManagerLPTransferor(signer) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const addr = (0, position_manager_1.getPositionManagerContract)(signer).address;
return (0, pool_1.approveLPTransferors)(signer, this.contract, [addr]);
});
}
/**
* Approve lend helper to manage quote token.
* @param signer pool user
* @param allowance normalized approval amount (or MaxUint256)
* @returns promise to transaction
*/
quoteApproveHelper(signer, allowance) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const denormalizedAllowance = allowance.eq(ethers_1.constants.MaxUint256)
? allowance
: allowance.div(yield (0, pool_1.quoteTokenScale)(this.contract));
return (0, erc20_pool_1.approve)(signer, this.lenderHelper.address, this.quoteAddress, denormalizedAllowance);
});
}
approveLenderHelperLPTransferor(signer) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return (0, pool_1.approveLPTransferors)(signer, this.contract, [this.lenderHelper.address]);
});
}
isLenderHelperLPTransferorApproved(signer) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const signerAddress = yield signer.getAddress();
return yield this.contract.approvedTransferors(signerAddress, this.lenderHelper.address);
});
}
increaseLenderHelperLPAllowance(signer, indexes, amounts) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (indexes.length !== amounts.length) {
throw new types_1.SdkError('indexes and amounts must be same length');
}
const poolWithSigner = this.contract.connect(signer);
return (0, pool_1.increaseLPAllowance)(poolWithSigner, this.lenderHelper.address, indexes, amounts);
});
}
isLPTransferorApproved(signer) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const transferor = (0, position_manager_1.getPositionManagerContract)(signer).address;
const signerAddress = yield signer.getAddress();
return yield this.contract.approvedTransferors(signerAddress, transferor);
});
}
revokePositionManagerLPTransferor(signer) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const addr = (0, position_manager_1.getPositionManagerContract)(signer).address;
return (0, pool_1.revokeLPTransferors)(signer, this.contract, [addr]);
});
}
}
exports.Pool = Pool;
//# sourceMappingURL=Pool.js.map