mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
1509 lines
48 KiB
TypeScript
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);
|
|
};
|