fix: updated implementation of the atokens/debt tokens to store additional data (user index/borrow rate) per user

This commit is contained in:
The3D 2021-07-27 10:05:55 +02:00
parent d62ef92d30
commit b6ae954093
9 changed files with 199 additions and 60 deletions

View File

@ -23,4 +23,11 @@ interface IScaledBalanceToken {
* @return The scaled total supply * @return The scaled total supply
**/ **/
function scaledTotalSupply() external view returns (uint256); function scaledTotalSupply() external view returns (uint256);
/**
* @dev Returns the index at the moment of the last action (mint/burn/transfer)
* @param user The address of the user
* @return The last user index
**/
function lastUserIndex(address user) external view returns (uint256);
} }

View File

@ -781,8 +781,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
vars.releaseUnderlying ? vars.amount : 0 vars.releaseUnderlying ? vars.amount : 0
); );
_usersLastBorrowTimestamp[vars.asset][vars.user] = block.timestamp;
if (vars.releaseUnderlying) { if (vars.releaseUnderlying) {
IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount); IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
} }
@ -906,8 +904,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
interestRateMode, interestRateMode,
onBehalfOf, onBehalfOf,
stableDebt, stableDebt,
variableDebt, variableDebt
_usersLastBorrowTimestamp
); );
uint256 paybackAmount = uint256 paybackAmount =

View File

@ -35,7 +35,4 @@ contract LendingPoolStorage {
mapping(address => bool) _authorizedFlashBorrowers; mapping(address => bool) _authorizedFlashBorrowers;
uint256 internal _flashLoanPremiumToProtocol; uint256 internal _flashLoanPremiumToProtocol;
mapping(address => mapping(address => uint256)) _usersLastBorrowTimestamp;
} }

View File

