Updated testsW

This commit is contained in:
The3D 2020-09-18 17:56:33 +02:00
parent 13f6c264b3
commit 5868a4844f
5 changed files with 173 additions and 74 deletions

View File

@ -9,7 +9,8 @@ usePlugin('buidler-typechain');
usePlugin('solidity-coverage'); usePlugin('solidity-coverage');
usePlugin('@nomiclabs/buidler-waffle'); usePlugin('@nomiclabs/buidler-waffle');
usePlugin('@nomiclabs/buidler-etherscan'); usePlugin('@nomiclabs/buidler-etherscan');
usePlugin('buidler-gas-reporter'); //usePlugin('buidler-gas-reporter');
const DEFAULT_BLOCK_GAS_LIMIT = 10000000; const DEFAULT_BLOCK_GAS_LIMIT = 10000000;
const DEFAULT_GAS_PRICE = 10; const DEFAULT_GAS_PRICE = 10;
const HARDFORK = 'istanbul'; const HARDFORK = 'istanbul';

View File

@ -167,6 +167,10 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
uint256 previousSupply = totalSupply(); uint256 previousSupply = totalSupply();
//since the total supply and each single user debt accrue separately,
//there might be accumulation errors so that the last borrower repaying
//might actually try to repay more than the available debt supply.
//in this case we simply set the total supply and the avg stable rate to 0
if (previousSupply <= amount) { if (previousSupply <= amount) {
_avgStableRate = 0; _avgStableRate = 0;
_totalSupply = 0; _totalSupply = 0;
@ -232,32 +236,51 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
); );
} }
/**
* @dev returns the principal, total supply and the average borrow rate
**/
function getSupplyData() public override view returns (uint256, uint256, uint256) { function getSupplyData() public override view returns (uint256, uint256, uint256) {
uint256 avgRate = _avgStableRate; uint256 avgRate = _avgStableRate;
return (super.totalSupply(), _calcTotalSupply(avgRate), avgRate); return (super.totalSupply(), _calcTotalSupply(avgRate), avgRate);
} }
/**
* @dev returns the the total supply and the average stable rate
**/
function getTotalSupplyAndAvgRate() public override view returns (uint256, uint256) { function getTotalSupplyAndAvgRate() public override view returns (uint256, uint256) {
uint256 avgRate = _avgStableRate; uint256 avgRate = _avgStableRate;
return (_calcTotalSupply(avgRate), avgRate); return (_calcTotalSupply(avgRate), avgRate);
} }
/**
* @dev returns the total supply
**/
function totalSupply() public override view returns (uint256) { function totalSupply() public override view returns (uint256) {
return _calcTotalSupply(_avgStableRate); return _calcTotalSupply(_avgStableRate);
} }
/**
* @dev returns the timestamp at which the total supply was updated
**/
function getTotalSupplyLastUpdated() public override view returns(uint40) { function getTotalSupplyLastUpdated() public override view returns(uint40) {
return _totalSupplyTimestamp; return _totalSupplyTimestamp;
} }
/** /**
* @dev Returns the principal debt balance of the user from * @dev Returns the principal debt balance of the user from
* @param user the user
* @return The debt balance of the user since the last burn/mint action * @return The debt balance of the user since the last burn/mint action
**/ **/
function principalBalanceOf(address user) external virtual override view returns (uint256) { function principalBalanceOf(address user) external virtual override view returns (uint256) {
return super.balanceOf(user); return super.balanceOf(user);
} }
/**
* @dev calculates the total supply
* @param avgRate the average rate at which calculate the total supply
* @return The debt balance of the user since the last burn/mint action
**/
function _calcTotalSupply(uint256 avgRate) internal view returns(uint256) { function _calcTotalSupply(uint256 avgRate) internal view returns(uint256) {
uint256 principalSupply = super.totalSupply(); uint256 principalSupply = super.totalSupply();
@ -273,6 +296,12 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
return principalSupply.rayMul(cumulatedInterest); return principalSupply.rayMul(cumulatedInterest);
} }
/**
* @dev mints stable debt tokens to an user
* @param account the account receiving the debt tokens
* @param amount the amount being minted
* @param oldTotalSupply the total supply before the minting event
**/
function _mint(address account, uint256 amount, uint256 oldTotalSupply) internal { function _mint(address account, uint256 amount, uint256 oldTotalSupply) internal {
uint256 oldAccountBalance = _balances[account]; uint256 oldAccountBalance = _balances[account];
@ -283,9 +312,14 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
} }
} }
/**
* @dev burns stable debt tokens of an user
* @param account the user getting his debt burned
* @param amount the amount being burned
* @param oldTotalSupply the total supply before the burning event
**/
function _burn(address account, uint256 amount, uint256 oldTotalSupply) internal { function _burn(address account, uint256 amount, uint256 oldTotalSupply) internal {
uint256 oldAccountBalance = _balances[account]; uint256 oldAccountBalance = _balances[account];
_balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'); _balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance');

View File

@ -375,6 +375,12 @@ export const borrow = async (
txCost txCost
); );
console.log("total stable debt actual: ", reserveDataAfter.totalStableDebt.toFixed());
console.log("total stable debt expected: ", expectedReserveData.totalStableDebt.toFixed());
console.log("total variable debt actual: ", reserveDataAfter.totalVariableDebt.toFixed());
console.log("total variable debt expected: ", expectedReserveData.totalVariableDebt.toFixed());
expectEqual(reserveDataAfter, expectedReserveData); expectEqual(reserveDataAfter, expectedReserveData);
expectEqual(userDataAfter, expectedUserData); expectEqual(userDataAfter, expectedUserData);
@ -479,6 +485,14 @@ export const repay = async (
txCost txCost
); );
console.log("total stable debt actual: ", reserveDataAfter.totalStableDebt.toFixed());
console.log("total stable debt expected: ", expectedReserveData.totalStableDebt.toFixed());
console.log("total variable debt actual: ", reserveDataAfter.totalVariableDebt.toFixed());
console.log("total variable debt expected: ", expectedReserveData.totalVariableDebt.toFixed());
expectEqual(reserveDataAfter, expectedReserveData); expectEqual(reserveDataAfter, expectedReserveData);
expectEqual(userDataAfter, expectedUserData); expectEqual(userDataAfter, expectedUserData);
@ -741,9 +755,12 @@ export const getContractsData = async (
sender?: string sender?: string
) => { ) => {
const {pool} = testEnv; const {pool} = testEnv;
const reserveData = await getReserveData(pool, reserve);
const userData = await getUserData(pool, reserve, user, sender || user); const [userData, reserveData, timestamp] = await Promise.all([
const timestamp = await timeLatest(); getUserData(pool, reserve, user, sender || user),
getReserveData(pool, reserve),
timeLatest(),
]);
return { return {
reserveData, reserveData,

View File

@ -97,6 +97,8 @@ export const calcExpectedUserDataAfterWithdraw = (
currentTimestamp: BigNumber, currentTimestamp: BigNumber,
txCost: BigNumber txCost: BigNumber
): UserReserveData => { ): UserReserveData => {
console.log('Checking withdraw');
const expectedUserData = <UserReserveData>{}; const expectedUserData = <UserReserveData>{};
const aTokenBalance = calcExpectedATokenBalance( const aTokenBalance = calcExpectedATokenBalance(
@ -183,7 +185,9 @@ export const calcExpectedReserveDataAfterDeposit = (
); );
expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt( expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt(
reserveDataBeforeAction, reserveDataBeforeAction.principalStableDebt,
reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.lastUpdateTimestamp,
txTimestamp txTimestamp
); );
expectedReserveData.totalVariableDebt = calcExpectedTotalVariableDebt( expectedReserveData.totalVariableDebt = calcExpectedTotalVariableDebt(
@ -252,7 +256,9 @@ export const calcExpectedReserveDataAfterWithdraw = (
); );
expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt( expectedReserveData.totalStableDebt = calcExpectedTotalStableDebt(
reserveDataBeforeAction, reserveDataBeforeAction.principalStableDebt,
reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.lastUpdateTimestamp,
txTimestamp txTimestamp
); );
expectedReserveData.totalVariableDebt = calcExpectedTotalVariableDebt( expectedReserveData.totalVariableDebt = calcExpectedTotalVariableDebt(
@ -313,33 +319,71 @@ export const calcExpectedReserveDataAfterBorrow = (
expectedReserveData.lastUpdateTimestamp = txTimestamp; expectedReserveData.lastUpdateTimestamp = txTimestamp;
if (borrowRateMode == RateMode.Stable) { if (borrowRateMode == RateMode.Stable) {
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt; expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt;
expectedReserveData.totalVariableDebt = reserveDataBeforeAction.scaledVariableDebt.rayMul( const expectedVariableDebtAfterTx = calcExpectedTotalVariableDebt(
expectedReserveData.variableBorrowIndex reserveDataBeforeAction,
txTimestamp
); );
const expectedStableDebtUntilTx = calcExpectedTotalStableDebt(
reserveDataBeforeAction.principalStableDebt,
reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.lastUpdateTimestamp,
txTimestamp
);
expectedReserveData.principalStableDebt = expectedStableDebtUntilTx.plus(amountBorrowedBN);
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate( expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
reserveDataBeforeAction.averageStableBorrowRate, reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.totalStableDebt, expectedStableDebtUntilTx,
amountBorrowedBN, amountBorrowedBN,
reserveDataBeforeAction.stableBorrowRate reserveDataBeforeAction.stableBorrowRate
); );
expectedReserveData.principalStableDebt = reserveDataBeforeAction.totalStableDebt.plus( const totalLiquidityAfterTx = expectedReserveData.availableLiquidity
amountBorrowedBN .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.totalStableDebt = calcExpectedTotalStableDebt(
{ expectedReserveData.principalStableDebt,
...reserveDataBeforeAction, expectedReserveData.averageStableBorrowRate,
principalStableDebt: expectedReserveData.principalStableDebt, txTimestamp,
averageStableBorrowRate: expectedReserveData.averageStableBorrowRate,
lastUpdateTimestamp: txTimestamp,
},
currentTimestamp currentTimestamp
); );
expectedReserveData.totalVariableDebt = reserveDataBeforeAction.scaledVariableDebt.rayMul(
calcExpectedReserveNormalizedDebt(
expectedReserveData.variableBorrowRate,
expectedReserveData.variableBorrowIndex,
txTimestamp,
currentTimestamp
)
);
expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity
.plus(expectedReserveData.totalVariableDebt) .plus(expectedReserveData.totalVariableDebt)
.plus(expectedReserveData.totalStableDebt); .plus(expectedReserveData.totalStableDebt);
@ -349,21 +393,6 @@ export const calcExpectedReserveDataAfterBorrow = (
expectedReserveData.totalVariableDebt, expectedReserveData.totalVariableDebt,
expectedReserveData.totalLiquidity 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];
} else { } else {
expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt; expectedReserveData.principalStableDebt = reserveDataBeforeAction.principalStableDebt;
@ -406,7 +435,12 @@ export const calcExpectedReserveDataAfterBorrow = (
expectedReserveData.variableBorrowRate = rates[2]; expectedReserveData.variableBorrowRate = rates[2];
expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul( expectedReserveData.totalVariableDebt = expectedReserveData.scaledVariableDebt.rayMul(
calcExpectedReserveNormalizedDebt(expectedReserveData, currentTimestamp) calcExpectedReserveNormalizedDebt(
expectedReserveData.variableBorrowRate,
expectedReserveData.variableBorrowIndex,
txTimestamp,
currentTimestamp
)
); );
expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity expectedReserveData.totalLiquidity = expectedReserveData.availableLiquidity
@ -469,16 +503,32 @@ export const calcExpectedReserveDataAfterRepay = (
); );
if (borrowRateMode == RateMode.Stable) { if (borrowRateMode == RateMode.Stable) {
expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = reserveDataBeforeAction.totalStableDebt.minus( const expectedDebt = calcExpectedTotalStableDebt(
amountRepaidBN reserveDataBeforeAction.principalStableDebt,
reserveDataBeforeAction.averageStableBorrowRate,
reserveDataBeforeAction.lastUpdateTimestamp,
txTimestamp
); );
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate( expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = expectedDebt.minus(
reserveDataBeforeAction.averageStableBorrowRate, amountRepaidBN
reserveDataBeforeAction.totalStableDebt,
amountRepaidBN.negated(),
userDataBeforeAction.stableBorrowRate
); );
//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.principalStableDebt.lt(0)) {
expectedReserveData.principalStableDebt = expectedReserveData.totalStableDebt = new BigNumber(
0
);
expectedReserveData.averageStableBorrowRate = new BigNumber(0);
} else {
expectedReserveData.averageStableBorrowRate = calcExpectedAverageStableBorrowRate(
reserveDataBeforeAction.averageStableBorrowRate,
expectedDebt,
amountRepaidBN.negated(),
userDataBeforeAction.stableBorrowRate
);
}
expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt; expectedReserveData.scaledVariableDebt = reserveDataBeforeAction.scaledVariableDebt;
expectedReserveData.totalVariableDebt = reserveDataBeforeAction.totalVariableDebt; expectedReserveData.totalVariableDebt = reserveDataBeforeAction.totalVariableDebt;
} else { } else {
@ -636,22 +686,18 @@ export const calcExpectedUserDataAfterRepay = (
userDataBeforeAction.stableRateLastUpdated, userDataBeforeAction.stableRateLastUpdated,
currentTimestamp currentTimestamp
); );
let totalRepaidBN = new BigNumber(totalRepaid); let totalRepaidBN = new BigNumber(totalRepaid);
if (totalRepaidBN.abs().eq(MAX_UINT_AMOUNT)) { if (totalRepaidBN.abs().eq(MAX_UINT_AMOUNT)) {
totalRepaidBN = totalRepaidBN = rateMode == RateMode.Stable ? stableDebt : variableDebt;
rateMode == RateMode.Stable
? stableDebt
: variableDebt;
} }
if (rateMode == RateMode.Stable) { if (rateMode == RateMode.Stable) {
expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt; expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt;
expectedUserData.currentVariableDebt = userDataBeforeAction.currentVariableDebt; expectedUserData.currentVariableDebt = userDataBeforeAction.currentVariableDebt;
expectedUserData.principalStableDebt = expectedUserData.currentStableDebt = stableDebt.minus( expectedUserData.principalStableDebt = expectedUserData.currentStableDebt = stableDebt.minus(
totalRepaid totalRepaidBN
); );
if (expectedUserData.currentStableDebt.eq('0')) { if (expectedUserData.currentStableDebt.eq('0')) {
@ -664,15 +710,17 @@ export const calcExpectedUserDataAfterRepay = (
expectedUserData.stableRateLastUpdated = txTimestamp; expectedUserData.stableRateLastUpdated = txTimestamp;
} }
} else { } else {
expectedUserData.currentStableDebt = userDataBeforeAction.principalStableDebt; expectedUserData.currentStableDebt = userDataBeforeAction.principalStableDebt;
expectedUserData.principalStableDebt = stableDebt; expectedUserData.principalStableDebt = stableDebt;
expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate; expectedUserData.stableBorrowRate = userDataBeforeAction.stableBorrowRate;
expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated; expectedUserData.stableRateLastUpdated = userDataBeforeAction.stableRateLastUpdated;
expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt.minus(totalRepaidBN.rayDiv(expectedDataAfterAction.variableBorrowIndex));
expectedUserData.currentVariableDebt = expectedUserData.scaledVariableDebt.rayMul(expectedDataAfterAction.variableBorrowIndex);
expectedUserData.scaledVariableDebt = userDataBeforeAction.scaledVariableDebt.minus(
totalRepaidBN.rayDiv(expectedDataAfterAction.variableBorrowIndex)
);
expectedUserData.currentVariableDebt = expectedUserData.scaledVariableDebt.rayMul(
expectedDataAfterAction.variableBorrowIndex
);
} }
expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate; expectedUserData.liquidityRate = expectedDataAfterAction.liquidityRate;
@ -1028,23 +1076,17 @@ const calcExpectedAverageStableBorrowRate = (
.decimalPlaces(0, BigNumber.ROUND_DOWN); .decimalPlaces(0, BigNumber.ROUND_DOWN);
}; };
const calcExpectedVariableDebtUserIndex = (
reserveDataBeforeAction: ReserveData,
expectedUserBalanceAfterAction: BigNumber,
currentTimestamp: BigNumber
) => {
if (expectedUserBalanceAfterAction.eq(0)) {
return new BigNumber(0);
}
return calcExpectedReserveNormalizedDebt(reserveDataBeforeAction, currentTimestamp);
};
export const calcExpectedVariableDebtTokenBalance = ( export const calcExpectedVariableDebtTokenBalance = (
reserveData: ReserveData, reserveData: ReserveData,
userData: UserReserveData, userData: UserReserveData,
currentTimestamp: BigNumber currentTimestamp: BigNumber
) => { ) => {
const normalizedDebt = calcExpectedReserveNormalizedDebt(reserveData, currentTimestamp); const normalizedDebt = calcExpectedReserveNormalizedDebt(
reserveData.variableBorrowRate,
reserveData.variableBorrowIndex,
reserveData.lastUpdateTimestamp,
currentTimestamp
);
const {scaledVariableDebt} = userData; const {scaledVariableDebt} = userData;
@ -1237,11 +1279,11 @@ const calcExpectedReserveNormalizedIncome = (
}; };
const calcExpectedReserveNormalizedDebt = ( const calcExpectedReserveNormalizedDebt = (
reserveData: ReserveData, variableBorrowRate: BigNumber,
variableBorrowIndex: BigNumber,
lastUpdateTimestamp: BigNumber,
currentTimestamp: BigNumber currentTimestamp: BigNumber
) => { ) => {
const {variableBorrowRate, variableBorrowIndex, lastUpdateTimestamp} = reserveData;
//if utilization rate is 0, nothing to compound //if utilization rate is 0, nothing to compound
if (variableBorrowRate.eq('0')) { if (variableBorrowRate.eq('0')) {
return variableBorrowIndex; return variableBorrowIndex;
@ -1300,14 +1342,19 @@ const calcExpectedVariableBorrowIndex = (reserveData: ReserveData, timestamp: Bi
return cumulatedInterest.rayMul(reserveData.variableBorrowIndex); return cumulatedInterest.rayMul(reserveData.variableBorrowIndex);
}; };
const calcExpectedTotalStableDebt = (reserveData: ReserveData, timestamp: BigNumber) => { const calcExpectedTotalStableDebt = (
principalStableDebt: BigNumber,
averageStableBorrowRate: BigNumber,
lastUpdateTimestamp: BigNumber,
currentTimestamp: BigNumber
) => {
const cumulatedInterest = calcCompoundedInterest( const cumulatedInterest = calcCompoundedInterest(
reserveData.averageStableBorrowRate, averageStableBorrowRate,
timestamp, currentTimestamp,
reserveData.lastUpdateTimestamp lastUpdateTimestamp
); );
return cumulatedInterest.rayMul(reserveData.principalStableDebt); return cumulatedInterest.rayMul(principalStableDebt);
}; };
const calcExpectedTotalVariableDebt = ( const calcExpectedTotalVariableDebt = (

View File

@ -10,7 +10,7 @@ import {executeStory} from './helpers/scenario-engine';
const scenarioFolder = './test/helpers/scenarios/'; const scenarioFolder = './test/helpers/scenarios/';
const selectedScenarios: string[] = []; const selectedScenarios: string[] = ['borrow-repay-variable.json'];
fs.readdirSync(scenarioFolder).forEach((file) => { fs.readdirSync(scenarioFolder).forEach((file) => {
if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return; if (selectedScenarios.length > 0 && !selectedScenarios.includes(file)) return;