mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
1436 lines
46 KiB
TypeScript
1436 lines
46 KiB
TypeScript
import BigNumber from 'bignumber.js';
|
|
import { ONE_YEAR, RAY, MAX_UINT_AMOUNT, PERCENTAGE_FACTOR } from '../../../../helpers/constants';
|
|
import {
|
|
IReserveParams,
|
|
iAavePoolAssets,
|
|
RateMode,
|
|
tEthereumAddress,
|
|
} from '../../../../helpers/types';
|
|
import './math';
|
|
import { ReserveData, UserReserveData } from './interfaces';
|
|
import { expect } from 'chai';
|
|
|
|
export const strToBN = (amount: string): BigNumber => new BigNumber(amount);
|
|
|
|
interface Configuration {
|
|
reservesParams: iAavePoolAssets<IReserveParams>;
|
|
}
|
|
|
|
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.currentStableDebt = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedUserData.currentVariableDebt = calcExpectedVariableDebtTokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt;
|
|
expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt;
|
|
expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex;
|
|
expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate;
|
|
expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated;
|
|
|
|
expectedUserData.liquidityRate = reserveDataAfterAction.liquidityRate;
|
|
|
|
expectedUserData.scaledATokenBalance = calcExpectedScaledATokenBalance(
|
|
userDataBeforeAction,
|
|
reserveDataAfterAction.liquidityIndex,
|
|
new BigNumber(amountDeposited),
|
|
new BigNumber(0)
|
|
);
|
|
expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
).plus(amountDeposited);
|
|
|
|
if (userDataBeforeAction.currentATokenBalance.eq(0)) {
|
|
expectedUserData.usageAsCollateralEnabled = true;
|
|
} else {
|
|
expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled;
|
|
}
|
|
|
|
expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex;
|
|
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(amountDeposited);
|
|
|
|
expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedUserData.currentVariableDebt = expectedUserData.principalStableDebt = calcExpectedVariableDebtTokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
return expectedUserData;
|
|
};
|
|
|
|
export const calcExpectedUserDataAfterWithdraw = (
|
|
amountWithdrawn: string,
|
|
reserveDataBeforeAction: ReserveData,
|
|
reserveDataAfterAction: ReserveData,
|
|
userDataBeforeAction: UserReserveData,
|
|
txTimestamp: BigNumber,
|
|
currentTimestamp: BigNumber,
|
|
txCost: BigNumber
|
|
): UserReserveData => {
|
|
const expectedUserData = <UserReserveData>{};
|
|
|
|
const aTokenBalance = calcExpectedATokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
if (amountWithdrawn == MAX_UINT_AMOUNT) {
|
|
amountWithdrawn = aTokenBalance.toFixed(0);
|
|
}
|
|
|
|
expectedUserData.scaledATokenBalance = calcExpectedScaledATokenBalance(
|
|
userDataBeforeAction,
|
|
reserveDataAfterAction.liquidityIndex,
|
|
new BigNumber(0),
|
|
new BigNumber(amountWithdrawn)
|
|
);
|
|
|
|
expectedUserData.currentATokenBalance = aTokenBalance.minus(amountWithdrawn);
|
|
|
|
expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt;
|
|
expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt;
|
|
|
|
expectedUserData.currentStableDebt = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedUserData.currentVariableDebt = calcExpectedVariableDebtTokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex;
|
|
expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate;
|
|
expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated;
|
|
|
|
expectedUserData.liquidityRate = reserveDataAfterAction.liquidityRate;
|
|
|
|
if (userDataBeforeAction.currentATokenBalance.eq(0)) {
|
|
expectedUserData.usageAsCollateralEnabled = true;
|
|
} else {
|
|
//if the user is withdrawing everything, usageAsCollateralEnabled must be false
|
|
if (expectedUserData.currentATokenBalance.eq(0)) {
|
|
expectedUserData.usageAsCollateralEnabled = false;
|
|
} else {
|
|
expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled;
|
|
}
|
|
}
|
|
|
|
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(amountWithdrawn);
|
|
|
|
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.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate;
|
|
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt(
|
|
reserveDataBeforeAction.principalStableDebt,
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
reserveDataBeforeAction.totalStableDebtLastUpdated,
|
|
txTimestamp
|
|
);
|
|
expectedReserveData.totalVariableDebt = calcExpectedTotalVariableDebt(
|
|
reserveDataBeforeAction,
|
|
expectedReserveData.variableBorrowIndex
|
|
);
|
|
|
|
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt;
|
|
expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt;
|
|
|
|
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.totalLiquidity
|
|
);
|
|
const rates = calcExpectedInterestRates(
|
|
reserveDataBeforeAction.symbol,
|
|
reserveDataBeforeAction.marketStableRate,
|
|
expectedReserveData.utilizationRate,
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.averageStableBorrowRate
|
|
);
|
|
expectedReserveData.liquidityRate = rates[0];
|
|
expectedReserveData.stableBorrowRate = rates[1];
|
|
expectedReserveData.variableBorrowRate = rates[2];
|
|
|
|
return expectedReserveData;
|
|
};
|
|
|
|
export const calcExpectedReserveDataAfterWithdraw = (
|
|
amountWithdrawn: string,
|
|
reserveDataBeforeAction: ReserveData,
|
|
userDataBeforeAction: UserReserveData,
|
|
txTimestamp: BigNumber
|
|
): ReserveData => {
|
|
const expectedReserveData: ReserveData = <ReserveData>{};
|
|
|
|
expectedReserveData.address = reserveDataBeforeAction.address;
|
|
|
|
if (amountWithdrawn == MAX_UINT_AMOUNT) {
|
|
amountWithdrawn = calcExpectedATokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
).toFixed();
|
|
}
|
|
|
|
expectedReserveData.availableLiquidity = new BigNumber(
|
|
reserveDataBeforeAction.availableLiquidity
|
|
).minus(amountWithdrawn);
|
|
|
|
expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt;
|
|
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt;
|
|
|
|
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt(
|
|
reserveDataBeforeAction.principalStableDebt,
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
reserveDataBeforeAction.totalStableDebtLastUpdated,
|
|
txTimestamp
|
|
);
|
|
expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul(
|
|
expectedReserveData.variableBorrowIndex
|
|
);
|
|
|
|
expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate;
|
|
|
|
expectedReserveData.totalLiquidity = new BigNumber(reserveDataBeforeAction.availableLiquidity)
|
|
.minus(amountWithdrawn)
|
|
.plus(expectedReserveData.totalVariableDebt)
|
|
.plus(expectedReserveData.totalStableDebt);
|
|
|
|
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.totalLiquidity
|
|
);
|
|
const rates = calcExpectedInterestRates(
|
|
reserveDataBeforeAction.symbol,
|
|
reserveDataBeforeAction.marketStableRate,
|
|
expectedReserveData.utilizationRate,
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.averageStableBorrowRate
|
|
);
|
|
expectedReserveData.liquidityRate = rates[0];
|
|
expectedReserveData.stableBorrowRate = rates[1];
|
|
expectedReserveData.variableBorrowRate = rates[2];
|
|
|
|
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;
|
|
|
|
const amountBorrowedBN = new BigNumber(amountBorrowed);
|
|
|
|
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity.minus(
|
|
amountBorrowedBN
|
|
);
|
|
|
|
expectedReserveData.lastUpdateTimestamp = txTimestamp;
|
|
|
|
if (borrowRateMode == RateMode.Stable) {
|
|
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt;
|
|
|
|
const expectedVariableDebtAfterTx = expectedReserveData.scaledVariableDebt.rayMul(
|
|
expectedReserveData.variableBorrowIndex
|
|
);
|
|
|
|
const expectedStableDebtUntilTx = calcExpectedTotalStableDebt(
|
|
reserveDataBeforeAction.principalStableDebt,
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
reserveDataBeforeAction.totalStableDebtLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.principalStableDebt = expectedStableDebtUntilTx.plus(amountBorrowedBN);
|
|
|
|
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
expectedStableDebtUntilTx,
|
|
amountBorrowedBN,
|
|
reserveDataBeforeAction.stableBorrowRate
|
|
);
|
|
|
|
const totalLiquidityAfterTx = expectedReserveData.availableLiquidity
|
|
.plus(expectedReserveData.principalStableDebt)
|
|
.plus(expectedVariableDebtAfterTx);
|
|
|
|
const utilizationRateAfterTx = calcExpectedUtilizationRate(
|
|
expectedReserveData.principalStableDebt, //the expected principal debt is the total debt immediately after the tx
|
|
expectedVariableDebtAfterTx,
|
|
totalLiquidityAfterTx
|
|
);
|
|
|
|
const ratesAfterTx = calcExpectedInterestRates(
|
|
reserveDataBeforeAction.symbol,
|
|
reserveDataBeforeAction.marketStableRate,
|
|
utilizationRateAfterTx,
|
|
expectedReserveData.principalStableDebt,
|
|
expectedVariableDebtAfterTx,
|
|
expectedReserveData.averageStableBorrowRate
|
|
);
|
|
|
|
expectedReserveData.liquidityRate = ratesAfterTx[0];
|
|
|
|
expectedReserveData.stableBorrowRate = ratesAfterTx[1];
|
|
|
|
expectedReserveData.variableBorrowRate = ratesAfterTx[2];
|
|
|
|
expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt(
|
|
expectedReserveData.principalStableDebt,
|
|
expectedReserveData.averageStableBorrowRate,
|
|
txTimestamp,
|
|
currentTimestamp
|
|
);
|
|
|
|
expectedReserveData.totalVariableDebt = reserveDataBeforeAction.scaledVariableDebt.rayMul(
|
|
calcExpectedReserveNormalizedDebt(
|
|
expectedReserveData.variableBorrowRate,
|
|
expectedReserveData.variableBorrowIndex,
|
|
txTimestamp,
|
|
currentTimestamp
|
|
)
|
|
);
|
|
|
|
expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity
|
|
.plus(expectedReserveData.totalVariableDebt)
|
|
.plus(expectedReserveData.totalStableDebt);
|
|
|
|
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.totalLiquidity
|
|
);
|
|
} else {
|
|
expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt;
|
|
|
|
const totalStableDebtAfterTx = calcExpectedStableDebtTokenBalance(
|
|
reserveDataBeforeAction.principalStableDebt,
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
reserveDataBeforeAction.totalStableDebtLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt(
|
|
reserveDataBeforeAction.principalStableDebt,
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
reserveDataBeforeAction.totalStableDebtLastUpdated,
|
|
currentTimestamp
|
|
);
|
|
|
|
expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate;
|
|
|
|
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.plus(
|
|
amountBorrowedBN.rayDiv(expectedReserveData.variableBorrowIndex)
|
|
);
|
|
|
|
const totalVariableDebtAfterTx = expectedReserveData.scaledVariableDebt.rayMul(
|
|
expectedReserveData.variableBorrowIndex
|
|
);
|
|
|
|
const utilizationRateAfterTx = calcExpectedUtilizationRate(
|
|
totalStableDebtAfterTx,
|
|
totalVariableDebtAfterTx,
|
|
expectedReserveData.availableLiquidity
|
|
.plus(totalStableDebtAfterTx)
|
|
.plus(totalVariableDebtAfterTx)
|
|
);
|
|
|
|
const rates = calcExpectedInterestRates(
|
|
reserveDataBeforeAction.symbol,
|
|
reserveDataBeforeAction.marketStableRate,
|
|
utilizationRateAfterTx,
|
|
totalStableDebtAfterTx,
|
|
totalVariableDebtAfterTx,
|
|
expectedReserveData.averageStableBorrowRate
|
|
);
|
|
|
|
expectedReserveData.liquidityRate = rates[0];
|
|
|
|
expectedReserveData.stableBorrowRate = rates[1];
|
|
|
|
expectedReserveData.variableBorrowRate = rates[2];
|
|
|
|
expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul(
|
|
calcExpectedReserveNormalizedDebt(
|
|
expectedReserveData.variableBorrowRate,
|
|
expectedReserveData.variableBorrowIndex,
|
|
txTimestamp,
|
|
currentTimestamp
|
|
)
|
|
);
|
|
|
|
expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity
|
|
.plus(expectedReserveData.totalStableDebt)
|
|
.plus(expectedReserveData.totalVariableDebt);
|
|
|
|
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.totalLiquidity
|
|
);
|
|
}
|
|
|
|
return expectedReserveData;
|
|
};
|
|
|
|
export const calcExpectedReserveDataAfterRepay = (
|
|
amountRepaid: string,
|
|
borrowRateMode: RateMode,
|
|
reserveDataBeforeAction: ReserveData,
|
|
userDataBeforeAction: UserReserveData,
|
|
txTimestamp: BigNumber,
|
|
currentTimestamp: BigNumber
|
|
): ReserveData => {
|
|
const expectedReserveData: ReserveData = <ReserveData>{};
|
|
|
|
expectedReserveData.address = reserveDataBeforeAction.address;
|
|
|
|
let amountRepaidBN = new BigNumber(amountRepaid);
|
|
|
|
const userStableDebt = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
const userVariableDebt = calcExpectedVariableDebtTokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
//if amount repaid == MAX_UINT_AMOUNT, user is repaying everything
|
|
if (amountRepaidBN.abs().eq(MAX_UINT_AMOUNT)) {
|
|
if (borrowRateMode == RateMode.Stable) {
|
|
amountRepaidBN = userStableDebt;
|
|
} else {
|
|
amountRepaidBN = userVariableDebt;
|
|
}
|
|
}
|
|
|
|
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
if (borrowRateMode == RateMode.Stable) {
|
|
const expectedDebt = calcExpectedTotalStableDebt(
|
|
reserveDataBeforeAction.principalStableDebt,
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
reserveDataBeforeAction.totalStableDebtLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = expectedDebt.minus(
|
|
amountRepaidBN
|
|
);
|
|
|
|
//due to accumulation errors, the total stable debt might be smaller than the last user debt.
|
|
//in this case we simply set the total supply and avg stable rate to 0.
|
|
if (expectedReserveData.totalStableDebt.lt(0)) {
|
|
expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = expectedReserveData.averageStableBorrowRate = new BigNumber(
|
|
0
|
|
);
|
|
} else {
|
|
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
expectedDebt,
|
|
amountRepaidBN.negated(),
|
|
userDataBeforeAction.stableBorrowRate
|
|
);
|
|
|
|
//also due to accumulation errors, the final avg stable rate when the last user repays might be negative.
|
|
//if that is the case, it means a small leftover of total stable debt is left, which can be erased.
|
|
|
|
if (expectedReserveData.averageStableBorrowRate.lt(0)) {
|
|
expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = expectedReserveData.averageStableBorrowRate = new BigNumber(
|
|
0
|
|
);
|
|
}
|
|
}
|
|
|
|
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt;
|
|
|
|
expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul(
|
|
expectedReserveData.variableBorrowIndex
|
|
);
|
|
} else {
|
|
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.minus(
|
|
amountRepaidBN.rayDiv(expectedReserveData.variableBorrowIndex)
|
|
);
|
|
|
|
expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul(
|
|
expectedReserveData.variableBorrowIndex
|
|
);
|
|
|
|
expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt;
|
|
expectedReserveData.totalStableDebt = reserveDataBeforeAction.totalStableDebt;
|
|
|
|
expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate;
|
|
}
|
|
|
|
expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity.plus(
|
|
amountRepaidBN
|
|
);
|
|
|
|
expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity
|
|
.plus(expectedReserveData.totalStableDebt)
|
|
.plus(expectedReserveData.totalVariableDebt);
|
|
|
|
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.totalLiquidity
|
|
);
|
|
|
|
const rates = calcExpectedInterestRates(
|
|
reserveDataBeforeAction.symbol,
|
|
reserveDataBeforeAction.marketStableRate,
|
|
expectedReserveData.utilizationRate,
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.averageStableBorrowRate
|
|
);
|
|
expectedReserveData.liquidityRate = rates[0];
|
|
|
|
expectedReserveData.stableBorrowRate = rates[1];
|
|
|
|
expectedReserveData.variableBorrowRate = rates[2];
|
|
|
|
expectedReserveData.lastUpdateTimestamp = txTimestamp;
|
|
|
|
return expectedReserveData;
|
|
};
|
|
|
|
export const calcExpectedUserDataAfterBorrow = (
|
|
amountBorrowed: string,
|
|
interestRateMode: string,
|
|
reserveDataBeforeAction: ReserveData,
|
|
expectedDataAfterAction: ReserveData,
|
|
userDataBeforeAction: UserReserveData,
|
|
txTimestamp: BigNumber,
|
|
currentTimestamp: BigNumber
|
|
): UserReserveData => {
|
|
const expectedUserData = <UserReserveData>{};
|
|
|
|
const amountBorrowedBN = new BigNumber(amountBorrowed);
|
|
|
|
if (interestRateMode == RateMode.Stable) {
|
|
const stableDebtUntilTx = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedUserData.principalStableDebt = stableDebtUntilTx.plus(amountBorrowed);
|
|
expectedUserData.stableRateLastUpdated = txTimestamp;
|
|
|
|
expectedUserData.stableBorrowRate = calcExpectedUserStableRate(
|
|
stableDebtUntilTx,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
amountBorrowedBN,
|
|
reserveDataBeforeAction.stableBorrowRate
|
|
);
|
|
|
|
expectedUserData.currentStableDebt = calcExpectedStableDebtTokenBalance(
|
|
expectedUserData.principalStableDebt,
|
|
expectedUserData.stableBorrowRate,
|
|
txTimestamp,
|
|
currentTimestamp
|
|
);
|
|
|
|
expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt;
|
|
} else {
|
|
expectedUserData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.plus(
|
|
amountBorrowedBN.rayDiv(expectedDataAfterAction.variableBorrowIndex)
|
|
);
|
|
|
|
expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt;
|
|
|
|
expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate;
|
|
|
|
expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated;
|
|
|
|
expectedUserData.currentStableDebt = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
currentTimestamp
|
|
);
|
|
}
|
|
|
|
expectedUserData.currentVariableDebt = calcExpectedVariableDebtTokenBalance(
|
|
expectedDataAfterAction,
|
|
expectedUserData,
|
|
currentTimestamp
|
|
);
|
|
|
|
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
|
|
|
|
expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled;
|
|
|
|
expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
|
|
expectedDataAfterAction,
|
|
userDataBeforeAction,
|
|
currentTimestamp
|
|
);
|
|
|
|
expectedUserData.scaledATokenBalance = userDataBeforeAction.scaledATokenBalance;
|
|
|
|
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.plus(amountBorrowed);
|
|
|
|
return expectedUserData;
|
|
};
|
|
|
|
export const calcExpectedUserDataAfterRepay = (
|
|
totalRepaid: string,
|
|
rateMode: RateMode,
|
|
reserveDataBeforeAction: ReserveData,
|
|
expectedDataAfterAction: ReserveData,
|
|
userDataBeforeAction: UserReserveData,
|
|
user: string,
|
|
onBehalfOf: string,
|
|
txTimestamp: BigNumber,
|
|
currentTimestamp: BigNumber
|
|
): UserReserveData => {
|
|
const expectedUserData = <UserReserveData>{};
|
|
|
|
const variableDebt = calcExpectedVariableDebtTokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
currentTimestamp
|
|
);
|
|
|
|
const stableDebt = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
currentTimestamp
|
|
);
|
|
|
|
let totalRepaidBN = new BigNumber(totalRepaid);
|
|
if (totalRepaidBN.abs().eq(MAX_UINT_AMOUNT)) {
|
|
totalRepaidBN = rateMode == RateMode.Stable ? stableDebt : variableDebt;
|
|
}
|
|
|
|
if (rateMode == RateMode.Stable) {
|
|
expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt;
|
|
expectedUserData.currentVariableDebt = variableDebt;
|
|
|
|
expectedUserData.principalStableDebt = expectedUserData.currentStableDebt = stableDebt.minus(
|
|
totalRepaidBN
|
|
);
|
|
|
|
if (expectedUserData.currentStableDebt.eq('0')) {
|
|
//user repaid everything
|
|
expectedUserData.stableBorrowRate = expectedUserData.stableRateLastUpdated = new BigNumber(
|
|
'0'
|
|
);
|
|
} else {
|
|
expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate;
|
|
expectedUserData.stableRateLastUpdated = txTimestamp;
|
|
}
|
|
} else {
|
|
expectedUserData.currentStableDebt = userDataBeforeAction.principalStableDebt;
|
|
expectedUserData.principalStableDebt = stableDebt;
|
|
expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate;
|
|
expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated;
|
|
|
|
expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt.minus(
|
|
totalRepaidBN.rayDiv(expectedDataAfterAction.variableBorrowIndex)
|
|
);
|
|
expectedUserData.currentVariableDebt = expectedUserData.scaledVariableDebt.rayMul(
|
|
expectedDataAfterAction.variableBorrowIndex
|
|
);
|
|
}
|
|
|
|
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
|
|
|
|
expectedUserData.usageAsCollateralEnabled = userDataBeforeAction.usageAsCollateralEnabled;
|
|
|
|
expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
expectedUserData.scaledATokenBalance = userDataBeforeAction.scaledATokenBalance;
|
|
|
|
if (user === onBehalfOf) {
|
|
expectedUserData.walletBalance = userDataBeforeAction.walletBalance.minus(totalRepaidBN);
|
|
} 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;
|
|
|
|
return expectedUserData;
|
|
};
|
|
|
|
export const calcExpectedReserveDataAfterSwapRateMode = (
|
|
reserveDataBeforeAction: ReserveData,
|
|
userDataBeforeAction: UserReserveData,
|
|
rateMode: string,
|
|
txTimestamp: BigNumber
|
|
): ReserveData => {
|
|
const expectedReserveData: ReserveData = <ReserveData>{};
|
|
|
|
expectedReserveData.address = reserveDataBeforeAction.address;
|
|
|
|
const variableDebt = calcExpectedVariableDebtTokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
const stableDebt = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity;
|
|
|
|
const totalStableDebtUntilTx = calcExpectedTotalStableDebt(
|
|
reserveDataBeforeAction.principalStableDebt,
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
reserveDataBeforeAction.totalStableDebtLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
if (rateMode === RateMode.Stable) {
|
|
//swap user stable debt to variable
|
|
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.plus(
|
|
stableDebt.rayDiv(expectedReserveData.variableBorrowIndex)
|
|
);
|
|
|
|
expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul(
|
|
expectedReserveData.variableBorrowIndex
|
|
);
|
|
|
|
expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = totalStableDebtUntilTx.minus(
|
|
stableDebt
|
|
);
|
|
|
|
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
expectedReserveData.principalStableDebt.plus(stableDebt),
|
|
stableDebt.negated(),
|
|
userDataBeforeAction.stableBorrowRate
|
|
);
|
|
} else {
|
|
//swap variable to stable
|
|
|
|
expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = totalStableDebtUntilTx.plus(
|
|
variableDebt
|
|
);
|
|
|
|
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt.minus(
|
|
variableDebt.rayDiv(expectedReserveData.variableBorrowIndex)
|
|
);
|
|
|
|
expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul(
|
|
expectedReserveData.variableBorrowIndex
|
|
);
|
|
|
|
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
reserveDataBeforeAction.totalStableDebt,
|
|
variableDebt,
|
|
reserveDataBeforeAction.stableBorrowRate
|
|
);
|
|
}
|
|
|
|
expectedReserveData.totalLiquidity = reserveDataBeforeAction.availableLiquidity
|
|
.plus(expectedReserveData.totalStableDebt)
|
|
.plus(expectedReserveData.totalVariableDebt);
|
|
|
|
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.totalLiquidity
|
|
);
|
|
|
|
const rates = calcExpectedInterestRates(
|
|
reserveDataBeforeAction.symbol,
|
|
reserveDataBeforeAction.marketStableRate,
|
|
expectedReserveData.utilizationRate,
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.averageStableBorrowRate
|
|
);
|
|
expectedReserveData.liquidityRate = rates[0];
|
|
|
|
expectedReserveData.stableBorrowRate = rates[1];
|
|
|
|
expectedReserveData.variableBorrowRate = rates[2];
|
|
|
|
return expectedReserveData;
|
|
};
|
|
|
|
export const calcExpectedUserDataAfterSwapRateMode = (
|
|
reserveDataBeforeAction: ReserveData,
|
|
expectedDataAfterAction: ReserveData,
|
|
userDataBeforeAction: UserReserveData,
|
|
rateMode: string,
|
|
txCost: BigNumber,
|
|
txTimestamp: BigNumber
|
|
): UserReserveData => {
|
|
const expectedUserData = { ...userDataBeforeAction };
|
|
|
|
const stableDebtBalance = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
const variableDebtBalance = calcExpectedVariableDebtTokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
if (rateMode === RateMode.Stable) {
|
|
// swap to variable
|
|
expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = new BigNumber(0);
|
|
|
|
expectedUserData.stableBorrowRate = new BigNumber(0);
|
|
|
|
expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt.plus(
|
|
stableDebtBalance.rayDiv(expectedDataAfterAction.variableBorrowIndex)
|
|
);
|
|
expectedUserData.currentVariableDebt = expectedUserData.scaledVariableDebt.rayMul(
|
|
expectedDataAfterAction.variableBorrowIndex
|
|
);
|
|
|
|
expectedUserData.stableRateLastUpdated = new BigNumber(0);
|
|
} else {
|
|
expectedUserData.principalStableDebt = expectedUserData.currentStableDebt = userDataBeforeAction.currentStableDebt.plus(
|
|
variableDebtBalance
|
|
);
|
|
|
|
//weighted average of the previous and the current
|
|
expectedUserData.stableBorrowRate = calcExpectedUserStableRate(
|
|
stableDebtBalance,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
variableDebtBalance,
|
|
reserveDataBeforeAction.stableBorrowRate
|
|
);
|
|
|
|
expectedUserData.stableRateLastUpdated = txTimestamp;
|
|
|
|
expectedUserData.currentVariableDebt = expectedUserData.scaledVariableDebt = new BigNumber(0);
|
|
}
|
|
|
|
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
|
|
|
|
return expectedUserData;
|
|
};
|
|
|
|
export const calcExpectedReserveDataAfterStableRateRebalance = (
|
|
reserveDataBeforeAction: ReserveData,
|
|
userDataBeforeAction: UserReserveData,
|
|
txTimestamp: BigNumber
|
|
): ReserveData => {
|
|
const expectedReserveData: ReserveData = <ReserveData>{};
|
|
|
|
expectedReserveData.address = reserveDataBeforeAction.address;
|
|
|
|
const userStableDebt = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex(
|
|
reserveDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt;
|
|
expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul(
|
|
expectedReserveData.variableBorrowIndex
|
|
);
|
|
|
|
expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt(
|
|
reserveDataBeforeAction.principalStableDebt,
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
reserveDataBeforeAction.totalStableDebtLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedReserveData.availableLiquidity = reserveDataBeforeAction.availableLiquidity;
|
|
|
|
expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity
|
|
.plus(expectedReserveData.totalStableDebt)
|
|
.plus(expectedReserveData.totalVariableDebt);
|
|
|
|
//removing the stable liquidity at the old rate
|
|
|
|
const avgRateBefore = calcExpectedAverageStableBorrowRateRebalance(
|
|
reserveDataBeforeAction.averageStableBorrowRate,
|
|
expectedReserveData.totalStableDebt,
|
|
userStableDebt.negated(),
|
|
userDataBeforeAction.stableBorrowRate
|
|
);
|
|
// adding it again at the new rate
|
|
|
|
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRateRebalance(
|
|
avgRateBefore,
|
|
expectedReserveData.totalStableDebt.minus(userStableDebt),
|
|
userStableDebt,
|
|
reserveDataBeforeAction.stableBorrowRate
|
|
);
|
|
|
|
expectedReserveData.utilizationRate = calcExpectedUtilizationRate(
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.totalLiquidity
|
|
);
|
|
|
|
const rates = calcExpectedInterestRates(
|
|
reserveDataBeforeAction.symbol,
|
|
reserveDataBeforeAction.marketStableRate,
|
|
expectedReserveData.utilizationRate,
|
|
expectedReserveData.totalStableDebt,
|
|
expectedReserveData.totalVariableDebt,
|
|
expectedReserveData.averageStableBorrowRate
|
|
);
|
|
|
|
expectedReserveData.liquidityRate = rates[0];
|
|
|
|
expectedReserveData.stableBorrowRate = rates[1];
|
|
|
|
expectedReserveData.variableBorrowRate = rates[2];
|
|
|
|
return expectedReserveData;
|
|
};
|
|
|
|
export const calcExpectedUserDataAfterStableRateRebalance = (
|
|
reserveDataBeforeAction: ReserveData,
|
|
expectedDataAfterAction: ReserveData,
|
|
userDataBeforeAction: UserReserveData,
|
|
txCost: BigNumber,
|
|
txTimestamp: BigNumber
|
|
): UserReserveData => {
|
|
const expectedUserData = { ...userDataBeforeAction };
|
|
|
|
expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt;
|
|
|
|
expectedUserData.principalVariableDebt = calcExpectedVariableDebtTokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
expectedUserData.currentStableDebt = expectedUserData.principalStableDebt = calcExpectedStableDebtTokenBalance(
|
|
userDataBeforeAction.principalStableDebt,
|
|
userDataBeforeAction.stableBorrowRate,
|
|
userDataBeforeAction.stableRateLastUpdated,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedUserData.currentVariableDebt = calcExpectedVariableDebtTokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
expectedUserData.stableRateLastUpdated = txTimestamp;
|
|
|
|
expectedUserData.principalVariableDebt = userDataBeforeAction.principalVariableDebt;
|
|
|
|
// Stable rate after burn
|
|
expectedUserData.stableBorrowRate = expectedDataAfterAction.averageStableBorrowRate;
|
|
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
|
|
|
|
expectedUserData.currentATokenBalance = calcExpectedATokenBalance(
|
|
reserveDataBeforeAction,
|
|
userDataBeforeAction,
|
|
txTimestamp
|
|
);
|
|
|
|
return expectedUserData;
|
|
};
|
|
|
|
const calcExpectedScaledATokenBalance = (
|
|
userDataBeforeAction: UserReserveData,
|
|
index: BigNumber,
|
|
amountAdded: BigNumber,
|
|
amountTaken: BigNumber
|
|
) => {
|
|
return userDataBeforeAction.scaledATokenBalance
|
|
.plus(amountAdded.rayDiv(index))
|
|
.minus(amountTaken.rayDiv(index));
|
|
};
|
|
|
|
export const calcExpectedATokenBalance = (
|
|
reserveData: ReserveData,
|
|
userData: UserReserveData,
|
|
currentTimestamp: BigNumber
|
|
) => {
|
|
const index = calcExpectedReserveNormalizedIncome(reserveData, currentTimestamp);
|
|
|
|
const { scaledATokenBalance: scaledBalanceBeforeAction } = userData;
|
|
|
|
return scaledBalanceBeforeAction.rayMul(index);
|
|
};
|
|
|
|
const calcExpectedAverageStableBorrowRate = (
|
|
avgStableRateBefore: BigNumber,
|
|
totalStableDebtBefore: BigNumber,
|
|
amountChanged: string | BigNumber,
|
|
rate: BigNumber
|
|
) => {
|
|
const weightedTotalBorrows = avgStableRateBefore.multipliedBy(totalStableDebtBefore);
|
|
const weightedAmountBorrowed = rate.multipliedBy(amountChanged);
|
|
const totalBorrowedStable = totalStableDebtBefore.plus(amountChanged);
|
|
|
|
if (totalBorrowedStable.eq(0)) return new BigNumber('0');
|
|
|
|
return weightedTotalBorrows
|
|
.plus(weightedAmountBorrowed)
|
|
.div(totalBorrowedStable)
|
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
};
|
|
|
|
const calcExpectedAverageStableBorrowRateRebalance = (
|
|
avgStableRateBefore: BigNumber,
|
|
totalStableDebtBefore: BigNumber,
|
|
amountChanged: BigNumber,
|
|
rate: BigNumber
|
|
) => {
|
|
const weightedTotalBorrows = avgStableRateBefore.rayMul(totalStableDebtBefore);
|
|
const weightedAmountBorrowed = rate.rayMul(amountChanged.wadToRay());
|
|
const totalBorrowedStable = totalStableDebtBefore.plus(amountChanged.wadToRay());
|
|
|
|
if (totalBorrowedStable.eq(0)) return new BigNumber('0');
|
|
|
|
return weightedTotalBorrows
|
|
.plus(weightedAmountBorrowed)
|
|
.rayDiv(totalBorrowedStable)
|
|
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
|
};
|
|
|
|
export const calcExpectedVariableDebtTokenBalance = (
|
|
reserveData: ReserveData,
|
|
userData: UserReserveData,
|
|
currentTimestamp: BigNumber
|
|
) => {
|
|
const normalizedDebt = calcExpectedReserveNormalizedDebt(
|
|
reserveData.variableBorrowRate,
|
|
reserveData.variableBorrowIndex,
|
|
reserveData.lastUpdateTimestamp,
|
|
currentTimestamp
|
|
);
|
|
|
|
const { scaledVariableDebt } = userData;
|
|
|
|
return scaledVariableDebt.rayMul(normalizedDebt);
|
|
};
|
|
|
|
export const calcExpectedStableDebtTokenBalance = (
|
|
principalStableDebt: BigNumber,
|
|
stableBorrowRate: BigNumber,
|
|
stableRateLastUpdated: BigNumber,
|
|
currentTimestamp: BigNumber
|
|
) => {
|
|
if (
|
|
stableBorrowRate.eq(0) ||
|
|
currentTimestamp.eq(stableRateLastUpdated) ||
|
|
stableRateLastUpdated.eq(0)
|
|
) {
|
|
return principalStableDebt;
|
|
}
|
|
|
|
const cumulatedInterest = calcCompoundedInterest(
|
|
stableBorrowRate,
|
|
currentTimestamp,
|
|
stableRateLastUpdated
|
|
);
|
|
|
|
return principalStableDebt.rayMul(cumulatedInterest);
|
|
};
|
|
|
|
const calcLinearInterest = (
|
|
rate: BigNumber,
|
|
currentTimestamp: BigNumber,
|
|
lastUpdateTimestamp: BigNumber
|
|
) => {
|
|
const timeDifference = currentTimestamp.minus(lastUpdateTimestamp);
|
|
|
|
const cumulatedInterest = rate
|
|
.multipliedBy(timeDifference)
|
|
.dividedBy(new BigNumber(ONE_YEAR))
|
|
.plus(RAY);
|
|
|
|
return cumulatedInterest;
|
|
};
|
|
|
|
const calcCompoundedInterest = (
|
|
rate: BigNumber,
|
|
currentTimestamp: BigNumber,
|
|
lastUpdateTimestamp: BigNumber
|
|
) => {
|
|
const timeDifference = currentTimestamp.minus(lastUpdateTimestamp);
|
|
|
|
if (timeDifference.eq(0)) {
|
|
return new BigNumber(RAY);
|
|
}
|
|
|
|
const expMinusOne = timeDifference.minus(1);
|
|
const expMinusTwo = timeDifference.gt(2) ? timeDifference.minus(2) : 0;
|
|
|
|
const ratePerSecond = rate.div(ONE_YEAR);
|
|
|
|
const basePowerTwo = ratePerSecond.rayMul(ratePerSecond);
|
|
const basePowerThree = basePowerTwo.rayMul(ratePerSecond);
|
|
|
|
const secondTerm = timeDifference.times(expMinusOne).times(basePowerTwo).div(2);
|
|
const thirdTerm = timeDifference
|
|
.times(expMinusOne)
|
|
.times(expMinusTwo)
|
|
.times(basePowerThree)
|
|
.div(6);
|
|
|
|
return new BigNumber(RAY)
|
|
.plus(ratePerSecond.times(timeDifference))
|
|
.plus(secondTerm)
|
|
.plus(thirdTerm);
|
|
};
|
|
|
|
export const calcExpectedInterestRates = (
|
|
reserveSymbol: string,
|
|
marketStableRate: BigNumber,
|
|
utilizationRate: BigNumber,
|
|
totalStableDebt: BigNumber,
|
|
totalVariableDebt: 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.strategy.baseVariableBorrowRate);
|
|
|
|
const optimalRate = new BigNumber(reserveConfiguration.strategy.optimalUtilizationRate);
|
|
const excessRate = new BigNumber(RAY).minus(optimalRate);
|
|
if (utilizationRate.gt(optimalRate)) {
|
|
const excessUtilizationRateRatio = utilizationRate
|
|
.minus(reserveConfiguration.strategy.optimalUtilizationRate)
|
|
.rayDiv(excessRate);
|
|
|
|
stableBorrowRate = stableBorrowRate
|
|
.plus(reserveConfiguration.strategy.stableRateSlope1)
|
|
.plus(
|
|
new BigNumber(reserveConfiguration.strategy.stableRateSlope2).rayMul(excessUtilizationRateRatio)
|
|
);
|
|
|
|
variableBorrowRate = variableBorrowRate
|
|
.plus(reserveConfiguration.strategy.variableRateSlope1)
|
|
.plus(
|
|
new BigNumber(reserveConfiguration.strategy.variableRateSlope2).rayMul(excessUtilizationRateRatio)
|
|
);
|
|
} else {
|
|
stableBorrowRate = stableBorrowRate.plus(
|
|
new BigNumber(reserveConfiguration.strategy.stableRateSlope1).rayMul(
|
|
utilizationRate.rayDiv(new BigNumber(optimalRate))
|
|
)
|
|
);
|
|
|
|
variableBorrowRate = variableBorrowRate.plus(
|
|
utilizationRate
|
|
.rayDiv(optimalRate)
|
|
.rayMul(new BigNumber(reserveConfiguration.strategy.variableRateSlope1))
|
|
);
|
|
}
|
|
|
|
const expectedOverallRate = calcExpectedOverallBorrowRate(
|
|
totalStableDebt,
|
|
totalVariableDebt,
|
|
variableBorrowRate,
|
|
averageStableBorrowRate
|
|
);
|
|
const liquidityRate = expectedOverallRate
|
|
.rayMul(utilizationRate)
|
|
.percentMul(new BigNumber(PERCENTAGE_FACTOR).minus(reserveConfiguration.reserveFactor));
|
|
|
|
return [liquidityRate, stableBorrowRate, variableBorrowRate];
|
|
};
|
|
|
|
export const calcExpectedOverallBorrowRate = (
|
|
totalStableDebt: BigNumber,
|
|
totalVariableDebt: BigNumber,
|
|
currentVariableBorrowRate: BigNumber,
|
|
currentAverageStableBorrowRate: BigNumber
|
|
): BigNumber => {
|
|
const totalBorrows = totalStableDebt.plus(totalVariableDebt);
|
|
|
|
if (totalBorrows.eq(0)) return strToBN('0');
|
|
|
|
const weightedVariableRate = totalVariableDebt.wadToRay().rayMul(currentVariableBorrowRate);
|
|
|
|
const weightedStableRate = totalStableDebt.wadToRay().rayMul(currentAverageStableBorrowRate);
|
|
|
|
const overallBorrowRate = weightedVariableRate
|
|
.plus(weightedStableRate)
|
|
.rayDiv(totalBorrows.wadToRay());
|
|
|
|
return overallBorrowRate;
|
|
};
|
|
|
|
export const calcExpectedUtilizationRate = (
|
|
totalStableDebt: BigNumber,
|
|
totalVariableDebt: BigNumber,
|
|
totalLiquidity: BigNumber
|
|
): BigNumber => {
|
|
if (totalStableDebt.eq('0') && totalVariableDebt.eq('0')) {
|
|
return strToBN('0');
|
|
}
|
|
|
|
const utilization = totalStableDebt.plus(totalVariableDebt).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 calcExpectedReserveNormalizedDebt = (
|
|
variableBorrowRate: BigNumber,
|
|
variableBorrowIndex: BigNumber,
|
|
lastUpdateTimestamp: BigNumber,
|
|
currentTimestamp: BigNumber
|
|
) => {
|
|
//if utilization rate is 0, nothing to compound
|
|
if (variableBorrowRate.eq('0')) {
|
|
return variableBorrowIndex;
|
|
}
|
|
|
|
const cumulatedInterest = calcCompoundedInterest(
|
|
variableBorrowRate,
|
|
currentTimestamp,
|
|
lastUpdateTimestamp
|
|
);
|
|
|
|
const debt = cumulatedInterest.rayMul(variableBorrowIndex);
|
|
|
|
return debt;
|
|
};
|
|
|
|
const calcExpectedUserStableRate = (
|
|
balanceBefore: BigNumber,
|
|
rateBefore: BigNumber,
|
|
amount: BigNumber,
|
|
rateNew: BigNumber
|
|
) => {
|
|
return balanceBefore
|
|
.times(rateBefore)
|
|
.plus(amount.times(rateNew))
|
|
.div(balanceBefore.plus(amount));
|
|
};
|
|
|
|
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 totalVariableDebt is 0, nothing to compound
|
|
if (reserveData.totalVariableDebt.eq('0')) {
|
|
return reserveData.variableBorrowIndex;
|
|
}
|
|
|
|
const cumulatedInterest = calcCompoundedInterest(
|
|
reserveData.variableBorrowRate,
|
|
timestamp,
|
|
reserveData.lastUpdateTimestamp
|
|
);
|
|
|
|
return cumulatedInterest.rayMul(reserveData.variableBorrowIndex);
|
|
};
|
|
|
|
const calcExpectedTotalStableDebt = (
|
|
principalStableDebt: BigNumber,
|
|
averageStableBorrowRate: BigNumber,
|
|
lastUpdateTimestamp: BigNumber,
|
|
currentTimestamp: BigNumber
|
|
) => {
|
|
const cumulatedInterest = calcCompoundedInterest(
|
|
averageStableBorrowRate,
|
|
currentTimestamp,
|
|
lastUpdateTimestamp
|
|
);
|
|
|
|
return cumulatedInterest.rayMul(principalStableDebt);
|
|
};
|
|
|
|
const calcExpectedTotalVariableDebt = (
|
|
reserveData: ReserveData,
|
|
expectedVariableDebtIndex: BigNumber
|
|
) => {
|
|
return reserveData.scaledVariableDebt.rayMul(expectedVariableDebtIndex);
|
|
};
|