aave-protocol-v2/test/helpers/utils/calculations.ts

1509 lines
48 KiB
TypeScript

import BigNumber from "bignumber.js";
import {
ONE_YEAR,
RAY,
MAX_UINT_AMOUNT,
OPTIMAL_UTILIZATION_RATE,
EXCESS_UTILIZATION_RATE,
ZERO_ADDRESS,
} from "../../../helpers/constants";
import {
IReserveParams,
iAavePoolAssets,
RateMode,
} from "../../../helpers/types";
import "./math";
import {ReserveData, UserReserveData} from "./interfaces";
export const strToBN = (amount: string): BigNumber => new BigNumber(amount);
interface Configuration {
reservesParams: iAavePoolAssets<IReserveParams>;
ethereumAddress: string;
}
export const configuration: Configuration = <Configuration>{};
export const calcExpectedUserDataAfterDeposit = (
amountDeposited: string,
reserveDataBeforeAction: ReserveData,
reserveDataAfterAction: ReserveData,
userDataBeforeAction: UserReserveData,
txTimestamp: BigNumber,
currentTimestamp: BigNumber,
txCost: BigNumber
): UserReserveData => {
const expectedUserData = <UserReserveData>{};
expectedUserData.currentBorrowBalance = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
expectedUserData.principalBorrowBalance =
userDataBeforeAction.principalBorrowBalance;
expectedUserData.borrowRateMode = userDataBeforeAction.borrowRateMode;
if (userDataBeforeAction.borrowRateMode === RateMode.None) {
expectedUserData.borrowRate = new BigNumber("0");
} else {
expectedUserData.borrowRate = userDataBeforeAction.borrowRate;
}
expectedUserData.liquidityRate = reserveDataAfterAction.liquidityRate;
expectedUserData.originationFee = userDataBeforeAction.originationFee;
expectedUserData.currentATokenBalance = userDataBeforeAction.currentATokenBalance.plus(
amountDeposited
);
if (userDataBeforeAction.currentATokenBalance.eq(0)) {
expectedUserData.usageAsCollateralEnabled = true;
} else {
//if user is redeeming everything, usageAsCollateralEnabled must be false
if (expectedUserData.currentATokenBalance.eq(0)) {
expectedUserData.usageAsCollateralEnabled = false;
} else {
expectedUserData.usageAsCollateralEnabled =
userDataBeforeAction.usageAsCollateralEnabled;
}
}
expectedUserData.variableBorrowIndex =
userDataBeforeAction.variableBorrowIndex;
if (reserveDataBeforeAction.address === configuration.ethereumAddress) {
// console.log("** ETH CASE ****")
expectedUserData.walletBalance = userDataBeforeAction.walletBalance
.minus(txCost)
.minus(amountDeposited);
} else {
// console.log("** TOKEN CASE ****")
// console.log(userDataBeforeAction.walletBalance.toString())
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(
amountDeposited
);
}
expectedUserData.principalATokenBalance = expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
reserveDataBeforeAction,
userDataBeforeAction,
txTimestamp
).plus(amountDeposited);
expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance;
expectedUserData.interestRedirectionAddress =
userDataBeforeAction.interestRedirectionAddress;
expectedUserData.currentATokenUserIndex = calcExpectedATokenUserIndex(
reserveDataBeforeAction,
expectedUserData.currentATokenBalance,
expectedUserData.redirectedBalance,
txTimestamp
);
expectedUserData.redirectionAddressRedirectedBalance = calcExpectedRedirectedBalance(
userDataBeforeAction,
expectedUserData,
userDataBeforeAction.redirectionAddressRedirectedBalance,
new BigNumber(amountDeposited),
new BigNumber(0)
);
return expectedUserData;
};
export const calcExpectedUserDataAfterRedeem = (
amountRedeemed: string,
reserveDataBeforeAction: ReserveData,
reserveDataAfterAction: ReserveData,
userDataBeforeAction: UserReserveData,
txTimestamp: BigNumber,
currentTimestamp: BigNumber,
txCost: BigNumber
): UserReserveData => {
const expectedUserData = <UserReserveData>{};
const aTokenBalance = calcExpectedATokenBalance(
reserveDataBeforeAction,
userDataBeforeAction,
txTimestamp
);
if (amountRedeemed == MAX_UINT_AMOUNT) {
amountRedeemed = aTokenBalance.toFixed(0);
}
expectedUserData.principalATokenBalance = expectedUserData.currentATokenBalance = aTokenBalance.minus(
amountRedeemed
);
expectedUserData.currentBorrowBalance = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
expectedUserData.principalBorrowBalance =
userDataBeforeAction.principalBorrowBalance;
expectedUserData.borrowRateMode = userDataBeforeAction.borrowRateMode;
expectedUserData.borrowRateMode = userDataBeforeAction.borrowRateMode;
if (userDataBeforeAction.borrowRateMode === RateMode.None) {
expectedUserData.borrowRate = new BigNumber("0");
} else {
expectedUserData.borrowRate = userDataBeforeAction.borrowRate;
}
expectedUserData.liquidityRate = reserveDataAfterAction.liquidityRate;
expectedUserData.originationFee = userDataBeforeAction.originationFee;
if (userDataBeforeAction.currentATokenBalance.eq(0)) {
expectedUserData.usageAsCollateralEnabled = true;
} else {
//if user is redeeming everything, usageAsCollateralEnabled must be false
if (expectedUserData.currentATokenBalance.eq(0)) {
expectedUserData.usageAsCollateralEnabled = false;
} else {
expectedUserData.usageAsCollateralEnabled =
userDataBeforeAction.usageAsCollateralEnabled;
}
}
expectedUserData.variableBorrowIndex =
userDataBeforeAction.variableBorrowIndex;
if (reserveDataBeforeAction.address === configuration.ethereumAddress) {
expectedUserData.walletBalance = userDataBeforeAction.walletBalance
.minus(txCost)
.plus(amountRedeemed);
} else {
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(
amountRedeemed
);
}
expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance;
if (
expectedUserData.currentATokenBalance.eq(0) &&
expectedUserData.redirectedBalance.eq(0)
) {
expectedUserData.interestRedirectionAddress = ZERO_ADDRESS;
} else {
expectedUserData.interestRedirectionAddress =
userDataBeforeAction.interestRedirectionAddress;
}
expectedUserData.currentATokenUserIndex = calcExpectedATokenUserIndex(
reserveDataBeforeAction,
expectedUserData.currentATokenBalance,
expectedUserData.redirectedBalance,
txTimestamp
);
expectedUserData.redirectionAddressRedirectedBalance = calcExpectedRedirectedBalance(
userDataBeforeAction,
expectedUserData,
userDataBeforeAction.redirectionAddressRedirectedBalance,
new BigNumber(0),
new BigNumber(amountRedeemed)
);
return expectedUserData;
};
export const calcExpectedReserveDataAfterDeposit = (
amountDeposited: string,
reserveDataBeforeAction: ReserveData,
txTimestamp: BigNumber
): ReserveData => {
const expectedReserveData: ReserveData = <ReserveData>{};
expectedReserveData.address = reserveDataBeforeAction.address;
expectedReserveData.totalLiquidity = new BigNumber(
reserveDataBeforeAction.totalLiquidity
).plus(amountDeposited);
expectedReserveData.availableLiquidity = new BigNumber(
reserveDataBeforeAction.availableLiquidity
).plus(amountDeposited);
expectedReserveData.totalBorrowsStable =
reserveDataBeforeAction.totalBorrowsStable;
expectedReserveData.totalBorrowsVariable =
reserveDataBeforeAction.totalBorrowsVariable;
expectedReserveData.averageStableBorrowRate =
reserveDataBeforeAction.averageStableBorrowRate;
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.totalLiquidity
);
const rates = calcExpectedInterestRates(
reserveDataBeforeAction.symbol,
reserveDataBeforeAction.marketStableRate,
expectedReserveData.utilizationRate,
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.averageStableBorrowRate
);
expectedReserveData.liquidityRate = rates[0];
expectedReserveData.stableBorrowRate = rates[1];
expectedReserveData.variableBorrowRate = rates[2];
expectedReserveData.averageStableBorrowRate =
reserveDataBeforeAction.averageStableBorrowRate;
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
reserveDataBeforeAction,
txTimestamp
);
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
reserveDataBeforeAction,
txTimestamp
);
return expectedReserveData;
};
export const calcExpectedReserveDataAfterRedeem = (
amountRedeemed: string,
reserveDataBeforeAction: ReserveData,
userDataBeforeAction: UserReserveData,
txTimestamp: BigNumber
): ReserveData => {
const expectedReserveData: ReserveData = <ReserveData>{};
expectedReserveData.address = reserveDataBeforeAction.address;
if (amountRedeemed == MAX_UINT_AMOUNT) {
amountRedeemed = calcExpectedATokenBalance(
reserveDataBeforeAction,
userDataBeforeAction,
txTimestamp
).toFixed();
}
expectedReserveData.totalLiquidity = new BigNumber(
reserveDataBeforeAction.totalLiquidity
).minus(amountRedeemed);
expectedReserveData.availableLiquidity = new BigNumber(
reserveDataBeforeAction.availableLiquidity
).minus(amountRedeemed);
expectedReserveData.totalBorrowsStable =
reserveDataBeforeAction.totalBorrowsStable;
expectedReserveData.totalBorrowsVariable =
reserveDataBeforeAction.totalBorrowsVariable;
expectedReserveData.averageStableBorrowRate =
reserveDataBeforeAction.averageStableBorrowRate;
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.totalLiquidity
);
const rates = calcExpectedInterestRates(
reserveDataBeforeAction.symbol,
reserveDataBeforeAction.marketStableRate,
expectedReserveData.utilizationRate,
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.averageStableBorrowRate
);
expectedReserveData.liquidityRate = rates[0];
expectedReserveData.stableBorrowRate = rates[1];
expectedReserveData.variableBorrowRate = rates[2];
expectedReserveData.averageStableBorrowRate =
reserveDataBeforeAction.averageStableBorrowRate;
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
reserveDataBeforeAction,
txTimestamp
);
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
reserveDataBeforeAction,
txTimestamp
);
return expectedReserveData;
};
export const calcExpectedReserveDataAfterBorrow = (
amountBorrowed: string,
borrowRateMode: string,
reserveDataBeforeAction: ReserveData,
userDataBeforeAction: UserReserveData,
txTimestamp: BigNumber,
currentTimestamp: BigNumber
): ReserveData => {
const expectedReserveData = <ReserveData>{};
expectedReserveData.address = reserveDataBeforeAction.address;
let userBalanceIncrease: BigNumber = new BigNumber(0);
let userCurrentBorrowBalance: BigNumber = new BigNumber(0);
const amountBorrowedBN = new BigNumber(amountBorrowed);
if (userDataBeforeAction.currentBorrowBalance.gt(0)) {
//if the user performing the action had already a borrow, we need to compound the balance until the action
userCurrentBorrowBalance = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
userBalanceIncrease = userCurrentBorrowBalance.minus(
userDataBeforeAction.principalBorrowBalance
);
expectedReserveData.totalLiquidity = reserveDataBeforeAction.totalLiquidity.plus(
userBalanceIncrease
);
} else {
expectedReserveData.totalLiquidity = reserveDataBeforeAction.totalLiquidity;
}
expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity.minus(
amountBorrowedBN
);
//substract the previous principal from the total borrows, depending on which borrow mode the previous borrow was
if (userDataBeforeAction.borrowRateMode == RateMode.Stable) {
expectedReserveData.totalBorrowsStable = reserveDataBeforeAction.totalBorrowsStable.minus(
userDataBeforeAction.principalBorrowBalance
);
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.totalBorrowsStable,
userDataBeforeAction.principalBorrowBalance.negated(),
userDataBeforeAction.borrowRate
);
expectedReserveData.totalBorrowsVariable =
reserveDataBeforeAction.totalBorrowsVariable;
} else if (userDataBeforeAction.borrowRateMode == RateMode.Variable) {
expectedReserveData.totalBorrowsVariable = reserveDataBeforeAction.totalBorrowsVariable.minus(
userDataBeforeAction.principalBorrowBalance
);
expectedReserveData.totalBorrowsStable =
reserveDataBeforeAction.totalBorrowsStable;
expectedReserveData.averageStableBorrowRate =
reserveDataBeforeAction.averageStableBorrowRate;
} else {
expectedReserveData.totalBorrowsVariable =
reserveDataBeforeAction.totalBorrowsVariable;
expectedReserveData.averageStableBorrowRate =
reserveDataBeforeAction.averageStableBorrowRate;
expectedReserveData.totalBorrowsStable =
reserveDataBeforeAction.totalBorrowsStable;
}
//add the previous principal + new amount borrowed + accrued interest to the total borrows, depending
//on the new borrow rate mode
if (borrowRateMode === RateMode.Stable) {
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
expectedReserveData.averageStableBorrowRate,
expectedReserveData.totalBorrowsStable,
userDataBeforeAction.principalBorrowBalance
.plus(amountBorrowedBN)
.plus(userBalanceIncrease),
reserveDataBeforeAction.stableBorrowRate
);
expectedReserveData.totalBorrowsStable = expectedReserveData.totalBorrowsStable
.plus(userDataBeforeAction.principalBorrowBalance)
.plus(userBalanceIncrease)
.plus(amountBorrowedBN);
} else {
expectedReserveData.totalBorrowsVariable = expectedReserveData.totalBorrowsVariable
.plus(userDataBeforeAction.principalBorrowBalance)
.plus(userBalanceIncrease)
.plus(amountBorrowedBN);
}
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.totalLiquidity
);
const rates = calcExpectedInterestRates(
reserveDataBeforeAction.symbol,
reserveDataBeforeAction.marketStableRate,
expectedReserveData.utilizationRate,
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.averageStableBorrowRate
);
expectedReserveData.liquidityRate = rates[0];
expectedReserveData.stableBorrowRate = rates[1];
expectedReserveData.variableBorrowRate = rates[2];
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
reserveDataBeforeAction,
txTimestamp
);
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
reserveDataBeforeAction,
txTimestamp
);
return expectedReserveData;
};
export const calcExpectedReserveDataAfterRepay = (
amountRepaid: string,
reserveDataBeforeAction: ReserveData,
userDataBeforeAction: UserReserveData,
txTimestamp: BigNumber,
currentTimestamp: BigNumber
): ReserveData => {
const expectedReserveData: ReserveData = <ReserveData>{};
expectedReserveData.address = reserveDataBeforeAction.address;
let amountRepaidBN = new BigNumber(amountRepaid);
const userCurrentBorrowBalance = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
const userBalanceIncrease = userCurrentBorrowBalance.minus(
userDataBeforeAction.principalBorrowBalance
);
expectedReserveData.totalLiquidity = reserveDataBeforeAction.totalLiquidity.plus(
userBalanceIncrease
);
//if amount repaid = MAX_UINT_AMOUNT, user is repaying everything
if (amountRepaidBN.abs().eq(MAX_UINT_AMOUNT)) {
amountRepaidBN = userCurrentBorrowBalance;
} else {
amountRepaidBN = userDataBeforeAction.originationFee.gt(amountRepaidBN)
? new BigNumber("0")
: amountRepaidBN.minus(userDataBeforeAction.originationFee);
}
if (amountRepaidBN.eq(0)) {
//user is only repaying part or all the utilization fee
expectedReserveData.availableLiquidity =
reserveDataBeforeAction.availableLiquidity;
} else {
expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity.plus(
amountRepaidBN
);
}
if (userDataBeforeAction.borrowRateMode === RateMode.Stable) {
expectedReserveData.totalBorrowsStable = reserveDataBeforeAction.totalBorrowsStable
.plus(userBalanceIncrease)
.minus(amountRepaidBN);
const avgStableRateAfterBalanceIncrease = calcExpectedAverageStableBorrowRate(
reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.totalBorrowsStable,
userBalanceIncrease,
userDataBeforeAction.borrowRate
);
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
avgStableRateAfterBalanceIncrease,
reserveDataBeforeAction.totalBorrowsStable.plus(userBalanceIncrease),
amountRepaidBN.negated(),
userDataBeforeAction.borrowRate
);
expectedReserveData.totalBorrowsVariable =
reserveDataBeforeAction.totalBorrowsVariable;
} else if (userDataBeforeAction.borrowRateMode === RateMode.Variable) {
expectedReserveData.totalBorrowsVariable = reserveDataBeforeAction.totalBorrowsVariable
.plus(userBalanceIncrease)
.minus(amountRepaidBN);
expectedReserveData.totalBorrowsStable =
reserveDataBeforeAction.totalBorrowsStable;
expectedReserveData.averageStableBorrowRate =
reserveDataBeforeAction.averageStableBorrowRate;
} else {
throw `Invalid rate mode found: Expected stable or variable but found ${userDataBeforeAction.borrowRateMode}`;
}
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.totalLiquidity
);
const rates = calcExpectedInterestRates(
reserveDataBeforeAction.symbol,
reserveDataBeforeAction.marketStableRate,
expectedReserveData.utilizationRate,
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.averageStableBorrowRate
);
expectedReserveData.liquidityRate = rates[0];
expectedReserveData.stableBorrowRate = rates[1];
expectedReserveData.variableBorrowRate = rates[2];
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
reserveDataBeforeAction,
txTimestamp
);
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
reserveDataBeforeAction,
txTimestamp
);
return expectedReserveData;
};
export const calcExpectedUserDataAfterBorrow = (
amountBorrowed: string,
interestRateMode: string,
reserveDataBeforeAction: ReserveData,
expectedDataAfterAction: ReserveData,
userDataBeforeAction: UserReserveData,
txTimestamp: BigNumber,
currentTimestamp: BigNumber,
txCost: BigNumber
): UserReserveData => {
const expectedUserData = <UserReserveData>{};
const originationFee = calcExpectedOriginationFee(amountBorrowed);
const borrowBalanceBeforeTx = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
expectedUserData.principalBorrowBalance = borrowBalanceBeforeTx.plus(
amountBorrowed
);
if (currentTimestamp.gt(txTimestamp)) {
//calculate also the accrued balance after the time passed
const borrowBalanceAfterTx = calcExpectedCompoundedBorrowBalance(
{
...userDataBeforeAction,
borrowRateMode: interestRateMode,
borrowRate:
interestRateMode === RateMode.Stable
? reserveDataBeforeAction.stableBorrowRate
: expectedDataAfterAction.variableBorrowRate,
principalBorrowBalance: borrowBalanceBeforeTx.plus(amountBorrowed),
variableBorrowIndex: expectedDataAfterAction.variableBorrowIndex,
lastUpdateTimestamp: txTimestamp,
},
expectedDataAfterAction,
currentTimestamp
);
expectedUserData.currentBorrowBalance = borrowBalanceAfterTx;
} else {
expectedUserData.currentBorrowBalance =
expectedUserData.principalBorrowBalance;
}
if (interestRateMode === RateMode.Stable) {
expectedUserData.borrowRate = reserveDataBeforeAction.stableBorrowRate;
expectedUserData.variableBorrowIndex = new BigNumber(0);
} else if (interestRateMode === RateMode.Variable) {
expectedUserData.borrowRate = expectedDataAfterAction.variableBorrowRate;
expectedUserData.variableBorrowIndex =
expectedDataAfterAction.variableBorrowIndex;
}
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
expectedUserData.originationFee = userDataBeforeAction.originationFee.plus(
originationFee
);
expectedUserData.usageAsCollateralEnabled =
userDataBeforeAction.usageAsCollateralEnabled;
expectedUserData.borrowRateMode = interestRateMode;
expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
expectedDataAfterAction,
userDataBeforeAction,
currentTimestamp
);
expectedUserData.principalATokenBalance =
userDataBeforeAction.principalATokenBalance;
expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance;
expectedUserData.interestRedirectionAddress =
userDataBeforeAction.interestRedirectionAddress;
expectedUserData.redirectionAddressRedirectedBalance =
userDataBeforeAction.redirectionAddressRedirectedBalance;
expectedUserData.currentATokenUserIndex =
userDataBeforeAction.currentATokenUserIndex;
if (reserveDataBeforeAction.address === configuration.ethereumAddress) {
expectedUserData.walletBalance = userDataBeforeAction.walletBalance
.minus(txCost)
.plus(amountBorrowed);
} else {
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(
amountBorrowed
);
}
return expectedUserData;
};
export const calcExpectedUserDataAfterRepay = (
totalRepaid: string,
reserveDataBeforeAction: ReserveData,
expectedDataAfterAction: ReserveData,
userDataBeforeAction: UserReserveData,
user: string,
onBehalfOf: string,
txTimestamp: BigNumber,
currentTimestamp: BigNumber,
txCost: BigNumber
): UserReserveData => {
const expectedUserData = <UserReserveData>{};
const userCurrentBorrowBalance = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
const userBalanceIncrease = userCurrentBorrowBalance.minus(
userDataBeforeAction.principalBorrowBalance
);
if (new BigNumber(totalRepaid).abs().eq(MAX_UINT_AMOUNT)) {
//full repay in progress
totalRepaid = userCurrentBorrowBalance
.plus(userDataBeforeAction.originationFee)
.toFixed(0);
}
if (userDataBeforeAction.originationFee.lt(totalRepaid)) {
expectedUserData.originationFee = new BigNumber(0);
const totalRepaidMinusFees = new BigNumber(totalRepaid).minus(
userDataBeforeAction.originationFee
);
expectedUserData.principalBorrowBalance = userDataBeforeAction.principalBorrowBalance
.plus(userBalanceIncrease)
.minus(totalRepaidMinusFees);
expectedUserData.currentBorrowBalance = userCurrentBorrowBalance.minus(
totalRepaidMinusFees
);
} else {
expectedUserData.originationFee = userDataBeforeAction.originationFee.minus(
totalRepaid
);
expectedUserData.principalBorrowBalance = userCurrentBorrowBalance;
expectedUserData.currentBorrowBalance = userCurrentBorrowBalance;
}
if (expectedUserData.currentBorrowBalance.eq("0")) {
//user repaid everything
expectedUserData.borrowRate = new BigNumber("0");
expectedUserData.borrowRateMode = RateMode.None;
expectedUserData.variableBorrowIndex = new BigNumber("0");
} else {
if (userDataBeforeAction.borrowRateMode === RateMode.Stable) {
expectedUserData.borrowRate = userDataBeforeAction.borrowRate;
expectedUserData.variableBorrowIndex = new BigNumber("0");
} else {
expectedUserData.borrowRate = expectedDataAfterAction.variableBorrowRate;
expectedUserData.variableBorrowIndex =
expectedDataAfterAction.variableBorrowIndex;
}
expectedUserData.borrowRateMode = userDataBeforeAction.borrowRateMode;
}
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
expectedUserData.usageAsCollateralEnabled =
userDataBeforeAction.usageAsCollateralEnabled;
expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
reserveDataBeforeAction,
userDataBeforeAction,
txTimestamp
);
expectedUserData.principalATokenBalance =
userDataBeforeAction.principalATokenBalance;
expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance;
expectedUserData.interestRedirectionAddress =
userDataBeforeAction.interestRedirectionAddress;
expectedUserData.redirectionAddressRedirectedBalance =
userDataBeforeAction.redirectionAddressRedirectedBalance;
expectedUserData.currentATokenUserIndex = calcExpectedATokenUserIndex(
reserveDataBeforeAction,
expectedUserData.currentATokenBalance,
expectedUserData.redirectedBalance,
txTimestamp
);
if (user === onBehalfOf) {
//if user repaid for himself, update the wallet balances
if (reserveDataBeforeAction.address === configuration.ethereumAddress) {
expectedUserData.walletBalance = userDataBeforeAction.walletBalance
.minus(txCost)
.minus(totalRepaid);
} else {
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(
totalRepaid
);
}
} else {
//wallet balance didn't change
expectedUserData.walletBalance = userDataBeforeAction.walletBalance;
}
return expectedUserData;
};
export const calcExpectedUserDataAfterSetUseAsCollateral = (
useAsCollateral: boolean,
reserveDataBeforeAction: ReserveData,
userDataBeforeAction: UserReserveData,
txCost: BigNumber
): UserReserveData => {
const expectedUserData = {...userDataBeforeAction};
expectedUserData.usageAsCollateralEnabled = useAsCollateral;
if (reserveDataBeforeAction.address === configuration.ethereumAddress) {
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(
txCost
);
}
return expectedUserData;
};
export const calcExpectedReserveDataAfterSwapRateMode = (
reserveDataBeforeAction: ReserveData,
userDataBeforeAction: UserReserveData,
txTimestamp: BigNumber
): ReserveData => {
const expectedReserveData: ReserveData = <ReserveData>{};
expectedReserveData.address = reserveDataBeforeAction.address;
let userBalanceIncrease: BigNumber = new BigNumber(0);
let userCurrentBorrowBalance: BigNumber = new BigNumber(0);
userCurrentBorrowBalance = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
userBalanceIncrease = userCurrentBorrowBalance.minus(
userDataBeforeAction.principalBorrowBalance
);
expectedReserveData.totalLiquidity = reserveDataBeforeAction.totalLiquidity.plus(
userBalanceIncrease
);
expectedReserveData.availableLiquidity =
reserveDataBeforeAction.availableLiquidity;
if (userDataBeforeAction.borrowRateMode === RateMode.Stable) {
//swap to variable
expectedReserveData.totalBorrowsStable = reserveDataBeforeAction.totalBorrowsStable
.plus(userBalanceIncrease)
.minus(userCurrentBorrowBalance);
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.totalBorrowsStable.plus(userBalanceIncrease),
userCurrentBorrowBalance.negated(),
userDataBeforeAction.borrowRate
);
expectedReserveData.totalBorrowsVariable = reserveDataBeforeAction.totalBorrowsVariable.plus(
userCurrentBorrowBalance
);
} else {
expectedReserveData.totalBorrowsVariable = reserveDataBeforeAction.totalBorrowsVariable
.plus(userBalanceIncrease)
.minus(userCurrentBorrowBalance);
expectedReserveData.totalBorrowsStable = reserveDataBeforeAction.totalBorrowsStable.plus(
userCurrentBorrowBalance
);
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.totalBorrowsStable,
userCurrentBorrowBalance,
reserveDataBeforeAction.stableBorrowRate
);
}
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.totalLiquidity
);
const rates = calcExpectedInterestRates(
reserveDataBeforeAction.symbol,
reserveDataBeforeAction.marketStableRate,
expectedReserveData.utilizationRate,
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.averageStableBorrowRate
);
expectedReserveData.liquidityRate = rates[0];
expectedReserveData.stableBorrowRate = rates[1];
expectedReserveData.variableBorrowRate = rates[2];
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
reserveDataBeforeAction,
txTimestamp
);
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
reserveDataBeforeAction,
txTimestamp
);
return expectedReserveData;
};
export const calcExpectedUserDataAfterSwapRateMode = (
reserveDataBeforeAction: ReserveData,
expectedDataAfterAction: ReserveData,
userDataBeforeAction: UserReserveData,
txCost: BigNumber,
txTimestamp: BigNumber
): UserReserveData => {
const expectedUserData = {...userDataBeforeAction};
const borrowBalanceBeforeTx = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
reserveDataBeforeAction,
userDataBeforeAction,
txTimestamp
);
expectedUserData.principalATokenBalance =
userDataBeforeAction.principalATokenBalance;
expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance;
expectedUserData.interestRedirectionAddress =
userDataBeforeAction.interestRedirectionAddress;
expectedUserData.redirectionAddressRedirectedBalance =
userDataBeforeAction.redirectionAddressRedirectedBalance;
expectedUserData.currentATokenUserIndex = calcExpectedATokenUserIndex(
reserveDataBeforeAction,
expectedUserData.currentATokenBalance,
expectedUserData.redirectedBalance,
txTimestamp
);
expectedUserData.currentBorrowBalance = expectedUserData.principalBorrowBalance = borrowBalanceBeforeTx;
if (userDataBeforeAction.borrowRateMode === RateMode.Stable) {
expectedUserData.borrowRateMode = RateMode.Variable;
expectedUserData.borrowRate = expectedDataAfterAction.variableBorrowRate;
expectedUserData.variableBorrowIndex =
expectedDataAfterAction.variableBorrowIndex;
} else {
expectedUserData.borrowRateMode = RateMode.Stable;
expectedUserData.borrowRate = reserveDataBeforeAction.stableBorrowRate;
expectedUserData.variableBorrowIndex = new BigNumber(0);
}
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
if (reserveDataBeforeAction.address === configuration.ethereumAddress) {
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(
txCost
);
}
return expectedUserData;
};
export const calcExpectedReserveDataAfterStableRateRebalance = (
reserveDataBeforeAction: ReserveData,
userDataBeforeAction: UserReserveData,
txTimestamp: BigNumber
): ReserveData => {
const expectedReserveData: ReserveData = <ReserveData>{};
expectedReserveData.address = reserveDataBeforeAction.address;
let userBalanceIncrease: BigNumber = new BigNumber(0);
let userCurrentBorrowBalance: BigNumber = new BigNumber(0);
userCurrentBorrowBalance = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
userBalanceIncrease = userCurrentBorrowBalance.minus(
userDataBeforeAction.principalBorrowBalance
);
expectedReserveData.totalLiquidity = reserveDataBeforeAction.totalLiquidity.plus(
userBalanceIncrease
);
expectedReserveData.availableLiquidity =
reserveDataBeforeAction.availableLiquidity;
expectedReserveData.totalBorrowsStable = reserveDataBeforeAction.totalBorrowsStable.plus(
userBalanceIncrease
);
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.totalBorrowsStable,
userBalanceIncrease,
userDataBeforeAction.borrowRate
);
expectedReserveData.totalBorrowsVariable =
reserveDataBeforeAction.totalBorrowsVariable;
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.totalLiquidity
);
const rates = calcExpectedInterestRates(
reserveDataBeforeAction.symbol,
reserveDataBeforeAction.marketStableRate,
expectedReserveData.utilizationRate,
expectedReserveData.totalBorrowsStable,
expectedReserveData.totalBorrowsVariable,
expectedReserveData.averageStableBorrowRate
);
expectedReserveData.liquidityRate = rates[0];
expectedReserveData.stableBorrowRate = rates[1];
expectedReserveData.variableBorrowRate = rates[2];
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
reserveDataBeforeAction,
txTimestamp
);
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
reserveDataBeforeAction,
txTimestamp
);
return expectedReserveData;
};
export const calcExpectedUserDataAfterStableRateRebalance = (
reserveDataBeforeAction: ReserveData,
expectedDataAfterAction: ReserveData,
userDataBeforeAction: UserReserveData,
txCost: BigNumber,
txTimestamp: BigNumber
): UserReserveData => {
const expectedUserData = {...userDataBeforeAction};
const borrowBalanceBeforeTx = calcExpectedCompoundedBorrowBalance(
userDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
expectedUserData.currentBorrowBalance = expectedUserData.principalBorrowBalance = borrowBalanceBeforeTx;
expectedUserData.borrowRateMode = RateMode.Stable;
expectedUserData.borrowRate = reserveDataBeforeAction.stableBorrowRate;
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
if (reserveDataBeforeAction.address === configuration.ethereumAddress) {
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(
txCost
);
}
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
reserveDataBeforeAction,
userDataBeforeAction,
txTimestamp
);
expectedUserData.principalATokenBalance =
userDataBeforeAction.principalATokenBalance;
expectedUserData.redirectedBalance = userDataBeforeAction.redirectedBalance;
expectedUserData.interestRedirectionAddress =
userDataBeforeAction.interestRedirectionAddress;
expectedUserData.redirectionAddressRedirectedBalance =
userDataBeforeAction.redirectionAddressRedirectedBalance;
expectedUserData.currentATokenUserIndex = calcExpectedATokenUserIndex(
reserveDataBeforeAction,
expectedUserData.currentATokenBalance,
expectedUserData.redirectedBalance,
txTimestamp
);
return expectedUserData;
};
export const calcExpectedUsersDataAfterRedirectInterest = (
reserveDataBeforeAction: ReserveData,
fromDataBeforeAction: UserReserveData,
toDataBeforeAction: UserReserveData,
fromAddress: string,
toAddress: string,
isFromExecutingTx: boolean,
txCost: BigNumber,
txTimestamp: BigNumber
): UserReserveData[] => {
const expectedFromData = {...fromDataBeforeAction};
const expectedToData = {...toDataBeforeAction};
expectedFromData.currentBorrowBalance = calcExpectedCompoundedBorrowBalance(
fromDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
expectedToData.currentBorrowBalance = calcExpectedCompoundedBorrowBalance(
toDataBeforeAction,
reserveDataBeforeAction,
txTimestamp
);
expectedFromData.principalATokenBalance = expectedFromData.currentATokenBalance = calcExpectedATokenBalance(
reserveDataBeforeAction,
fromDataBeforeAction,
txTimestamp
);
expectedToData.principalATokenBalance = expectedToData.currentATokenBalance = calcExpectedATokenBalance(
reserveDataBeforeAction,
toDataBeforeAction,
txTimestamp
);
if (isFromExecutingTx) {
if (reserveDataBeforeAction.address === configuration.ethereumAddress) {
expectedFromData.walletBalance = fromDataBeforeAction.walletBalance.minus(
txCost
);
}
}
expectedToData.redirectedBalance = toDataBeforeAction.redirectedBalance.plus(
expectedFromData.currentATokenBalance
);
if (fromAddress === toAddress) {
expectedFromData.interestRedirectionAddress = ZERO_ADDRESS;
expectedFromData.redirectedBalance = new BigNumber(0);
expectedFromData.redirectionAddressRedirectedBalance = new BigNumber(0);
expectedToData.interestRedirectionAddress = ZERO_ADDRESS;
expectedToData.redirectedBalance = new BigNumber(0);
expectedToData.redirectionAddressRedirectedBalance = new BigNumber(0);
} else {
expectedFromData.interestRedirectionAddress = toAddress;
expectedFromData.redirectionAddressRedirectedBalance = calcExpectedRedirectedBalance(
toDataBeforeAction,
expectedFromData,
toDataBeforeAction.redirectedBalance,
expectedFromData.currentATokenBalance,
new BigNumber(0)
);
}
expectedFromData.currentATokenUserIndex = calcExpectedATokenUserIndex(
reserveDataBeforeAction,
expectedFromData.currentATokenBalance,
expectedFromData.redirectedBalance,
txTimestamp
);
expectedToData.currentATokenUserIndex = calcExpectedATokenUserIndex(
reserveDataBeforeAction,
expectedToData.currentATokenBalance,
expectedToData.redirectedBalance,
txTimestamp
);
return [expectedFromData, expectedToData];
};
const calcExpectedATokenUserIndex = (
reserveDataBeforeAction: ReserveData,
expectedUserBalanceAfterAction: BigNumber,
expectedUserRedirectedBalanceAterAction: BigNumber,
currentTimestamp: BigNumber
) => {
if (
expectedUserBalanceAfterAction.eq(0) &&
expectedUserRedirectedBalanceAterAction.eq(0)
) {
return new BigNumber(0);
}
return calcExpectedReserveNormalizedIncome(
reserveDataBeforeAction,
currentTimestamp
);
};
const calcExpectedATokenBalance = (
reserveDataBeforeAction: ReserveData,
userDataBeforeAction: UserReserveData,
currentTimestamp: BigNumber
) => {
const income = calcExpectedReserveNormalizedIncome(
reserveDataBeforeAction,
currentTimestamp
);
const {
interestRedirectionAddress,
currentATokenUserIndex: userIndexBeforeAction,
redirectedBalance,
principalATokenBalance: principalBalanceBeforeAction,
} = userDataBeforeAction;
if (userIndexBeforeAction.eq(0)) {
return principalBalanceBeforeAction;
}
if (interestRedirectionAddress === ZERO_ADDRESS) {
return principalBalanceBeforeAction
.plus(redirectedBalance)
.wadToRay()
.rayMul(income)
.rayDiv(userIndexBeforeAction)
.rayToWad()
.minus(redirectedBalance);
} else {
return principalBalanceBeforeAction.plus(
redirectedBalance
.wadToRay()
.rayMul(income)
.rayDiv(userIndexBeforeAction)
.rayToWad()
.minus(redirectedBalance)
);
}
};
const calcExpectedRedirectedBalance = (
userDataBeforeAction: UserReserveData,
expectedUserDataAfterAction: UserReserveData,
redirectedBalanceBefore: BigNumber,
amountToAdd: BigNumber,
amountToSubstract: BigNumber
): BigNumber => {
const balanceIncrease = userDataBeforeAction.currentATokenBalance.minus(
userDataBeforeAction.principalATokenBalance
);
return expectedUserDataAfterAction.interestRedirectionAddress !== ZERO_ADDRESS
? redirectedBalanceBefore
.plus(balanceIncrease)
.plus(amountToAdd)
.minus(amountToSubstract)
: new BigNumber("0");
};
const calcExpectedAverageStableBorrowRate = (
avgStableRateBefore: BigNumber,
totalBorrowsStableBefore: BigNumber,
amountChanged: string | BigNumber,
rate: BigNumber
) => {
const weightedTotalBorrows = avgStableRateBefore.multipliedBy(
totalBorrowsStableBefore
);
const weightedAmountBorrowed = rate.multipliedBy(amountChanged);
const totalBorrowedStable = totalBorrowsStableBefore.plus(
new BigNumber(amountChanged)
);
if (totalBorrowedStable.eq(0)) return new BigNumber("0");
return weightedTotalBorrows
.plus(weightedAmountBorrowed)
.div(totalBorrowedStable)
.decimalPlaces(0, BigNumber.ROUND_DOWN);
};
const calcExpectedCompoundedBorrowBalance = (
userData: UserReserveData,
reserveData: ReserveData,
timestamp: BigNumber
): BigNumber => {
if (userData.principalBorrowBalance.eq(0)) {
return strToBN("0");
}
const cumulatedInterest = calcCompoundedInterest(
userData.borrowRate,
timestamp,
userData.lastUpdateTimestamp
);
const borrowBalanceRay = userData.principalBorrowBalance.wadToRay();
if (userData.borrowRateMode === RateMode.Stable) {
return borrowBalanceRay.rayMul(cumulatedInterest).rayToWad();
}
// variable
const cumulatedInterestVariable = cumulatedInterest
.rayMul(reserveData.variableBorrowIndex)
.rayDiv(userData.variableBorrowIndex);
return borrowBalanceRay.rayMul(cumulatedInterestVariable).rayToWad();
};
const calcLinearInterest = (
rate: BigNumber,
currentTimestamp: BigNumber,
lastUpdateTimestamp: BigNumber
) => {
const timeDifference = currentTimestamp.minus(lastUpdateTimestamp).wadToRay();
const timeDelta = timeDifference.rayDiv(new BigNumber(ONE_YEAR).wadToRay());
const cumulatedInterest = rate.rayMul(timeDelta).plus(RAY);
return cumulatedInterest;
};
const calcCompoundedInterest = (
rate: BigNumber,
currentTimestamp: BigNumber,
lastUpdateTimestamp: BigNumber
) => {
const timeDifference = currentTimestamp.minus(lastUpdateTimestamp);
const ratePerSecond = rate.div(ONE_YEAR);
const compoundedInterest = ratePerSecond.plus(RAY).rayPow(timeDifference);
return compoundedInterest;
};
const calcExpectedInterestRates = (
reserveSymbol: string,
marketStableRate: BigNumber,
utilizationRate: BigNumber,
totalBorrowsStable: BigNumber,
totalBorrowsVariable: BigNumber,
averageStableBorrowRate: BigNumber
): BigNumber[] => {
const {reservesParams} = configuration;
const reserveIndex = Object.keys(reservesParams).findIndex(
(value) => value === reserveSymbol
);
const [, reserveConfiguration] = (Object.entries(reservesParams) as [
string,
IReserveParams
][])[reserveIndex];
let stableBorrowRate: BigNumber = marketStableRate;
let variableBorrowRate: BigNumber = new BigNumber(
reserveConfiguration.baseVariableBorrowRate
);
if (utilizationRate.gt(OPTIMAL_UTILIZATION_RATE)) {
const excessUtilizationRateRatio = utilizationRate
.minus(OPTIMAL_UTILIZATION_RATE)
.rayDiv(EXCESS_UTILIZATION_RATE);
stableBorrowRate = stableBorrowRate
.plus(reserveConfiguration.stableRateSlope1)
.plus(
new BigNumber(reserveConfiguration.stableRateSlope2).rayMul(
excessUtilizationRateRatio
)
);
variableBorrowRate = variableBorrowRate
.plus(reserveConfiguration.variableRateSlope1)
.plus(
new BigNumber(reserveConfiguration.variableRateSlope2).rayMul(
excessUtilizationRateRatio
)
);
} else {
stableBorrowRate = stableBorrowRate.plus(
new BigNumber(reserveConfiguration.stableRateSlope1).rayMul(
utilizationRate.rayDiv(new BigNumber(OPTIMAL_UTILIZATION_RATE))
)
);
variableBorrowRate = variableBorrowRate.plus(
utilizationRate
.rayDiv(OPTIMAL_UTILIZATION_RATE)
.rayMul(new BigNumber(reserveConfiguration.variableRateSlope1))
);
}
const expectedOverallRate = calcExpectedOverallBorrowRate(
totalBorrowsStable,
totalBorrowsVariable,
variableBorrowRate,
averageStableBorrowRate
);
const liquidityRate = expectedOverallRate.rayMul(utilizationRate);
return [liquidityRate, stableBorrowRate, variableBorrowRate];
};
const calcExpectedOverallBorrowRate = (
totalBorrowsStable: BigNumber,
totalBorrowsVariable: BigNumber,
currentVariableBorrowRate: BigNumber,
currentAverageStableBorrowRate: BigNumber
): BigNumber => {
const totalBorrows = totalBorrowsStable.plus(totalBorrowsVariable);
if (totalBorrows.eq(0)) return strToBN("0");
const weightedVariableRate = totalBorrowsVariable
.wadToRay()
.rayMul(currentVariableBorrowRate);
const weightedStableRate = totalBorrowsStable
.wadToRay()
.rayMul(currentAverageStableBorrowRate);
const overallBorrowRate = weightedVariableRate
.plus(weightedStableRate)
.rayDiv(totalBorrows.wadToRay());
return overallBorrowRate;
};
const calcExpectedUtilizationRate = (
totalBorrowsStable: BigNumber,
totalBorrowsVariable: BigNumber,
totalLiquidity: BigNumber
): BigNumber => {
if (totalBorrowsStable.eq("0") && totalBorrowsVariable.eq("0")) {
return strToBN("0");
}
const utilization = totalBorrowsStable
.plus(totalBorrowsVariable)
.rayDiv(totalLiquidity);
return utilization;
};
const calcExpectedReserveNormalizedIncome = (
reserveData: ReserveData,
currentTimestamp: BigNumber
) => {
const {liquidityRate, liquidityIndex, lastUpdateTimestamp} = reserveData;
//if utilization rate is 0, nothing to compound
if (liquidityRate.eq("0")) {
return liquidityIndex;
}
const cumulatedInterest = calcLinearInterest(
liquidityRate,
currentTimestamp,
lastUpdateTimestamp
);
const income = cumulatedInterest.rayMul(liquidityIndex);
return income;
};
const calcExpectedLiquidityIndex = (
reserveData: ReserveData,
timestamp: BigNumber
) => {
//if utilization rate is 0, nothing to compound
if (reserveData.utilizationRate.eq("0")) {
return reserveData.liquidityIndex;
}
const cumulatedInterest = calcLinearInterest(
reserveData.liquidityRate,
timestamp,
reserveData.lastUpdateTimestamp
);
return cumulatedInterest.rayMul(reserveData.liquidityIndex);
};
const calcExpectedVariableBorrowIndex = (
reserveData: ReserveData,
timestamp: BigNumber
) => {
//if utilization rate is 0, nothing to compound
if (reserveData.utilizationRate.eq("0")) {
return reserveData.variableBorrowIndex;
}
const cumulatedInterest = calcCompoundedInterest(
reserveData.variableBorrowRate,
timestamp,
reserveData.lastUpdateTimestamp
);
return cumulatedInterest.rayMul(reserveData.variableBorrowIndex);
};
const calcExpectedOriginationFee = (amount: string): BigNumber => {
return new BigNumber(amount)
.multipliedBy(0.0025)
.decimalPlaces(0, BigNumber.ROUND_DOWN);
};
export const calculateHealthFactorFromBalances = (
collateralBalanceETH: BigNumber,
borrowBalanceETH: BigNumber,
currentLiquidationThreshold: BigNumber
): BigNumber => {
if (borrowBalanceETH.eq(0)) {
return strToBN("-1"); // invalid number
}
return collateralBalanceETH
.multipliedBy(currentLiquidationThreshold)
.div(borrowBalanceETH);
};
const calculateAvailableBorrowsETH = (
collateralBalanceETH: BigNumber,
borrowBalanceETH: BigNumber,
currentLtv: BigNumber
): BigNumber => {
if (currentLtv.eq(0)) {
return strToBN("0");
}
let availableBorrowsETH = collateralBalanceETH.multipliedBy(currentLtv);
if (availableBorrowsETH.lt(borrowBalanceETH)) {
return strToBN("0");
}
availableBorrowsETH = availableBorrowsETH.minus(borrowBalanceETH);
const borrowFee = availableBorrowsETH.multipliedBy(0.0025);
return availableBorrowsETH.minus(borrowFee);
};