@ -255,7 +255,6 @@ library ValidationLogic {
* @param onBehalfOf The address of the user msg.sender is repaying for * @param onBehalfOf The address of the user msg.sender is repaying for
* @param stableDebt The borrow balance of the user * @param stableDebt The borrow balance of the user
* @param variableDebt The borrow balance of the user * @param variableDebt The borrow balance of the user
* @param lastUsersBorrowTimestamp The data structure that keeps track of all the latest borrowings from the users
*/ */
function validateRepay( function validateRepay(
DataTypes.ReserveCache memory reserveCache, DataTypes.ReserveCache memory reserveCache,
@ -264,8 +263,7 @@ library ValidationLogic {
DataTypes.InterestRateMode rateMode, DataTypes.InterestRateMode rateMode,
address onBehalfOf, address onBehalfOf,
uint256 stableDebt, uint256 stableDebt,
uint256 variableDebt, uint256 variableDebt
mapping(address => mapping(address => uint256)) storage lastUsersBorrowTimestamp
) external view { ) external view {
(bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory(); (bool isActive, , , , bool isPaused) = reserveCache.reserveConfiguration.getFlagsMemory();
require(isActive, Errors.VL_NO_ACTIVE_RESERVE); require(isActive, Errors.VL_NO_ACTIVE_RESERVE);
@ -273,18 +271,22 @@ library ValidationLogic {
require(amountSent > 0, Errors.VL_INVALID_AMOUNT); require(amountSent > 0, Errors.VL_INVALID_AMOUNT);
require( if (DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) {
lastUsersBorrowTimestamp[asset][onBehalfOf] != uint40(block.timestamp), require(
Errors.VL_SAME_BLOCK_BORROW_REPAY reserveCache.stableDebtLastUpdateTimestamp != block.timestamp,
); Errors.VL_SAME_BLOCK_BORROW_REPAY
);
require( require(stableDebt > 0, Errors.VL_NO_DEBT_OF_SELECTED_TYPE);
(stableDebt > 0 && } else if (DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.VARIABLE) {
DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) || require(
(variableDebt > 0 && IVariableDebtToken(reserveCache.variableDebtTokenAddress).lastUserIndex(onBehalfOf) !=
DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.VARIABLE), reserveCache.currVariableBorrowIndex,
Errors.VL_NO_DEBT_OF_SELECTED_TYPE Errors.VL_SAME_BLOCK_BORROW_REPAY
); );
require(variableDebt > 0, Errors.VL_NO_DEBT_OF_SELECTED_TYPE);
} else {
revert(Errors.VL_INVALID_INTEREST_RATE_MODE_SELECTED);
}
require( require(
amountSent != uint256(-1) || msg.sender == onBehalfOf, amountSent != uint256(-1) || msg.sender == onBehalfOf,

View File

@ -125,7 +125,7 @@ contract AToken is
) external override onlyLendingPool { ) external override onlyLendingPool {
uint256 amountScaled = amount.rayDiv(index); uint256 amountScaled = amount.rayDiv(index);
require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT); require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT);
_burn(user, amountScaled); _burn(user, amountScaled, index);
IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount); IERC20(_underlyingAsset).safeTransfer(receiverOfUnderlying, amount);
@ -150,7 +150,7 @@ contract AToken is
uint256 amountScaled = amount.rayDiv(index); uint256 amountScaled = amount.rayDiv(index);
require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT); require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT);
_mint(user, amountScaled); _mint(user, amountScaled, index);
emit Transfer(address(0), user, amount); emit Transfer(address(0), user, amount);
emit Mint(user, amount, index); emit Mint(user, amount, index);
@ -175,7 +175,7 @@ contract AToken is
// The amount to mint can easily be very small since it is a fraction of the interest ccrued. // The amount to mint can easily be very small since it is a fraction of the interest ccrued.
// In that case, the treasury will experience a (very small) loss, but it // In that case, the treasury will experience a (very small) loss, but it
// wont cause potentially valid transactions to fail. // wont cause potentially valid transactions to fail.
_mint(treasury, amount.rayDiv(index)); _mint(treasury, amount.rayDiv(index), uint128(index));
emit Transfer(address(0), treasury, amount); emit Transfer(address(0), treasury, amount);
emit Mint(treasury, amount, index); emit Mint(treasury, amount, index);
@ -263,6 +263,15 @@ contract AToken is
return super.totalSupply(); return super.totalSupply();
} }
/**
* @dev Returns the index at the moment of the last action (mint/burn/transfer)
* @param user The address of the user
* @return The last user index
**/
function lastUserIndex(address user) external view virtual override returns (uint256) {
return _usersData[user].data;
}
/** /**
* @dev Returns the address of the Aave treasury, receiving the fees on this aToken * @dev Returns the address of the Aave treasury, receiving the fees on this aToken
**/ **/
@ -383,6 +392,9 @@ contract AToken is
super._transfer(from, to, amount.rayDiv(index)); super._transfer(from, to, amount.rayDiv(index));
_usersData[from].data = uint128(index);
_usersData[to].data = uint128(index);
if (validate) { if (validate) {
pool.finalizeTransfer(underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore); pool.finalizeTransfer(underlyingAsset, from, to, amount, fromBalanceBefore, toBalanceBefore);
} }

View File

@ -15,7 +15,12 @@ import {IAaveIncentivesController} from '../../interfaces/IAaveIncentivesControl
abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed { abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed {
using SafeMath for uint256; using SafeMath for uint256;
mapping(address => uint256) internal _balances; struct BalanceInfo {
uint128 balance;
uint128 data;
}
mapping(address => BalanceInfo) internal _usersData;
mapping(address => mapping(address => uint256)) private _allowances; mapping(address => mapping(address => uint256)) private _allowances;
uint256 internal _totalSupply; uint256 internal _totalSupply;
@ -65,7 +70,7 @@ abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed {
* @return The balance of the token * @return The balance of the token
**/ **/
function balanceOf(address account) public view virtual override returns (uint256) { function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account]; return _usersData[account].balance;
} }
/** /**
@ -177,10 +182,11 @@ abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed {
_beforeTokenTransfer(sender, recipient, amount); _beforeTokenTransfer(sender, recipient, amount);
uint256 oldSenderBalance = _balances[sender]; uint256 oldSenderBalance = _usersData[sender].balance;
_balances[sender] = oldSenderBalance.sub(amount, 'ERC20: transfer amount exceeds balance'); _usersData[sender].balance = uint128(oldSenderBalance.sub(amount, 'ERC20: transfer amount exceeds balance'));
uint256 oldRecipientBalance = _balances[recipient];
_balances[recipient] = _balances[recipient].add(amount); uint256 oldRecipientBalance = _usersData[recipient].balance;
require((_usersData[recipient].balance = uint128(oldRecipientBalance.add(amount))) >= oldRecipientBalance, 'ERC20: Balance overflow');
if (address(_getIncentivesController()) != address(0)) { if (address(_getIncentivesController()) != address(0)) {
uint256 currentTotalSupply = _totalSupply; uint256 currentTotalSupply = _totalSupply;
@ -191,7 +197,7 @@ abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed {
} }
} }
function _mint(address account, uint256 amount) internal virtual { function _mint(address account, uint256 amount, uint256 data) internal virtual {
require(account != address(0), 'ERC20: mint to the zero address'); require(account != address(0), 'ERC20: mint to the zero address');
_beforeTokenTransfer(address(0), account, amount); _beforeTokenTransfer(address(0), account, amount);
@ -199,15 +205,16 @@ abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed {
uint256 oldTotalSupply = _totalSupply; uint256 oldTotalSupply = _totalSupply;
_totalSupply = oldTotalSupply.add(amount); _totalSupply = oldTotalSupply.add(amount);
uint256 oldAccountBalance = _balances[account]; uint256 oldAccountBalance = _usersData[account].balance;
_balances[account] = oldAccountBalance.add(amount); require((_usersData[account].balance = uint128(oldAccountBalance.add(amount))) >= oldAccountBalance, 'ERC20: Balance overflow');
require((_usersData[account].data = uint128(data)) == data, 'ERC20: Data field overflow');
if (address(_getIncentivesController()) != address(0)) { if (address(_getIncentivesController()) != address(0)) {
_getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance); _getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance);
} }
} }
function _burn(address account, uint256 amount) internal virtual { function _burn(address account, uint256 amount, uint256 data) internal virtual {
require(account != address(0), 'ERC20: burn from the zero address'); require(account != address(0), 'ERC20: burn from the zero address');
_beforeTokenTransfer(account, address(0), amount); _beforeTokenTransfer(account, address(0), amount);
@ -215,8 +222,9 @@ abstract contract IncentivizedERC20 is Context, IERC20, IERC20Detailed {
uint256 oldTotalSupply = _totalSupply; uint256 oldTotalSupply = _totalSupply;
_totalSupply = oldTotalSupply.sub(amount); _totalSupply = oldTotalSupply.sub(amount);
uint256 oldAccountBalance = _balances[account]; uint256 oldAccountBalance = _usersData[account].balance;
_balances[account] = oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'); _usersData[account].balance = uint128(oldAccountBalance.sub(amount, 'ERC20: burn amount exceeds balance'));
require((_usersData[account].data = uint128(data)) == data, 'ERC20: Data field overflow');
if (address(_getIncentivesController()) != address(0)) { if (address(_getIncentivesController()) != address(0)) {
_getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance); _getIncentivesController().handleAction(account, oldTotalSupply, oldAccountBalance);

View File

@ -22,7 +22,6 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
uint256 internal _avgStableRate; uint256 internal _avgStableRate;
mapping(address => uint40) internal _timestamps; mapping(address => uint40) internal _timestamps;
mapping(address => uint256) internal _usersStableRate;
uint40 internal _totalSupplyTimestamp; uint40 internal _totalSupplyTimestamp;
ILendingPool internal _pool; ILendingPool internal _pool;
@ -96,7 +95,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
* @return The stable rate of user * @return The stable rate of user
**/ **/
function getUserStableRate(address user) external view virtual override returns (uint256) { function getUserStableRate(address user) external view virtual override returns (uint256) {
return _usersStableRate[user]; return _usersData[user].data;
} }
/** /**
@ -105,7 +104,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
**/ **/
function balanceOf(address account) public view virtual override returns (uint256) { function balanceOf(address account) public view virtual override returns (uint256) {
uint256 accountBalance = super.balanceOf(account); uint256 accountBalance = super.balanceOf(account);
uint256 stableRate = _usersStableRate[account]; uint256 stableRate = _usersData[account].data;
if (accountBalance == 0) { if (accountBalance == 0) {
return 0; return 0;
} }
@ -120,6 +119,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
uint256 amountInRay; uint256 amountInRay;
uint256 newStableRate; uint256 newStableRate;
uint256 currentAvgStableRate; uint256 currentAvgStableRate;
uint256 currentStableRate;
} }
/** /**
@ -150,16 +150,18 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
vars.previousSupply = totalSupply(); vars.previousSupply = totalSupply();
vars.currentAvgStableRate = _avgStableRate; vars.currentAvgStableRate = _avgStableRate;
vars.nextSupply = _totalSupply = vars.previousSupply.add(amount); vars.nextSupply = _totalSupply = vars.previousSupply.add(amount);
vars.amountInRay = amount.wadToRay(); vars.amountInRay = amount.wadToRay();
vars.currentStableRate = _usersData[onBehalfOf].data;
vars.newStableRate = _usersStableRate[onBehalfOf]
if(vars.currentStableRate == 0){
vars.newStableRate = rate;
}else {
vars.newStableRate = vars.currentStableRate
.rayMul(currentBalance.wadToRay()) .rayMul(currentBalance.wadToRay())
.add(vars.amountInRay.rayMul(rate)) .add(vars.amountInRay.rayMul(rate))
.rayDiv(currentBalance.add(amount).wadToRay()); .rayDiv(currentBalance.add(amount).wadToRay());
}
require(vars.newStableRate <= type(uint128).max, Errors.SDT_STABLE_DEBT_OVERFLOW);
_usersStableRate[onBehalfOf] = vars.newStableRate;
//solium-disable-next-line //solium-disable-next-line
_totalSupplyTimestamp = _timestamps[onBehalfOf] = uint40(block.timestamp); _totalSupplyTimestamp = _timestamps[onBehalfOf] = uint40(block.timestamp);
@ -171,7 +173,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
.add(rate.rayMul(vars.amountInRay)) .add(rate.rayMul(vars.amountInRay))
.rayDiv(vars.nextSupply.wadToRay()); .rayDiv(vars.nextSupply.wadToRay());
_mint(onBehalfOf, amount.add(balanceIncrease), vars.previousSupply); _mint(onBehalfOf, amount.add(balanceIncrease), vars.previousSupply, vars.newStableRate);
emit Transfer(address(0), onBehalfOf, amount); emit Transfer(address(0), onBehalfOf, amount);
@ -200,7 +202,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
uint256 previousSupply = totalSupply(); uint256 previousSupply = totalSupply();
uint256 newAvgStableRate = 0; uint256 newAvgStableRate = 0;
uint256 nextSupply = 0; uint256 nextSupply = 0;
uint256 userStableRate = _usersStableRate[user]; uint256 userStableRate = _usersData[user].data;
// Since the total supply and each single user debt accrue separately, // Since the total supply and each single user debt accrue separately,
// there might be accumulation errors so that the last borrower repaying // there might be accumulation errors so that the last borrower repaying
@ -225,7 +227,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
} }
if (amount == currentBalance) { if (amount == currentBalance) {
_usersStableRate[user] = 0; _usersData[user].data = 0;
_timestamps[user] = 0; _timestamps[user] = 0;
} else { } else {
//solium-disable-next-line //solium-disable-next-line
@ -236,7 +238,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
if (balanceIncrease > amount) { if (balanceIncrease > amount) {
uint256 amountToMint = balanceIncrease.sub(amount); uint256 amountToMint = balanceIncrease.sub(amount);
_mint(user, amountToMint, previousSupply); _mint(user, amountToMint, previousSupply, userStableRate);
emit Mint( emit Mint(
user, user,
user, user,
@ -249,7 +251,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
); );
} else { } else {
uint256 amountToBurn = amount.sub(balanceIncrease); uint256 amountToBurn = amount.sub(balanceIncrease);
_burn(user, amountToBurn, previousSupply); _burn(user, amountToBurn, previousSupply, userStableRate);
emit Burn(user, amountToBurn, currentBalance, balanceIncrease, newAvgStableRate, nextSupply); emit Burn(user, amountToBurn, currentBalance, balanceIncrease, newAvgStableRate, nextSupply);
} }
@ -404,11 +406,15 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
function _mint( function _mint(
address account, address account,
uint256 amount, uint256 amount,
uint256 oldTotalSupply uint256 oldTotalSupply,
uint256 data
) internal { ) internal {
uint256 oldAccountBalance = _balances[account]; uint256 oldAccountBalance = _usersData[account].balance;
_balances[account] = oldAccountBalance.add(amount); require(
(_usersData[account].balance = uint128(oldAccountBalance.add(amount))) >= oldAccountBalance,
'ERC20: Balance overflow'
);
_usersData[account].data = uint128(data);
if (address(_incentivesController) != address(0)) { if (address(_incentivesController) != address(0)) {
_incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
} }
@ -423,11 +429,14 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
function _burn( function _burn(
address account, address account,
uint256 amount, uint256 amount,
uint256 oldTotalSupply uint256 oldTotalSupply,
uint256 data
) internal { ) internal {
uint256 oldAccountBalance = _balances[account]; uint256 oldAccountBalance = _usersData[account].balance;
_balances[account] = oldAccountBalance.sub(amount, Errors.SDT_BURN_EXCEEDS_BALANCE); _usersData[account].balance = uint128(
oldAccountBalance.sub(amount, Errors.SDT_BURN_EXCEEDS_BALANCE)
);
_usersData[account].data = uint128(data);
if (address(_incentivesController) != address(0)) { if (address(_incentivesController) != address(0)) {
_incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance); _incentivesController.handleAction(account, oldTotalSupply, oldAccountBalance);
} }

View File

@ -106,7 +106,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
uint256 amountScaled = amount.rayDiv(index); uint256 amountScaled = amount.rayDiv(index);
require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT); require(amountScaled != 0, Errors.CT_INVALID_MINT_AMOUNT);
_mint(onBehalfOf, amountScaled); _mint(onBehalfOf, amountScaled, index);
emit Transfer(address(0), onBehalfOf, amount); emit Transfer(address(0), onBehalfOf, amount);
emit Mint(user, onBehalfOf, amount, index); emit Mint(user, onBehalfOf, amount, index);
@ -129,7 +129,7 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
uint256 amountScaled = amount.rayDiv(index); uint256 amountScaled = amount.rayDiv(index);
require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT); require(amountScaled != 0, Errors.CT_INVALID_BURN_AMOUNT);
_burn(user, amountScaled); _burn(user, amountScaled, index);
emit Transfer(user, address(0), amount); emit Transfer(user, address(0), amount);
emit Burn(user, amount, index); emit Burn(user, amount, index);
@ -159,6 +159,15 @@ contract VariableDebtToken is DebtTokenBase, IVariableDebtToken {
return super.totalSupply(); return super.totalSupply();
} }
/**
* @dev Returns the index at the moment of the last action (mint/burn/transfer)
* @param user The address of the user
* @return The last user index
**/
function lastUserIndex(address user) external view virtual override returns (uint256) {
return _usersData[user].data;
}
/** /**
* @dev Returns the principal balance of the user and principal total supply. * @dev Returns the principal balance of the user and principal total supply.
* @param user The address of the user * @param user The address of the user

View File

@ -0,0 +1,98 @@
import { TestEnv, makeSuite } from './helpers/make-suite';
import {
APPROVAL_AMOUNT_LENDING_POOL,
MAX_UINT_AMOUNT,
RAY,
MAX_EXPOSURE_CAP,
MOCK_CHAINLINK_AGGREGATORS_PRICES,
oneEther,
} from '../../helpers/constants';
import { ProtocolErrors } from '../../helpers/types';
import { MintableERC20, WETH9, WETH9Mocked } from '../../types';
import { parseEther } from '@ethersproject/units';
import { BigNumber } from '@ethersproject/bignumber';
import { strategyDAI } from '../../markets/amm/reservesConfigs';
import { strategyUSDC } from '../../markets/amm/reservesConfigs';
import { ethers } from 'ethers';
import { convertToCurrencyDecimals } from '../../helpers/contracts-helpers';
const { expect } = require('chai');
makeSuite('LTV validation tests', (testEnv: TestEnv) => {
const {
VL_LTV_VALIDATION_FAILED,
RC_INVALID_EXPOSURE_CAP,
VL_COLLATERAL_CANNOT_COVER_NEW_BORROW,
} = ProtocolErrors;
const daiPrice = Number(MOCK_CHAINLINK_AGGREGATORS_PRICES.DAI);
const usdcPrice = Number(MOCK_CHAINLINK_AGGREGATORS_PRICES.USDC);
const daiLTV = Number(strategyDAI.baseLTVAsCollateral);
const usdcLTV = Number(strategyUSDC.baseLTVAsCollateral);
it('User 1 deposits 10 Dai, 10 USDC, user 2 deposits 1 WETH', async () => {
const {
pool,
dai,
usdc,
weth,
users: [user1, user2],
} = testEnv;
const daiAmount = await convertToCurrencyDecimals(dai.address, '10');
const usdcAmount = await convertToCurrencyDecimals(usdc.address, '10');
const wethAmount = await convertToCurrencyDecimals(weth.address, '1');
await dai.connect(user1.signer).approve(pool.address, MAX_UINT_AMOUNT);
await usdc.connect(user1.signer).approve(pool.address, MAX_UINT_AMOUNT);
await weth.connect(user2.signer).approve(pool.address, MAX_UINT_AMOUNT);
await dai.connect(user1.signer).mint(daiAmount);
await usdc.connect(user1.signer).mint(usdcAmount);
await weth.connect(user2.signer).mint(wethAmount);
await pool.connect(user1.signer).deposit(dai.address, daiAmount, user1.address, 0);
await pool.connect(user1.signer).deposit(usdc.address, usdcAmount, user1.address, 0);
await pool.connect(user2.signer).deposit(weth.address, wethAmount, user2.address, 0);
});
it('Sets the ltv of DAI to 0', async () => {
const {
configurator,
dai,
helpersContract,
users: [],
} = testEnv;
await configurator.configureReserveAsCollateral(dai.address, 0, 8000, 10500);
const ltv = (await helpersContract.getReserveConfigurationData(dai.address)).ltv;
expect(ltv).to.be.equal(0);
});
it('Borrows 0.01 weth', async () => {
const {
pool,
weth,
users: [user1],
} = testEnv;
const borrowedAmount = await convertToCurrencyDecimals(weth.address, "0.01");
pool.connect(user1.signer).borrow(weth.address, borrowedAmount, 1, 0, user1.address);
});
it('Tries to withdraw USDC (revert expected)', async () => {
const {
pool,
usdc,
users: [user1],
} = testEnv;
const withdrawnAmount = await convertToCurrencyDecimals(usdc.address, "1");
await expect(
pool.connect(user1.signer).withdraw(usdc.address, withdrawnAmount, user1.address)
).to.be.revertedWith(VL_LTV_VALIDATION_FAILED);
});
});