From d542f098c14ca31f60286eb75c158c7b4b656872 Mon Sep 17 00:00:00 2001 From: The3D Date: Mon, 14 Sep 2020 19:25:45 +0200 Subject: [PATCH] fixed deposit, withdraw tests --- contracts/libraries/logic/ReserveLogic.sol | 20 ++-- contracts/tokenization/StableDebtToken.sol | 32 ++++-- .../interfaces/IStableDebtToken.sol | 16 ++- helpers/contracts-helpers.ts | 15 +++ test/helpers/utils/calculations.ts | 97 ++++++++++++------- test/helpers/utils/helpers.ts | 55 ++++++----- test/helpers/utils/interfaces/index.ts | 2 + test/scenario.spec.ts | 2 +- 8 files changed, 157 insertions(+), 82 deletions(-) diff --git a/contracts/libraries/logic/ReserveLogic.sol b/contracts/libraries/logic/ReserveLogic.sol index b287094a..73b68f17 100644 --- a/contracts/libraries/logic/ReserveLogic.sol +++ b/contracts/libraries/logic/ReserveLogic.sol @@ -31,7 +31,6 @@ library ReserveLogic { * @param reserve the address of the reserve * @param liquidityRate the new liquidity rate * @param stableBorrowRate the new stable borrow rate - * @param averageStableBorrowRate the new average stable borrow rate * @param variableBorrowRate the new variable borrow rate * @param liquidityIndex the new liquidity index * @param variableBorrowIndex the new variable borrow index @@ -40,7 +39,6 @@ library ReserveLogic { address indexed reserve, uint256 liquidityRate, uint256 stableBorrowRate, - uint256 averageStableBorrowRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex @@ -190,12 +188,13 @@ library ReserveLogic { } struct UpdateInterestRatesLocalVars { - uint256 currentAvgStableRate; - uint256 availableLiquidity; address stableDebtTokenAddress; + uint256 availableLiquidity; + uint256 totalStableDebt; uint256 newLiquidityRate; uint256 newStableRate; uint256 newVariableRate; + uint256 avgStableRate; } /** @@ -215,8 +214,10 @@ library ReserveLogic { UpdateInterestRatesLocalVars memory vars; vars.stableDebtTokenAddress = reserve.stableDebtTokenAddress; - vars.currentAvgStableRate = IStableDebtToken(vars.stableDebtTokenAddress) - .getAverageStableRate(); + + (vars.totalStableDebt, vars.avgStableRate) = IStableDebtToken(vars.stableDebtTokenAddress) + .getTotalSupplyAndAvgRate(); + vars.availableLiquidity = IERC20(reserveAddress).balanceOf(aTokenAddress); ( @@ -226,9 +227,9 @@ library ReserveLogic { ) = IReserveInterestRateStrategy(reserve.interestRateStrategyAddress).calculateInterestRates( reserveAddress, vars.availableLiquidity.add(liquidityAdded).sub(liquidityTaken), - IERC20(vars.stableDebtTokenAddress).totalSupply(), + vars.totalStableDebt, IERC20(reserve.variableDebtTokenAddress).totalSupply(), - vars.currentAvgStableRate, + vars.avgStableRate, reserve.configuration.getReserveFactor() ); require(vars.newLiquidityRate < (1 << 128), 'ReserveLogic: Liquidity rate overflow'); @@ -243,7 +244,6 @@ library ReserveLogic { reserveAddress, vars.newLiquidityRate, vars.newStableRate, - vars.currentAvgStableRate, vars.newVariableRate, reserve.liquidityIndex, reserve.variableBorrowIndex @@ -318,7 +318,7 @@ library ReserveLogic { currentLiquidityRate, lastUpdateTimestamp ); - uint256 index = cumulatedLiquidityInterest.rayMul(reserve.liquidityIndex); + uint256 index = cumulatedLiquidityInterest.rayMul(liquidityIndex); require(index < (1 << 128), Errors.LIQUIDITY_INDEX_OVERFLOW); reserve.liquidityIndex = uint128(index); diff --git a/contracts/tokenization/StableDebtToken.sol b/contracts/tokenization/StableDebtToken.sol index 1d166c8f..0434906f 100644 --- a/contracts/tokenization/StableDebtToken.sol +++ b/contracts/tokenization/StableDebtToken.sol @@ -219,18 +219,18 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { return (super.totalSupply(), _avgStableRate); } - function totalSupply() public override view returns (uint256) { - uint256 principalSupply = super.totalSupply(); - if (principalSupply == 0) { - return 0; - } - uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( - _avgStableRate, - _totalSupplyTimestamp - ); - return principalSupply.rayMul(cumulatedInterest); + function getTotalSupplyAndAvgRate() public override view returns (uint256, uint256) { + uint256 avgRate = _avgStableRate; + return (_calcTotalSupply(avgRate), avgRate); } + function totalSupply() public override view returns (uint256) { + _calcTotalSupply(_avgStableRate); + } + + function getTotalSupplyLastUpdated() public override view returns(uint40) { + return _totalSupplyTimestamp; + } /** * @dev Returns the principal debt balance of the user from * @return The debt balance of the user since the last burn/mint action @@ -238,4 +238,16 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase { function principalBalanceOf(address user) external virtual override view returns (uint256) { return super.balanceOf(user); } + + function _calcTotalSupply(uint256 avgRate) internal view returns(uint256) { + uint256 principalSupply = super.totalSupply(); + if (principalSupply == 0) { + return 0; + } + uint256 cumulatedInterest = MathUtils.calculateCompoundedInterest( + avgRate, + _totalSupplyTimestamp + ); + return principalSupply.rayMul(cumulatedInterest); + } } diff --git a/contracts/tokenization/interfaces/IStableDebtToken.sol b/contracts/tokenization/interfaces/IStableDebtToken.sol index c1e97388..46324e88 100644 --- a/contracts/tokenization/interfaces/IStableDebtToken.sol +++ b/contracts/tokenization/interfaces/IStableDebtToken.sol @@ -90,10 +90,20 @@ interface IStableDebtToken { **/ function getPrincipalSupplyAndAvgRate() external view returns (uint256, uint256); - /** - * @dev Returns the principal debt balance of the user + /** + * @dev returns the timestamp of the last update of the total supply + * @return the timestamp + **/ + function getTotalSupplyLastUpdated() external view returns (uint40); + + /** + * @dev returns the total supply and the average stable rate + **/ + function getTotalSupplyAndAvgRate() external view returns (uint256, uint256); + + /** + * @dev Returns the principal debt balance of the user * @return The debt balance of the user since the last burn/mint action **/ function principalBalanceOf(address user) external view returns (uint256); - } diff --git a/helpers/contracts-helpers.ts b/helpers/contracts-helpers.ts index 11780c11..c5edc9da 100644 --- a/helpers/contracts-helpers.ts +++ b/helpers/contracts-helpers.ts @@ -348,6 +348,21 @@ export const getAToken = async (address?: tEthereumAddress) => { ); }; +export const getStableDebtToken = async (address?: tEthereumAddress) => { + return await getContract( + eContractid.StableDebtToken, + address || (await getDb().get(`${eContractid.StableDebtToken}.${BRE.network.name}`).value()).address + ); +}; + +export const getVariableDebtToken = async (address?: tEthereumAddress) => { + return await getContract( + eContractid.VariableDebtToken, + address || (await getDb().get(`${eContractid.VariableDebtToken}.${BRE.network.name}`).value()).address + ); +}; + + export const getMintableErc20 = async (address: tEthereumAddress) => { return await getContract( eContractid.MintableERC20, diff --git a/test/helpers/utils/calculations.ts b/test/helpers/utils/calculations.ts index 9c84d851..98d52cf2 100644 --- a/test/helpers/utils/calculations.ts +++ b/test/helpers/utils/calculations.ts @@ -42,7 +42,7 @@ export const calcExpectedUserDataAfterDeposit = ( ); expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt; - expectedUserData.principalVariableDebt = userDataBeforeAction.principalVariableDebt; + expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt; expectedUserData.variableBorrowIndex = userDataBeforeAction.variableBorrowIndex; expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate; expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated; @@ -115,7 +115,7 @@ export const calcExpectedUserDataAfterWithdraw = ( expectedUserData.currentATokenBalance = aTokenBalance.minus(amountWithdrawn); expectedUserData.principalStableDebt = userDataBeforeAction.principalStableDebt; - expectedUserData.principalVariableDebt = userDataBeforeAction.principalVariableDebt; + expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt; expectedUserData.currentStableDebt = calcExpectedStableDebtTokenBalance( userDataBeforeAction, @@ -166,9 +166,28 @@ export const calcExpectedReserveDataAfterDeposit = ( reserveDataBeforeAction.availableLiquidity ).plus(amountDeposited); - expectedReserveData.totalStableDebt = reserveDataBeforeAction.totalStableDebt; - expectedReserveData.totalVariableDebt = reserveDataBeforeAction.totalVariableDebt; expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate; + expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex( + reserveDataBeforeAction, + txTimestamp + ); + expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt( + reserveDataBeforeAction, + txTimestamp + ); + expectedReserveData.totalVariableDebt = calcExpectedTotalVariableDebt( + reserveDataBeforeAction, + expectedReserveData.variableBorrowIndex + ); + + + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt; + expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt; expectedReserveData.utilizationRate = calcExpectedUtilizationRate( expectedReserveData.totalStableDebt, @@ -186,17 +205,7 @@ export const calcExpectedReserveDataAfterDeposit = ( 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; }; @@ -225,8 +234,21 @@ export const calcExpectedReserveDataAfterWithdraw = ( reserveDataBeforeAction.availableLiquidity ).minus(amountWithdrawn); - expectedReserveData.totalStableDebt = reserveDataBeforeAction.totalStableDebt; - expectedReserveData.totalVariableDebt = reserveDataBeforeAction.totalVariableDebt; + expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt; + expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt; + + expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex( + reserveDataBeforeAction, + txTimestamp + ); + expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex( + reserveDataBeforeAction, + txTimestamp + ); + + expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt(reserveDataBeforeAction, txTimestamp); + expectedReserveData.totalVariableDebt = calcExpectedTotalVariableDebt(reserveDataBeforeAction, expectedReserveData.variableBorrowIndex); + expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate; expectedReserveData.utilizationRate = calcExpectedUtilizationRate( @@ -246,16 +268,6 @@ export const calcExpectedReserveDataAfterWithdraw = ( expectedReserveData.stableBorrowRate = rates[1]; expectedReserveData.variableBorrowRate = rates[2]; - expectedReserveData.averageStableBorrowRate = reserveDataBeforeAction.averageStableBorrowRate; - expectedReserveData.liquidityIndex = calcExpectedLiquidityIndex( - reserveDataBeforeAction, - txTimestamp - ); - expectedReserveData.variableBorrowIndex = calcExpectedVariableBorrowIndex( - reserveDataBeforeAction, - txTimestamp - ); - return expectedReserveData; }; @@ -512,7 +524,9 @@ export const calcExpectedUserDataAfterBorrow = ( expectedDataAfterAction, { ...userDataBeforeAction, - currentVariableDebt: expectedUserData.scaledVariableDebt.rayMul(reserveDataBeforeAction.variableBorrowIndex), + currentVariableDebt: expectedUserData.scaledVariableDebt.rayMul( + reserveDataBeforeAction.variableBorrowIndex + ), scaledVariableDebt: expectedUserData.scaledVariableDebt, variableBorrowIndex: interestRateMode == RateMode.Variable @@ -690,13 +704,14 @@ export const calcExpectedReserveDataAfterSwapRateMode = ( userDataBeforeAction.principalStableDebt ); } else { - const totalDebtBefore = userDataBeforeAction.scaledVariableDebt.rayMul(reserveDataBeforeAction.variableBorrowIndex); + const totalDebtBefore = userDataBeforeAction.scaledVariableDebt.rayMul( + reserveDataBeforeAction.variableBorrowIndex + ); const debtAccrued = variableDebt.minus(totalDebtBefore); expectedReserveData.totalLiquidity = reserveDataBeforeAction.totalLiquidity.plus(debtAccrued); - + expectedReserveData.totalVariableDebt = reserveDataBeforeAction.totalVariableDebt; - expectedReserveData.totalStableDebt = reserveDataBeforeAction.totalStableDebt.plus( variableDebt @@ -838,9 +853,7 @@ export const calcExpectedReserveDataAfterStableRateRebalance = ( ); expectedReserveData.totalVariableDebt = reserveDataBeforeAction.totalVariableDebt; - expectedReserveData.totalStableDebt = reserveDataBeforeAction.totalStableDebt.plus( - debtAccrued - ); + expectedReserveData.totalStableDebt = reserveDataBeforeAction.totalStableDebt.plus(debtAccrued); expectedReserveData.utilizationRate = calcExpectedUtilizationRate( expectedReserveData.totalStableDebt, @@ -1223,3 +1236,19 @@ const calcExpectedVariableBorrowIndex = (reserveData: ReserveData, timestamp: Bi return cumulatedInterest.rayMul(reserveData.variableBorrowIndex); }; + +const calcExpectedTotalStableDebt = (reserveData: ReserveData, timestamp: BigNumber) => { + + const cumulatedInterest = calcCompoundedInterest( + reserveData.averageStableBorrowRate, + timestamp, + reserveData.lastUpdateTimestamp + ); + + return cumulatedInterest.rayMul(reserveData.principalStableDebt); +} + +const calcExpectedTotalVariableDebt = (reserveData: ReserveData, expectedVariableDebtIndex: BigNumber) => { + + return reserveData.scaledVariableDebt.rayMul(expectedVariableDebtIndex); +} diff --git a/test/helpers/utils/helpers.ts b/test/helpers/utils/helpers.ts index 2289c7ad..3f3a2ec8 100644 --- a/test/helpers/utils/helpers.ts +++ b/test/helpers/utils/helpers.ts @@ -4,9 +4,8 @@ import { getLendingRateOracle, getIErc20Detailed, getMintableErc20, - getAToken, + getAToken, getStableDebtToken, getVariableDebtToken } from '../../../helpers/contracts-helpers'; -import {ZERO_ADDRESS} from '../../../helpers/constants'; import {tEthereumAddress} from '../../../helpers/types'; import BigNumber from 'bignumber.js'; import {getDb, BRE} from '../../../helpers/misc-utils'; @@ -15,41 +14,51 @@ export const getReserveData = async ( pool: LendingPool, reserve: tEthereumAddress ): Promise => { - const data = await pool.getReserveData(reserve); - const tokenAddresses = await pool.getReserveTokensAddresses(reserve); - const rateOracle = await getLendingRateOracle(); + const [reserveData, tokenAddresses, rateOracle, token] = await Promise.all([ + pool.getReserveData(reserve), + pool.getReserveTokensAddresses(reserve), + getLendingRateOracle(), + getIErc20Detailed(reserve), + ]); + + const stableDebtToken = await getStableDebtToken(tokenAddresses.stableDebtTokenAddress); + const variableDebtToken = await getVariableDebtToken(tokenAddresses.variableDebtTokenAddress); + + const [principalStableDebt] = await stableDebtToken.getPrincipalSupplyAndAvgRate(); + + const scaledVariableDebt = await variableDebtToken.scaledTotalSupply(); const rate = (await rateOracle.getMarketBorrowRate(reserve)).toString(); - - const token = await getIErc20Detailed(reserve); const symbol = await token.symbol(); const decimals = new BigNumber(await token.decimals()); - const totalLiquidity = new BigNumber(data.availableLiquidity.toString()) - .plus(data.totalStableDebt.toString()) - .plus(data.totalVariableDebt.toString()); + const totalLiquidity = new BigNumber(reserveData.availableLiquidity.toString()) + .plus(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()); const utilizationRate = new BigNumber( totalLiquidity.eq(0) ? 0 - : new BigNumber(data.totalStableDebt.toString()) - .plus(data.totalVariableDebt.toString()) + : new BigNumber(reserveData.totalStableDebt.toString()) + .plus(reserveData.totalVariableDebt.toString()) .rayDiv(totalLiquidity) ); return { totalLiquidity, utilizationRate, - availableLiquidity: new BigNumber(data.availableLiquidity.toString()), - totalStableDebt: new BigNumber(data.totalStableDebt.toString()), - totalVariableDebt: new BigNumber(data.totalVariableDebt.toString()), - liquidityRate: new BigNumber(data.liquidityRate.toString()), - variableBorrowRate: new BigNumber(data.variableBorrowRate.toString()), - stableBorrowRate: new BigNumber(data.stableBorrowRate.toString()), - averageStableBorrowRate: new BigNumber(data.averageStableBorrowRate.toString()), - liquidityIndex: new BigNumber(data.liquidityIndex.toString()), - variableBorrowIndex: new BigNumber(data.variableBorrowIndex.toString()), - lastUpdateTimestamp: new BigNumber(data.lastUpdateTimestamp), + availableLiquidity: new BigNumber(reserveData.availableLiquidity.toString()), + totalStableDebt: new BigNumber(reserveData.totalStableDebt.toString()), + totalVariableDebt: new BigNumber(reserveData.totalVariableDebt.toString()), + liquidityRate: new BigNumber(reserveData.liquidityRate.toString()), + variableBorrowRate: new BigNumber(reserveData.variableBorrowRate.toString()), + stableBorrowRate: new BigNumber(reserveData.stableBorrowRate.toString()), + averageStableBorrowRate: new BigNumber(reserveData.averageStableBorrowRate.toString()), + liquidityIndex: new BigNumber(reserveData.liquidityIndex.toString()), + variableBorrowIndex: new BigNumber(reserveData.variableBorrowIndex.toString()), + lastUpdateTimestamp: new BigNumber(reserveData.lastUpdateTimestamp), + principalStableDebt: new BigNumber(principalStableDebt.toString()), + scaledVariableDebt: new BigNumber(scaledVariableDebt.toString()), address: reserve, aTokenAddress: tokenAddresses.aTokenAddress, symbol, @@ -69,7 +78,6 @@ export const getUserData = async ( getATokenUserData(reserve, user, pool), ]); - const token = await getMintableErc20(reserve); const walletBalance = new BigNumber((await token.balanceOf(sender || user)).toString()); @@ -106,5 +114,4 @@ const getATokenUserData = async (reserve: string, user: string, pool: LendingPoo const scaledBalance = await aToken.scaledBalanceOf(user); return scaledBalance.toString(); - }; diff --git a/test/helpers/utils/interfaces/index.ts b/test/helpers/utils/interfaces/index.ts index b0497592..3162e398 100644 --- a/test/helpers/utils/interfaces/index.ts +++ b/test/helpers/utils/interfaces/index.ts @@ -23,6 +23,8 @@ export interface ReserveData { availableLiquidity: BigNumber; totalStableDebt: BigNumber; totalVariableDebt: BigNumber; + principalStableDebt: BigNumber, + scaledVariableDebt: BigNumber, averageStableBorrowRate: BigNumber; variableBorrowRate: BigNumber; stableBorrowRate: BigNumber; diff --git a/test/scenario.spec.ts b/test/scenario.spec.ts index 17830d1c..ee45a08f 100644 --- a/test/scenario.spec.ts +++ b/test/scenario.spec.ts @@ -10,7 +10,7 @@ import {executeStory} from './helpers/scenario-engine'; const scenarioFolder = './test/helpers/scenarios/'; -const selectedScenarios: string[] = ['deposit.json']; +const selectedScenarios: string[] = ['withdraw.json']; fs.readdirSync(scenarioFolder).forEach((file) => { if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return;