mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'master' into fix/73
This commit is contained in:
commit
302a19a1bc
config
contracts
interfaces
lendingpool
libraries/logic
mocks/flashloan
helpers
package.jsontasks/full
test
|
@ -258,4 +258,11 @@ export const CommonsConfig: ICommonConfiguration = {
|
|||
[eEthereumNetwork.ropsten]: '',
|
||||
[eEthereumNetwork.main]: '',
|
||||
},
|
||||
ProxyPriceProvider: {
|
||||
[eEthereumNetwork.coverage]: '',
|
||||
[eEthereumNetwork.buidlerevm]: '',
|
||||
[eEthereumNetwork.kovan]: '0x276C4793F2EE3D5Bf18C5b879529dD4270BA4814',
|
||||
[eEthereumNetwork.ropsten]: '',
|
||||
[eEthereumNetwork.main]: '',
|
||||
},
|
||||
};
|
||||
|
|
|
@ -259,27 +259,6 @@ interface ILendingPool {
|
|||
bool receiveAToken
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev flashes the underlying collateral on an user to swap for the owed asset and repay
|
||||
* - Both the owner of the position and other liquidators can execute it
|
||||
* - The owner can repay with his collateral at any point, no matter the health factor
|
||||
* - Other liquidators can only use this function below 1 HF. To liquidate 50% of the debt > HF 0.98 or the whole below
|
||||
* @param collateral The address of the collateral asset
|
||||
* @param principal The address of the owed asset
|
||||
* @param user Address of the borrower
|
||||
* @param principalAmount Amount of the debt to repay. type(uint256).max to repay the maximum possible
|
||||
* @param receiver Address of the contract receiving the collateral to swap
|
||||
* @param params Variadic bytes param to pass with extra information to the receiver
|
||||
**/
|
||||
function repayWithCollateral(
|
||||
address collateral,
|
||||
address principal,
|
||||
address user,
|
||||
uint256 principalAmount,
|
||||
address receiver,
|
||||
bytes calldata params
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev allows smartcontracts to access the liquidity of the pool within one transaction,
|
||||
* as long as the amount taken plus a fee is returned. NOTE There are security concerns for developers of flashloan receiver contracts
|
||||
|
@ -299,22 +278,6 @@ interface ILendingPool {
|
|||
uint16 referralCode
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev Allows an user to release one of his assets deposited in the protocol, even if it is used as collateral, to swap for another.
|
||||
* - It's not possible to release one asset to swap for the same
|
||||
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the ISwapAdapter interface
|
||||
* @param fromAsset Asset to swap from
|
||||
* @param toAsset Asset to swap to
|
||||
* @param params a bytes array to be sent (if needed) to the receiver contract with extra data
|
||||
**/
|
||||
function swapLiquidity(
|
||||
address receiverAddress,
|
||||
address fromAsset,
|
||||
address toAsset,
|
||||
uint256 amountToSwap,
|
||||
bytes calldata params
|
||||
) external;
|
||||
|
||||
function getUserAccountData(address user)
|
||||
external
|
||||
view
|
||||
|
|
|
@ -482,55 +482,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev flashes the underlying collateral on an user to swap for the owed asset and repay
|
||||
* - Both the owner of the position and other liquidators can execute it
|
||||
* - The owner can repay with his collateral at any point, no matter the health factor
|
||||
* - Other liquidators can only use this function below 1 HF. To liquidate 50% of the debt > HF 0.98 or the whole below
|
||||
* @param collateral The address of the collateral asset
|
||||
* @param principal The address of the owed asset
|
||||
* @param user Address of the borrower
|
||||
* @param principalAmount Amount of the debt to repay. type(uint256).max to repay the maximum possible
|
||||
* @param receiver Address of the contract receiving the collateral to swap
|
||||
* @param params Variadic bytes param to pass with extra information to the receiver
|
||||
**/
|
||||
function repayWithCollateral(
|
||||
address collateral,
|
||||
address principal,
|
||||
address user,
|
||||
uint256 principalAmount,
|
||||
address receiver,
|
||||
bytes calldata params
|
||||
) external override {
|
||||
_whenNotPaused();
|
||||
require(!_flashLiquidationLocked, Errors.REENTRANCY_NOT_ALLOWED);
|
||||
_flashLiquidationLocked = true;
|
||||
|
||||
address collateralManager = _addressesProvider.getLendingPoolCollateralManager();
|
||||
|
||||
//solium-disable-next-line
|
||||
(bool success, bytes memory result) = collateralManager.delegatecall(
|
||||
abi.encodeWithSignature(
|
||||
'repayWithCollateral(address,address,address,uint256,address,bytes)',
|
||||
collateral,
|
||||
principal,
|
||||
user,
|
||||
principalAmount,
|
||||
receiver,
|
||||
params
|
||||
)
|
||||
);
|
||||
require(success, Errors.FAILED_REPAY_WITH_COLLATERAL);
|
||||
|
||||
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
|
||||
|
||||
if (returnCode != 0) {
|
||||
revert(string(abi.encodePacked(returnMessage)));
|
||||
}
|
||||
|
||||
_flashLiquidationLocked = false;
|
||||
}
|
||||
|
||||
struct FlashLoanLocalVars {
|
||||
uint256 premium;
|
||||
uint256 amountPlusPremium;
|
||||
|
@ -609,44 +560,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows an user to release one of his assets deposited in the protocol, even if it is used as collateral, to swap for another.
|
||||
* - It's not possible to release one asset to swap for the same
|
||||
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the ISwapAdapter interface
|
||||
* @param fromAsset Asset to swap from
|
||||
* @param toAsset Asset to swap to
|
||||
* @param params a bytes array to be sent (if needed) to the receiver contract with extra data
|
||||
**/
|
||||
function swapLiquidity(
|
||||
address receiverAddress,
|
||||
address fromAsset,
|
||||
address toAsset,
|
||||
uint256 amountToSwap,
|
||||
bytes calldata params
|
||||
) external override {
|
||||
_whenNotPaused();
|
||||
address collateralManager = _addressesProvider.getLendingPoolCollateralManager();
|
||||
|
||||
//solium-disable-next-line
|
||||
(bool success, bytes memory result) = collateralManager.delegatecall(
|
||||
abi.encodeWithSignature(
|
||||
'swapLiquidity(address,address,address,uint256,bytes)',
|
||||
receiverAddress,
|
||||
fromAsset,
|
||||
toAsset,
|
||||
amountToSwap,
|
||||
params
|
||||
)
|
||||
);
|
||||
require(success, Errors.FAILED_COLLATERAL_SWAP);
|
||||
|
||||
(uint256 returnCode, string memory returnMessage) = abi.decode(result, (uint256, string));
|
||||
|
||||
if (returnCode != 0) {
|
||||
revert(string(abi.encodePacked(returnMessage)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev returns the state and configuration of the reserve
|
||||
* @param asset the address of the reserve
|
||||
|
|
|
@ -58,24 +58,6 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
bool receiveAToken
|
||||
);
|
||||
|
||||
/**
|
||||
@dev emitted when a borrower/liquidator repays with the borrower's collateral
|
||||
@param collateral the address of the collateral being swapped to repay
|
||||
@param principal the address of the reserve of the debt
|
||||
@param user the borrower's address
|
||||
@param liquidator the address of the liquidator, same as the one of the borrower on self-repayment
|
||||
@param principalAmount the amount of the debt finally covered
|
||||
@param swappedCollateralAmount the amount of collateral finally swapped
|
||||
*/
|
||||
event RepaidWithCollateral(
|
||||
address indexed collateral,
|
||||
address indexed principal,
|
||||
address indexed user,
|
||||
address liquidator,
|
||||
uint256 principalAmount,
|
||||
uint256 swappedCollateralAmount
|
||||
);
|
||||
|
||||
struct LiquidationCallLocalVars {
|
||||
uint256 userCollateralBalance;
|
||||
uint256 userStableDebt;
|
||||
|
@ -96,16 +78,6 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
string errorMsg;
|
||||
}
|
||||
|
||||
struct SwapLiquidityLocalVars {
|
||||
uint256 healthFactor;
|
||||
uint256 amountToReceive;
|
||||
uint256 userBalanceBefore;
|
||||
IAToken fromReserveAToken;
|
||||
IAToken toReserveAToken;
|
||||
uint256 errorCode;
|
||||
string errorMsg;
|
||||
}
|
||||
|
||||
struct AvailableCollateralToLiquidateLocalVars {
|
||||
uint256 userCompoundedBorrowBalance;
|
||||
uint256 liquidationBonus;
|
||||
|
@ -189,7 +161,7 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
(
|
||||
vars.maxCollateralToLiquidate,
|
||||
vars.principalAmountNeeded
|
||||
) = calculateAvailableCollateralToLiquidate(
|
||||
) = _calculateAvailableCollateralToLiquidate(
|
||||
collateralReserve,
|
||||
principalReserve,
|
||||
collateral,
|
||||
|
@ -295,259 +267,6 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev flashes the underlying collateral on an user to swap for the owed asset and repay
|
||||
* - Both the owner of the position and other liquidators can execute it.
|
||||
* - The owner can repay with his collateral at any point, no matter the health factor.
|
||||
* - Other liquidators can only use this function below 1 HF. To liquidate 50% of the debt > HF 0.98 or the whole below.
|
||||
* @param collateral The address of the collateral asset.
|
||||
* @param principal The address of the owed asset.
|
||||
* @param user Address of the borrower.
|
||||
* @param principalAmount Amount of the debt to repay.
|
||||
* @param receiver Address of the contract receiving the collateral to swap.
|
||||
* @param params Variadic bytes param to pass with extra information to the receiver
|
||||
**/
|
||||
function repayWithCollateral(
|
||||
address collateral,
|
||||
address principal,
|
||||
address user,
|
||||
uint256 principalAmount,
|
||||
address receiver,
|
||||
bytes calldata params
|
||||
) external returns (uint256, string memory) {
|
||||
ReserveLogic.ReserveData storage collateralReserve = _reserves[collateral];
|
||||
ReserveLogic.ReserveData storage debtReserve = _reserves[principal];
|
||||
UserConfiguration.Map storage userConfig = _usersConfig[user];
|
||||
|
||||
LiquidationCallLocalVars memory vars;
|
||||
|
||||
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
|
||||
user,
|
||||
_reserves,
|
||||
_usersConfig[user],
|
||||
_reservesList,
|
||||
_reservesCount,
|
||||
_addressesProvider.getPriceOracle()
|
||||
);
|
||||
|
||||
(vars.userStableDebt, vars.userVariableDebt) = Helpers.getUserCurrentDebt(user, debtReserve);
|
||||
|
||||
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateRepayWithCollateral(
|
||||
collateralReserve,
|
||||
debtReserve,
|
||||
userConfig,
|
||||
user,
|
||||
vars.healthFactor,
|
||||
vars.userStableDebt,
|
||||
vars.userVariableDebt
|
||||
);
|
||||
|
||||
if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) {
|
||||
return (vars.errorCode, vars.errorMsg);
|
||||
}
|
||||
|
||||
vars.maxPrincipalAmountToLiquidate = vars.userStableDebt.add(vars.userVariableDebt);
|
||||
|
||||
vars.actualAmountToLiquidate = principalAmount > vars.maxPrincipalAmountToLiquidate
|
||||
? vars.maxPrincipalAmountToLiquidate
|
||||
: principalAmount;
|
||||
|
||||
vars.collateralAtoken = IAToken(collateralReserve.aTokenAddress);
|
||||
vars.userCollateralBalance = vars.collateralAtoken.balanceOf(user);
|
||||
|
||||
(
|
||||
vars.maxCollateralToLiquidate,
|
||||
vars.principalAmountNeeded
|
||||
) = calculateAvailableCollateralToLiquidate(
|
||||
collateralReserve,
|
||||
debtReserve,
|
||||
collateral,
|
||||
principal,
|
||||
vars.actualAmountToLiquidate,
|
||||
vars.userCollateralBalance
|
||||
);
|
||||
|
||||
//if principalAmountNeeded < vars.ActualAmountToLiquidate, there isn't enough
|
||||
//of collateral to cover the actual amount that is being liquidated, hence we liquidate
|
||||
//a smaller amount
|
||||
if (vars.principalAmountNeeded < vars.actualAmountToLiquidate) {
|
||||
vars.actualAmountToLiquidate = vars.principalAmountNeeded;
|
||||
}
|
||||
//updating collateral reserve indexes
|
||||
collateralReserve.updateState();
|
||||
|
||||
//updating collateral reserve interest rates
|
||||
collateralReserve.updateInterestRates(
|
||||
collateral,
|
||||
address(vars.collateralAtoken),
|
||||
0,
|
||||
vars.maxCollateralToLiquidate
|
||||
);
|
||||
|
||||
vars.collateralAtoken.burn(
|
||||
user,
|
||||
receiver,
|
||||
vars.maxCollateralToLiquidate,
|
||||
collateralReserve.liquidityIndex
|
||||
);
|
||||
|
||||
if (vars.userCollateralBalance == vars.maxCollateralToLiquidate) {
|
||||
_usersConfig[user].setUsingAsCollateral(collateralReserve.id, false);
|
||||
}
|
||||
|
||||
vars.principalAToken = debtReserve.aTokenAddress;
|
||||
|
||||
// Notifies the receiver to proceed, sending as param the underlying already transferred
|
||||
ISwapAdapter(receiver).executeOperation(
|
||||
collateral,
|
||||
principal,
|
||||
vars.maxCollateralToLiquidate,
|
||||
address(this),
|
||||
params
|
||||
);
|
||||
|
||||
//updating debt reserve
|
||||
debtReserve.updateState();
|
||||
debtReserve.updateInterestRates(
|
||||
principal,
|
||||
vars.principalAToken,
|
||||
vars.actualAmountToLiquidate,
|
||||
0
|
||||
);
|
||||
IERC20(principal).safeTransferFrom(
|
||||
receiver,
|
||||
vars.principalAToken,
|
||||
vars.actualAmountToLiquidate
|
||||
);
|
||||
|
||||
if (vars.userVariableDebt >= vars.actualAmountToLiquidate) {
|
||||
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
|
||||
user,
|
||||
vars.actualAmountToLiquidate,
|
||||
debtReserve.variableBorrowIndex
|
||||
);
|
||||
} else {
|
||||
IVariableDebtToken(debtReserve.variableDebtTokenAddress).burn(
|
||||
user,
|
||||
vars.userVariableDebt,
|
||||
debtReserve.variableBorrowIndex
|
||||
);
|
||||
IStableDebtToken(debtReserve.stableDebtTokenAddress).burn(
|
||||
user,
|
||||
vars.actualAmountToLiquidate.sub(vars.userVariableDebt)
|
||||
);
|
||||
}
|
||||
|
||||
emit RepaidWithCollateral(
|
||||
collateral,
|
||||
principal,
|
||||
user,
|
||||
msg.sender,
|
||||
vars.actualAmountToLiquidate,
|
||||
vars.maxCollateralToLiquidate
|
||||
);
|
||||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Allows an user to release one of his assets deposited in the protocol, even if it is used as collateral, to swap for another.
|
||||
* - It's not possible to release one asset to swap for the same
|
||||
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the ISwapAdapter interface
|
||||
* @param fromAsset Asset to swap from
|
||||
* @param toAsset Asset to swap to
|
||||
* @param params a bytes array to be sent (if needed) to the receiver contract with extra data
|
||||
**/
|
||||
function swapLiquidity(
|
||||
address receiverAddress,
|
||||
address fromAsset,
|
||||
address toAsset,
|
||||
uint256 amountToSwap,
|
||||
bytes calldata params
|
||||
) external returns (uint256, string memory) {
|
||||
ReserveLogic.ReserveData storage fromReserve = _reserves[fromAsset];
|
||||
ReserveLogic.ReserveData storage toReserve = _reserves[toAsset];
|
||||
|
||||
SwapLiquidityLocalVars memory vars;
|
||||
|
||||
(vars.errorCode, vars.errorMsg) = ValidationLogic.validateSwapLiquidity(
|
||||
fromReserve,
|
||||
toReserve,
|
||||
fromAsset,
|
||||
toAsset
|
||||
);
|
||||
|
||||
if (Errors.CollateralManagerErrors(vars.errorCode) != Errors.CollateralManagerErrors.NO_ERROR) {
|
||||
return (vars.errorCode, vars.errorMsg);
|
||||
}
|
||||
|
||||
vars.fromReserveAToken = IAToken(fromReserve.aTokenAddress);
|
||||
vars.toReserveAToken = IAToken(toReserve.aTokenAddress);
|
||||
|
||||
fromReserve.updateState();
|
||||
toReserve.updateState();
|
||||
|
||||
if (vars.fromReserveAToken.balanceOf(msg.sender) == amountToSwap) {
|
||||
_usersConfig[msg.sender].setUsingAsCollateral(fromReserve.id, false);
|
||||
}
|
||||
|
||||
fromReserve.updateInterestRates(fromAsset, address(vars.fromReserveAToken), 0, amountToSwap);
|
||||
|
||||
vars.fromReserveAToken.burn(
|
||||
msg.sender,
|
||||
receiverAddress,
|
||||
amountToSwap,
|
||||
fromReserve.liquidityIndex
|
||||
);
|
||||
// Notifies the receiver to proceed, sending as param the underlying already transferred
|
||||
ISwapAdapter(receiverAddress).executeOperation(
|
||||
fromAsset,
|
||||
toAsset,
|
||||
amountToSwap,
|
||||
address(this),
|
||||
params
|
||||
);
|
||||
|
||||
vars.amountToReceive = IERC20(toAsset).balanceOf(receiverAddress);
|
||||
if (vars.amountToReceive != 0) {
|
||||
IERC20(toAsset).safeTransferFrom(
|
||||
receiverAddress,
|
||||
address(vars.toReserveAToken),
|
||||
vars.amountToReceive
|
||||
);
|
||||
|
||||
if (vars.toReserveAToken.balanceOf(msg.sender) == 0) {
|
||||
_usersConfig[msg.sender].setUsingAsCollateral(toReserve.id, true);
|
||||
}
|
||||
|
||||
vars.toReserveAToken.mint(msg.sender, vars.amountToReceive, toReserve.liquidityIndex);
|
||||
toReserve.updateInterestRates(
|
||||
toAsset,
|
||||
address(vars.toReserveAToken),
|
||||
vars.amountToReceive,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
(, , , , vars.healthFactor) = GenericLogic.calculateUserAccountData(
|
||||
msg.sender,
|
||||
_reserves,
|
||||
_usersConfig[msg.sender],
|
||||
_reservesList,
|
||||
_reservesCount,
|
||||
_addressesProvider.getPriceOracle()
|
||||
);
|
||||
|
||||
if (vars.healthFactor < GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD),
|
||||
Errors.HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD
|
||||
);
|
||||
}
|
||||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev calculates how much of a specific collateral can be liquidated, given
|
||||
* a certain amount of principal currency. This function needs to be called after
|
||||
|
@ -559,7 +278,7 @@ contract LendingPoolCollateralManager is VersionedInitializable, LendingPoolStor
|
|||
* @return collateralAmount the maximum amount that is possible to liquidated given all the liquidation constraints (user balance, close factor)
|
||||
* @return principalAmountNeeded the purchase amount
|
||||
**/
|
||||
function calculateAvailableCollateralToLiquidate(
|
||||
function _calculateAvailableCollateralToLiquidate(
|
||||
ReserveLogic.ReserveData storage collateralReserve,
|
||||
ReserveLogic.ReserveData storage principalReserve,
|
||||
address collateralAddress,
|
||||
|
|
|
@ -384,95 +384,4 @@ library ValidationLogic {
|
|||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Validates the repayWithCollateral() action
|
||||
* @param collateralReserve The reserve data of the collateral
|
||||
* @param principalReserve The reserve data of the principal
|
||||
* @param userConfig The user configuration
|
||||
* @param user The address of the user
|
||||
* @param userHealthFactor The user's health factor
|
||||
* @param userStableDebt Total stable debt balance of the user
|
||||
* @param userVariableDebt Total variable debt balance of the user
|
||||
**/
|
||||
function validateRepayWithCollateral(
|
||||
ReserveLogic.ReserveData storage collateralReserve,
|
||||
ReserveLogic.ReserveData storage principalReserve,
|
||||
UserConfiguration.Map storage userConfig,
|
||||
address user,
|
||||
uint256 userHealthFactor,
|
||||
uint256 userStableDebt,
|
||||
uint256 userVariableDebt
|
||||
) internal view returns (uint256, string memory) {
|
||||
if (
|
||||
!collateralReserve.configuration.getActive() || !principalReserve.configuration.getActive()
|
||||
) {
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), Errors.NO_ACTIVE_RESERVE);
|
||||
}
|
||||
|
||||
if (
|
||||
msg.sender != user && userHealthFactor >= GenericLogic.HEALTH_FACTOR_LIQUIDATION_THRESHOLD
|
||||
) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.HEALTH_FACTOR_ABOVE_THRESHOLD),
|
||||
Errors.HEALTH_FACTOR_NOT_BELOW_THRESHOLD
|
||||
);
|
||||
}
|
||||
|
||||
if (msg.sender != user) {
|
||||
bool isCollateralEnabled = collateralReserve.configuration.getLiquidationThreshold() > 0 &&
|
||||
userConfig.isUsingAsCollateral(collateralReserve.id);
|
||||
|
||||
//if collateral isn't enabled as collateral by user, it cannot be liquidated
|
||||
if (!isCollateralEnabled) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.COLLATERAL_CANNOT_BE_LIQUIDATED),
|
||||
Errors.COLLATERAL_CANNOT_BE_LIQUIDATED
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (userStableDebt == 0 && userVariableDebt == 0) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.CURRRENCY_NOT_BORROWED),
|
||||
Errors.SPECIFIED_CURRENCY_NOT_BORROWED_BY_USER
|
||||
);
|
||||
}
|
||||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Validates the swapLiquidity() action
|
||||
* @param fromReserve The reserve data of the asset to swap from
|
||||
* @param toReserve The reserve data of the asset to swap to
|
||||
* @param fromAsset Address of the asset to swap from
|
||||
* @param toAsset Address of the asset to swap to
|
||||
**/
|
||||
function validateSwapLiquidity(
|
||||
ReserveLogic.ReserveData storage fromReserve,
|
||||
ReserveLogic.ReserveData storage toReserve,
|
||||
address fromAsset,
|
||||
address toAsset
|
||||
) internal view returns (uint256, string memory) {
|
||||
if (fromAsset == toAsset) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.INVALID_EQUAL_ASSETS_TO_SWAP),
|
||||
Errors.INVALID_EQUAL_ASSETS_TO_SWAP
|
||||
);
|
||||
}
|
||||
|
||||
(bool isToActive, bool isToFreezed, , ) = toReserve.configuration.getFlags();
|
||||
if (!fromReserve.configuration.getActive() || !isToActive) {
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ACTIVE_RESERVE), Errors.NO_ACTIVE_RESERVE);
|
||||
}
|
||||
if (isToFreezed) {
|
||||
return (
|
||||
uint256(Errors.CollateralManagerErrors.NO_UNFREEZED_RESERVE),
|
||||
Errors.NO_UNFREEZED_RESERVE
|
||||
);
|
||||
}
|
||||
|
||||
return (uint256(Errors.CollateralManagerErrors.NO_ERROR), Errors.NO_ERRORS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
import {MintableERC20} from '../tokens/MintableERC20.sol';
|
||||
import {ILendingPoolAddressesProvider} from '../../interfaces/ILendingPoolAddressesProvider.sol';
|
||||
import {ISwapAdapter} from '../../interfaces/ISwapAdapter.sol';
|
||||
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
|
||||
import {IERC20} from '../../dependencies/openzeppelin/contracts/IERC20.sol';
|
||||
|
||||
contract MockSwapAdapter is ISwapAdapter {
|
||||
uint256 internal _amountToReturn;
|
||||
bool internal _tryReentrancy;
|
||||
ILendingPoolAddressesProvider public addressesProvider;
|
||||
|
||||
event Swapped(address fromAsset, address toAsset, uint256 fromAmount, uint256 receivedAmount);
|
||||
|
||||
constructor(ILendingPoolAddressesProvider provider) public {
|
||||
addressesProvider = provider;
|
||||
}
|
||||
|
||||
function setAmountToReturn(uint256 amount) public {
|
||||
_amountToReturn = amount;
|
||||
}
|
||||
|
||||
function setTryReentrancy(bool tryReentrancy) public {
|
||||
_tryReentrancy = tryReentrancy;
|
||||
}
|
||||
|
||||
function executeOperation(
|
||||
address assetToSwapFrom,
|
||||
address assetToSwapTo,
|
||||
uint256 amountToSwap,
|
||||
address fundsDestination,
|
||||
bytes calldata params
|
||||
) external override {
|
||||
params;
|
||||
IERC20(assetToSwapFrom).transfer(address(1), amountToSwap); // We don't want to keep funds here
|
||||
MintableERC20(assetToSwapTo).mint(_amountToReturn);
|
||||
IERC20(assetToSwapTo).approve(fundsDestination, _amountToReturn);
|
||||
|
||||
if (_tryReentrancy) {
|
||||
ILendingPool(fundsDestination).repayWithCollateral(
|
||||
assetToSwapFrom,
|
||||
assetToSwapTo,
|
||||
address(1), // Doesn't matter, we just want to test the reentrancy
|
||||
1 ether, // Same
|
||||
address(1), // Same
|
||||
'0x'
|
||||
);
|
||||
}
|
||||
|
||||
emit Swapped(assetToSwapFrom, assetToSwapTo, amountToSwap, _amountToReturn);
|
||||
}
|
||||
|
||||
function burnAsset(IERC20 asset, uint256 amount) public {
|
||||
uint256 amountToBurn = (amount == type(uint256).max) ? asset.balanceOf(address(this)) : amount;
|
||||
asset.transfer(address(0), amountToBurn);
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xAA6DfC2A802857Fadb75726B6166484e2c011cf5",
|
||||
"address": "0xDFbeeed692AA81E7f86E72F7ACbEA2A1C4d63544",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -19,7 +19,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xFd23fD3d937ae73a7b545B8Bfeb218395bDe9b8f",
|
||||
"address": "0x5191aA68c7dB195181Dd2441dBE23A48EA24b040",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -27,8 +27,8 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x20e080B395341B3b617E893c281c7E999C942276",
|
||||
"deployer": "0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F"
|
||||
"address": "0xd7e3C4b2CE495066dE1923c268D68A844bD7Ae13",
|
||||
"deployer": "0x6b40a028d2Ab94e5f6d3793F32D326CDf724Bb1D"
|
||||
}
|
||||
},
|
||||
"LendingPoolAddressesProviderRegistry": {
|
||||
|
@ -37,7 +37,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xE567BA007c79D491D585015c30c73BA7FfDAFaF0",
|
||||
"address": "0xa89E20284Bd638F31b0011D0fC754Fc9d2fa73e3",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -45,8 +45,8 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x00219a2958f758122106Bb8A801AFc1B70897663",
|
||||
"deployer": "0x85e4A467343c0dc4aDAB74Af84448D9c45D8ae6F"
|
||||
"address": "0x83c7A0E78e8eee2108a87d7a6770f22BAcb68b5A",
|
||||
"deployer": "0x6b40a028d2Ab94e5f6d3793F32D326CDf724Bb1D"
|
||||
}
|
||||
},
|
||||
"FeeProvider": {
|
||||
|
@ -73,10 +73,10 @@
|
|||
"address": "0x6642B57e4265BAD868C17Fc1d1F4F88DBBA04Aa8"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x81228BFb2fE916C0fF76bF4e12c165321BdF1CFB"
|
||||
"address": "0x9Ec55627757348b322c8dD0865D704649bFa0c7b"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x50C9d3aD9399c1EEf6DDeadF8e57fF69994F552e"
|
||||
"address": "0x1339f3c1FfF00D0FD8946187fdC61F0ef0fFe786"
|
||||
}
|
||||
},
|
||||
"LendingPoolDataProvider": {
|
||||
|
@ -89,10 +89,10 @@
|
|||
"address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xE2302ab9A6754EfaA8Ea55eECA668bCE72d8a4FE"
|
||||
"address": "0x3EE716e38f21e5FC16BFDB773db24D63C637A5d8"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x6d1e69bB0578699dd955Eefbf23aAC65c0DA5cE7"
|
||||
"address": "0xB43CCfF1148bb5ab2104E2ee68A7c30cDEBb9A9C"
|
||||
}
|
||||
},
|
||||
"PriceOracle": {
|
||||
|
@ -101,7 +101,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x8720da7Bc69d35800937CD0CB2a88517Ab681a34",
|
||||
"address": "0x5889354f21A1C8D8D2f82669d778f6Dab778B519",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -115,7 +115,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x3F1976472C22fb2bC05Ff74CF3d03E52b0FDD6b8",
|
||||
"address": "0xC452C5244F701108B4e8E8BCe693160046b30332",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -129,7 +129,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x804E98F470694629b5005b3B8f092B0188B2A770",
|
||||
"address": "0x0B63c002cb44B2e5e580C3B3560a27F4101D95c0",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -147,7 +147,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xdaE51d2e6C77C5EC035Ab3B2a8eEF552F74035d0",
|
||||
"address": "0xCeB290A2C6614BF23B2faa0f0B8067F29C48DB0F",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -165,12 +165,16 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x4e92ed34740Ef54325D0382BeA1F433374e92593",
|
||||
"address": "0x7C95b1ad025F0C9aB14192f87bF2aD53889bE4F7",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x626FdE749F9d499d3777320CAf29484B624ab84a",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0xc4e3d83AEd3D3c60Cf4b238F634014cE103F6fa1",
|
||||
"deployer": "0x6b40a028d2Ab94e5f6d3793F32D326CDf724Bb1D"
|
||||
}
|
||||
},
|
||||
"LendingPoolLiquidationManager": {
|
||||
|
@ -223,7 +227,7 @@
|
|||
"address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xaFc2D7E7f6915c053E257200D10BF53daCD37206"
|
||||
"address": "0x9c91aEaD98b1354C7B0EAfb8ff539d0796c79894"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x2B681757d757fbB80cc51c6094cEF5eE75bF55aA"
|
||||
|
@ -235,7 +239,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x917873BDf56b8404b984e1D0946A9791fB8723ff",
|
||||
"address": "0x145b7B6368Df63e7F3497b0A948B30fC1A4d5E55",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -249,7 +253,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xda8D610DBF58CdbBd9FF63Cfd5d152d6aC03D827",
|
||||
"address": "0x010e948B9B7D30771E23346C0B17a4D5Ff04e300",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -263,7 +267,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x230063F7a99684eCB09C0100B46e4ef52F790787",
|
||||
"address": "0x79094eDB848047e87a4B8a64ab5Ee2f527791bC0",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -277,7 +281,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x9357981b57000D78aE088D85D8B93aabd971E44c",
|
||||
"address": "0xEE0A69d0Bb1312685870Dd7E20AcAD66b6f6264F",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -291,7 +295,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x347aB2276e45528d0869d98454dB85d0353289cc",
|
||||
"address": "0x631B367fBE1dbB934bC039aAA0C9eC2EE5943fd5",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -305,7 +309,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x2120a59e1800077C8dd0a60bB36315282d3AD73d",
|
||||
"address": "0xf55Af78B3f3059fACF166Aa338FFe059A14e75F6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -319,7 +323,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xAD8b260d4a55fa27D9611E32029b74CE4fBb43B7",
|
||||
"address": "0xD5A0587aAEB195028909E98930B391dFB3f9F589",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -333,7 +337,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x15556f286eA3dc789b31d71058F236d3f84a8CdC",
|
||||
"address": "0xaD3AdbC18E4AD090034A6C74Eda61f4310dce313",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -347,7 +351,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xc1D388F1b4a9448cC88249020D3Bb82588496aDD",
|
||||
"address": "0x25a88BbA9c8D2a46e3Ff4bFe98712DF7A1044fB6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -361,7 +365,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x8AA4ef0B7069333d32e1a301c3e8867050Ce7dd4",
|
||||
"address": "0x16d1802cd7cfcb67955BBBa26bAae1cE559B5F5B",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -375,7 +379,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xFB2DB49aEfdfb4A1443D369F9707BF07EC200609",
|
||||
"address": "0xE58d8c88f5A670f16BE8F7864707170F43e943A6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -389,7 +393,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x231926CDdAf87b45382127E83b371D81dd3071dE",
|
||||
"address": "0xfdAF4f6e47e854c05bE158993d32872e784F0502",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -403,7 +407,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x3DBE70A3bE31C9Bd7EeE42F2d89e6D8BdbB60d9D",
|
||||
"address": "0x92edC13A10036A3C50396f2B63148a3e9a8D589e",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -417,7 +421,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x17CcC196687feD7693580e5C327C16623c1422aA",
|
||||
"address": "0xE5C277cDb7E10372918Ac54Ce54022910A24FE88",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -431,7 +435,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xa3C9636e8A83e9A20F17d6101D8f5fC19cFa5cF1",
|
||||
"address": "0xF5742a599a0F4520089cbf2EBBa66Bb4F471B85F",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -445,7 +449,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x7b3e185D7Dd72CB6a8e4f3b77f2afa9cc2986702",
|
||||
"address": "0x380EF388e13D8cAdeACef6eF682C7B7D85865076",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -459,7 +463,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x582e9540A4FCA6aF23543D10e274bFfC6A1C4bd0",
|
||||
"address": "0xC89577DED8441e52C17C13D527b85b225C5c8311",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -473,7 +477,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x1380773F68a96A09181551658ae1F23EA5298339",
|
||||
"address": "0xD4b06774A717Ff5A7c20c8712e31c6BbfFcb1F01",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -487,7 +491,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x0E512D4c480168bB354F9D70EfEe4c2779757Dc7",
|
||||
"address": "0xbe66dC9DFEe580ED968403e35dF7b5159f873df8",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -501,7 +505,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x77607B1E8f05c2D80Ad209368F9d812a79E89D0a",
|
||||
"address": "0x93AfC6Df4bB8F62F2493B19e577f8382c0BA9EBC",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -515,7 +519,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x86089a044275B66A8C01D31B5e234a2c4099DCcA",
|
||||
"address": "0x75Ded61646B5945BdDd0CD9a9Db7c8288DA6F810",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -529,7 +533,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x7B49a4DA95c3d8e3a308D9d550fd735285D2445b",
|
||||
"address": "0xdE7c40e675bF1aA45c18cCbaEb9662B16b0Ddf7E",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -543,7 +547,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xE3e60d4513d7A63a5C4EE91df71e92cfDd97ac89",
|
||||
"address": "0xEcb928A3c079a1696Aa5244779eEc3dE1717fACd",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -557,7 +561,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xAA6DfC2A802857Fadb75726B6166484e2c011cf5",
|
||||
"address": "0xDFbeeed692AA81E7f86E72F7ACbEA2A1C4d63544",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -570,10 +574,14 @@
|
|||
"address": "0xe7536f450378748E1BD4645D3c77ec38e0F3ba28"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xAD4EA7747fF8C3ea98009B016280d3E5A93B71e4"
|
||||
"address": "0x987223924D2DD6c6efB601756850f3886ECbceF6"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x2cfcA5785261fbC88EFFDd46fCFc04c22525F9e4"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0xE4566ce19626826360f4faD941418e2849fC3685",
|
||||
"deployer": "0x6b40a028d2Ab94e5f6d3793F32D326CDf724Bb1D"
|
||||
}
|
||||
},
|
||||
"StableDebtToken": {
|
||||
|
@ -582,12 +590,16 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xc59Ff5B5Ed3F1aEF6e37ec21B5BfFA21bD7fb2D9",
|
||||
"address": "0xaca5aCeB6f44845d07Fd339a51F0bd52Bb3D8D1A",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0xB660Fdd109a95718cB9d20E3A89EE6cE342aDcB6",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x0043967C1Cf13c4Ff3Bc38109054D5a97C147B4A",
|
||||
"deployer": "0x6b40a028d2Ab94e5f6d3793F32D326CDf724Bb1D"
|
||||
}
|
||||
},
|
||||
"VariableDebtToken": {
|
||||
|
@ -596,17 +608,21 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xB6a7e0831d309e3dA99A4D169f0074910f047Dae",
|
||||
"address": "0x9bD0Bec44106D8Ea8fFb6296d7A84742a290E064",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
"address": "0x830bceA96E56DBC1F8578f75fBaC0AF16B32A07d",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0xdF75B68c75c30D177f4Dbd47cBcb5E2E4f3cf8F9",
|
||||
"deployer": "0x6b40a028d2Ab94e5f6d3793F32D326CDf724Bb1D"
|
||||
}
|
||||
},
|
||||
"AToken": {
|
||||
"localhost": {
|
||||
"address": "0x094D1D9DbA786f0cb1269e5Ddb3EfeB0d12d20c5",
|
||||
"address": "0x00f126cCA2266bFb634Ed6DB17c4C74fb8cA5177",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"buidlerevm": {
|
||||
|
@ -616,6 +632,10 @@
|
|||
"coverage": {
|
||||
"address": "0xA0AB1cB92A4AF81f84dCd258155B5c25D247b54E",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"kovan": {
|
||||
"address": "0x1A23ADa7218e0a66b7368E12E379Ea88d7a68a27",
|
||||
"deployer": "0x6b40a028d2Ab94e5f6d3793F32D326CDf724Bb1D"
|
||||
}
|
||||
},
|
||||
"MockAToken": {
|
||||
|
@ -624,7 +644,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x5DbA865405aAEFe5bf9b77a877EA3956C92E309b",
|
||||
"address": "0xbF538F34cb100bAeEE55aa1F036D33F03b03d900",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -638,7 +658,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0xdA43fC20d88c77FdebE172b960f8eb1853a0A46F",
|
||||
"address": "0xff1B1B810F5DCe853a9b1819DE220D532D1CFeF2",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -652,7 +672,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x7AE7D25Ce30A48FED0bf4A5e86B46829EA73C69a",
|
||||
"address": "0x7436d6adaA697413F00cb63E1A2A854bF2Aec5A1",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -666,7 +686,7 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x031F94d0E0B894aa4458E3d34ceA141103641CDC",
|
||||
"address": "0x2A7BE996B8801ED21f2f45148791D402811A2106",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
},
|
||||
"coverage": {
|
||||
|
@ -682,7 +702,7 @@
|
|||
"address": "0xBEF0d4b9c089a5883741fC14cbA352055f35DDA2"
|
||||
},
|
||||
"localhost": {
|
||||
"address": "0x48AbD2dBBbaA7466313963ea17804B3cBC2cA71f"
|
||||
"address": "0x48FAde2E719B770E1783d03466dAEe98b5183538"
|
||||
}
|
||||
},
|
||||
"MockFlashRepayAdapter": {
|
||||
|
|
|
@ -345,6 +345,7 @@ export interface ICommonConfiguration {
|
|||
ReserveAssets: iParamsPerNetwork<SymbolMap<tEthereumAddress>>;
|
||||
ReservesConfig: iMultiPoolsAssets<IReserveParams>;
|
||||
ATokenDomainSeparator: iParamsPerNetwork<string>;
|
||||
ProxyPriceProvider: iParamsPerNetwork<tEthereumAddress>;
|
||||
}
|
||||
|
||||
export interface IAaveConfiguration extends ICommonConfiguration {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
"aave:evm:dev:migration": "buidler aave:dev",
|
||||
"aave:evm:full:migration": "buidler aave:full",
|
||||
"aave:kovan:dev:migration": "npm run buidler:kovan -- aave:dev --verify",
|
||||
"aave:kovan:full:migration": "npm run buidler:kovan -- aave:full --verify",
|
||||
"aave:kovan:full:migration": "npm run buidler:kovan -- aave:full",
|
||||
"aave:ropsten:dev:migration": "npm run buidler:ropsten -- aave:dev --verify",
|
||||
"aave:ropsten:full:migration": "npm run buidler:ropsten -- aave:full --verify",
|
||||
"aave:main:dev:migration": "npm run buidler:main -- aave:dev --verify",
|
||||
|
|
|
@ -37,4 +37,11 @@ task(
|
|||
ProviderId
|
||||
)
|
||||
);
|
||||
|
||||
//register the proxy price provider on the addressesProvider
|
||||
const proxyProvider = getParamPerNetwork(poolConfig.ProxyPriceProvider, network);
|
||||
|
||||
if (proxyProvider && proxyProvider !== '') {
|
||||
await waitForTx(await addressesProvider.setPriceOracle(proxyProvider));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,258 +0,0 @@
|
|||
import {makeSuite, TestEnv} from './helpers/make-suite';
|
||||
import {MockSwapAdapter} from '../types/MockSwapAdapter';
|
||||
import {getMockSwapAdapter} from '../helpers/contracts-helpers';
|
||||
import {ProtocolErrors} from '../helpers/types';
|
||||
import {ethers} from 'ethers';
|
||||
import {APPROVAL_AMOUNT_LENDING_POOL} from '../helpers/constants';
|
||||
import {getContractsData, getTxCostAndTimestamp} from './helpers/actions';
|
||||
import {calcExpectedATokenBalance} from './helpers/utils/calculations';
|
||||
import {waitForTx} from '../helpers/misc-utils';
|
||||
import {advanceBlock, timeLatest} from '../helpers/misc-utils';
|
||||
|
||||
const {expect} = require('chai');
|
||||
|
||||
makeSuite('LendingPool SwapDeposit function', (testEnv: TestEnv) => {
|
||||
let _mockSwapAdapter = {} as MockSwapAdapter;
|
||||
const {
|
||||
HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD,
|
||||
NO_UNFREEZED_RESERVE,
|
||||
NO_ACTIVE_RESERVE,
|
||||
INVALID_EQUAL_ASSETS_TO_SWAP,
|
||||
} = ProtocolErrors;
|
||||
|
||||
before(async () => {
|
||||
_mockSwapAdapter = await getMockSwapAdapter();
|
||||
});
|
||||
|
||||
it('Should not allow to swap if from equal to', async () => {
|
||||
const {pool, weth} = testEnv;
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
weth.address,
|
||||
'1'.toString(),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith(INVALID_EQUAL_ASSETS_TO_SWAP);
|
||||
});
|
||||
|
||||
it('Should not allow to swap if from or to reserves are not active', async () => {
|
||||
const {pool, weth, dai, configurator} = testEnv;
|
||||
|
||||
await configurator.deactivateReserve(weth.address);
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
'1'.toString(),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith(NO_ACTIVE_RESERVE);
|
||||
await configurator.activateReserve(weth.address);
|
||||
|
||||
await configurator.deactivateReserve(dai.address);
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
'1'.toString(),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith(NO_ACTIVE_RESERVE);
|
||||
|
||||
//cleanup state
|
||||
await configurator.activateReserve(dai.address);
|
||||
});
|
||||
|
||||
it('Deposits WETH into the reserve', async () => {
|
||||
const {pool, weth, users} = testEnv;
|
||||
const amountToDeposit = ethers.utils.parseEther('1');
|
||||
|
||||
for (const signer of [weth.signer, users[2].signer]) {
|
||||
const connectedWETH = weth.connect(signer);
|
||||
await connectedWETH.mint(amountToDeposit);
|
||||
await connectedWETH.approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool
|
||||
.connect(signer)
|
||||
.deposit(weth.address, amountToDeposit, await signer.getAddress(), '0');
|
||||
}
|
||||
});
|
||||
|
||||
it('User tries to swap more then he can, revert expected', async () => {
|
||||
const {pool, weth, dai} = testEnv;
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
ethers.utils.parseEther('1.1'),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith('55');
|
||||
});
|
||||
|
||||
it('User tries to swap more then available on the reserve', async () => {
|
||||
const {pool, weth, dai, users, aEth, deployer} = testEnv;
|
||||
|
||||
await pool.borrow(weth.address, ethers.utils.parseEther('0.1'), 1, 0, deployer.address);
|
||||
await pool.connect(users[2].signer).withdraw(weth.address, ethers.utils.parseEther('1'));
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
ethers.utils.parseEther('1'),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith('55');
|
||||
});
|
||||
|
||||
it('User tries to swap correct amount', async () => {
|
||||
const {pool, weth, dai, aEth, aDai, helpersContract} = testEnv;
|
||||
const userAddress = await pool.signer.getAddress();
|
||||
const amountToSwap = ethers.utils.parseEther('0.25');
|
||||
|
||||
const amountToReturn = ethers.utils.parseEther('0.5');
|
||||
await _mockSwapAdapter.setAmountToReturn(amountToReturn);
|
||||
|
||||
const {
|
||||
reserveData: wethReserveDataBefore,
|
||||
userData: wethUserDataBefore,
|
||||
} = await getContractsData(weth.address, userAddress, testEnv);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
userAddress,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const reserveBalanceWETHBefore = await weth.balanceOf(aEth.address);
|
||||
const reserveBalanceDAIBefore = await dai.balanceOf(aDai.address);
|
||||
|
||||
const txReceipt = await waitForTx(
|
||||
await pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
amountToSwap,
|
||||
'0x10'
|
||||
)
|
||||
);
|
||||
const {txTimestamp} = await getTxCostAndTimestamp(txReceipt);
|
||||
const userATokenBalanceWETHAfter = await aEth.balanceOf(userAddress);
|
||||
const userATokenBalanceDAIAfter = await aDai.balanceOf(userAddress);
|
||||
|
||||
const reserveBalanceWETHAfter = await weth.balanceOf(aEth.address);
|
||||
const reserveBalanceDAIAfter = await dai.balanceOf(aDai.address);
|
||||
|
||||
expect(userATokenBalanceWETHAfter.toString()).to.be.equal(
|
||||
calcExpectedATokenBalance(wethReserveDataBefore, wethUserDataBefore, txTimestamp)
|
||||
.minus(amountToSwap.toString())
|
||||
.toString(),
|
||||
'was burned incorrect amount of user funds'
|
||||
);
|
||||
expect(userATokenBalanceDAIAfter.toString()).to.be.equal(
|
||||
calcExpectedATokenBalance(daiReserveDataBefore, daiUserDataBefore, txTimestamp)
|
||||
.plus(amountToReturn.toString())
|
||||
.toString(),
|
||||
'was minted incorrect amount of user funds'
|
||||
);
|
||||
|
||||
expect(reserveBalanceWETHAfter.toString()).to.be.equal(
|
||||
reserveBalanceWETHBefore.sub(amountToSwap).toString(),
|
||||
'was sent incorrect amount if reserve funds'
|
||||
);
|
||||
expect(reserveBalanceDAIAfter.toString()).to.be.equal(
|
||||
reserveBalanceDAIBefore.add(amountToReturn).toString(),
|
||||
'was received incorrect amount if reserve funds'
|
||||
);
|
||||
expect(
|
||||
(await helpersContract.getUserReserveData(dai.address, userAddress)).usageAsCollateralEnabled
|
||||
).to.be.equal(true, 'usage as collateral was not enabled on destination reserve for the user');
|
||||
});
|
||||
|
||||
it('User tries to drop HF below one', async () => {
|
||||
const {pool, weth, dai, deployer} = testEnv;
|
||||
const amountToSwap = ethers.utils.parseEther('0.3');
|
||||
|
||||
const amountToReturn = ethers.utils.parseEther('0.5');
|
||||
await _mockSwapAdapter.setAmountToReturn(amountToReturn);
|
||||
|
||||
await pool.borrow(weth.address, ethers.utils.parseEther('0.3'), 1, 0, deployer.address);
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(_mockSwapAdapter.address, weth.address, dai.address, amountToSwap, '0x10')
|
||||
).to.be.revertedWith(HEALTH_FACTOR_LOWER_THAN_LIQUIDATION_THRESHOLD);
|
||||
});
|
||||
|
||||
it('Should set usage as collateral to false if no leftovers after swap', async () => {
|
||||
const {pool, weth, dai, users} = testEnv;
|
||||
const userAddress = await pool.signer.getAddress();
|
||||
|
||||
// add more liquidity to allow user 0 to swap everything he has
|
||||
await weth.connect(users[2].signer).mint(ethers.utils.parseEther('1'));
|
||||
await pool
|
||||
.connect(users[2].signer)
|
||||
.deposit(weth.address, ethers.utils.parseEther('1'), users[2].address, '0');
|
||||
|
||||
// cleanup borrowings, to be abe to swap whole weth
|
||||
const amountToRepay = ethers.utils.parseEther('0.5');
|
||||
await weth.mint(amountToRepay);
|
||||
await pool.repay(weth.address, amountToRepay, '1', userAddress);
|
||||
const txTimestamp = (await timeLatest()).plus(100);
|
||||
|
||||
const {
|
||||
reserveData: wethReserveDataBefore,
|
||||
userData: wethUserDataBefore,
|
||||
} = await getContractsData(weth.address, userAddress, testEnv);
|
||||
const amountToSwap = calcExpectedATokenBalance(
|
||||
wethReserveDataBefore,
|
||||
wethUserDataBefore,
|
||||
txTimestamp.plus('1')
|
||||
);
|
||||
|
||||
await advanceBlock(txTimestamp.toNumber());
|
||||
|
||||
await pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
amountToSwap.toString(),
|
||||
'0x10'
|
||||
);
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
userAddress,
|
||||
testEnv
|
||||
);
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.equal(
|
||||
false,
|
||||
'usageAsCollateralEnabled are not set to false'
|
||||
);
|
||||
});
|
||||
it('Should not allow to swap if to reserve are freezed', async () => {
|
||||
const {pool, weth, dai, configurator} = testEnv;
|
||||
|
||||
await configurator.freezeReserve(dai.address);
|
||||
|
||||
await expect(
|
||||
pool.swapLiquidity(
|
||||
_mockSwapAdapter.address,
|
||||
weth.address,
|
||||
dai.address,
|
||||
'1'.toString(),
|
||||
'0x10'
|
||||
)
|
||||
).to.be.revertedWith(NO_UNFREEZED_RESERVE);
|
||||
|
||||
//cleanup state
|
||||
await configurator.unfreezeReserve(dai.address);
|
||||
});
|
||||
});
|
|
@ -1,932 +0,0 @@
|
|||
import {TestEnv, makeSuite} from './helpers/make-suite';
|
||||
import {APPROVAL_AMOUNT_LENDING_POOL, oneEther} from '../helpers/constants';
|
||||
import {ethers} from 'ethers';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import {
|
||||
calcExpectedVariableDebtTokenBalance,
|
||||
calcExpectedStableDebtTokenBalance,
|
||||
} from './helpers/utils/calculations';
|
||||
import {getContractsData} from './helpers/actions';
|
||||
import {timeLatest, BRE, increaseTime, waitForTx} from '../helpers/misc-utils';
|
||||
import {ProtocolErrors} from '../helpers/types';
|
||||
import {convertToCurrencyDecimals} from '../helpers/contracts-helpers';
|
||||
import {expectRepayWithCollateralEvent} from './repay-with-collateral.spec';
|
||||
|
||||
const {expect} = require('chai');
|
||||
const {parseUnits, parseEther} = ethers.utils;
|
||||
|
||||
makeSuite('LendingPool. repayWithCollateral() with liquidator', (testEnv: TestEnv) => {
|
||||
const {INVALID_HF, COLLATERAL_CANNOT_BE_LIQUIDATED, IS_PAUSED} = ProtocolErrors;
|
||||
|
||||
it('User 1 provides some liquidity for others to borrow', async () => {
|
||||
const {pool, weth, dai, usdc, deployer} = testEnv;
|
||||
|
||||
await weth.mint(parseEther('200'));
|
||||
await weth.approve(pool.address, parseEther('200'));
|
||||
await pool.deposit(weth.address, parseEther('200'), deployer.address, 0);
|
||||
await dai.mint(parseEther('20000'));
|
||||
await dai.approve(pool.address, parseEther('20000'));
|
||||
await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0);
|
||||
await usdc.mint(parseEther('20000'));
|
||||
await usdc.approve(pool.address, parseEther('20000'));
|
||||
await pool.deposit(usdc.address, parseEther('20000'), deployer.address, 0);
|
||||
});
|
||||
|
||||
it('User 5 liquidate User 3 collateral, all his variable debt and part of the stable', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
const liquidator = users[4];
|
||||
const amountToDeposit = parseEther('20');
|
||||
const amountToBorrow = parseUnits('40', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, amountToDeposit);
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 1, 0, user.address);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
// Set HF below 1
|
||||
await oracle.setAssetPrice(
|
||||
usdc.address,
|
||||
new BigNumber(usdcPrice.toString()).multipliedBy(60).toFixed(0)
|
||||
);
|
||||
const userGlobalDataPrior = await pool.getUserAccountData(user.address);
|
||||
expect(userGlobalDataPrior.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
const amountToRepay = parseUnits('80', 6);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
const txReceipt = await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
const expectedStableDebtIncrease = calcExpectedStableDebtTokenBalance(
|
||||
usdcUserDataBefore.principalStableDebt,
|
||||
usdcUserDataBefore.stableBorrowRate,
|
||||
usdcUserDataBefore.stableRateLastUpdated,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentStableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.equal(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.gte(0)
|
||||
? new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
: '0',
|
||||
'INVALID_VARIABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
const stableDebtRepaid = new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.abs();
|
||||
|
||||
expect(usdcUserDataAfter.currentStableDebt).to.be.bignumber.equal(
|
||||
new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||
.minus(stableDebtRepaid)
|
||||
.plus(expectedStableDebtIncrease)
|
||||
.gte(0)
|
||||
? new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||
.minus(stableDebtRepaid)
|
||||
.plus(expectedStableDebtIncrease)
|
||||
.toString()
|
||||
: '0',
|
||||
'INVALID_STABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
const eventsEmitted = txReceipt.events || [];
|
||||
|
||||
expectRepayWithCollateralEvent(
|
||||
eventsEmitted,
|
||||
pool.address,
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address
|
||||
);
|
||||
// Resets USDC Price
|
||||
await oracle.setAssetPrice(usdc.address, usdcPrice);
|
||||
});
|
||||
|
||||
it('User 3 deposits WETH and borrows USDC at Variable', async () => {
|
||||
const {pool, weth, usdc, users, oracle} = testEnv;
|
||||
const user = users[2];
|
||||
const amountToDeposit = parseEther('10');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const amountUSDCToBorrow = await convertToCurrencyDecimals(
|
||||
usdc.address,
|
||||
new BigNumber(userGlobalData.availableBorrowsETH.toString())
|
||||
.div(usdcPrice.toString())
|
||||
.multipliedBy(0.95)
|
||||
.toFixed(0)
|
||||
);
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountUSDCToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('User 5 liquidates half the USDC loan of User 3 by swapping his WETH collateral', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
const liquidator = users[4];
|
||||
// Sets USDC Price higher to decrease health factor below 1
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
usdc.address,
|
||||
new BigNumber(usdcPrice.toString()).multipliedBy(1.15).toFixed(0)
|
||||
);
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
const amountToRepay = usdcReserveDataBefore.totalVariableDebt.dividedBy(2).toFixed(0);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.almostEqual(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
|
||||
// Resets USDC Price
|
||||
await oracle.setAssetPrice(usdc.address, usdcPrice);
|
||||
});
|
||||
|
||||
it('Revert expected. User 5 tries to liquidate an User 3 collateral a currency he havent borrow', async () => {
|
||||
const {pool, weth, dai, users, oracle, mockSwapAdapter, usdc} = testEnv;
|
||||
const user = users[2];
|
||||
const liquidator = users[4];
|
||||
|
||||
const amountToRepay = parseUnits('10', 6);
|
||||
|
||||
// Sets USDC Price higher to decrease health factor below 1
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
usdc.address,
|
||||
new BigNumber(usdcPrice.toString()).multipliedBy(6.4).toFixed(0)
|
||||
);
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('40');
|
||||
|
||||
await oracle.setAssetPrice(usdc.address, usdcPrice);
|
||||
});
|
||||
|
||||
it('User 5 liquidates all the USDC loan of User 3 by swapping his WETH collateral', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
const liquidator = users[4];
|
||||
// Sets USDC Price higher to decrease health factor below 1
|
||||
const usdcPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
usdc.address,
|
||||
new BigNumber(usdcPrice.toString()).multipliedBy(1.35).toFixed(0)
|
||||
);
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
const amountToRepay = usdcReserveDataBefore.totalVariableDebt.toFixed(0);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
// Resets USDC Price
|
||||
await oracle.setAssetPrice(usdc.address, usdcPrice);
|
||||
});
|
||||
|
||||
it('User 2 deposit WETH and borrows DAI at Variable', async () => {
|
||||
const {pool, weth, dai, users, oracle} = testEnv;
|
||||
const user = users[1];
|
||||
const amountToDeposit = ethers.utils.parseEther('2');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const amountDAIToBorrow = await convertToCurrencyDecimals(
|
||||
dai.address,
|
||||
new BigNumber(userGlobalData.availableBorrowsETH.toString())
|
||||
.div(daiPrice.toString())
|
||||
.multipliedBy(0.9)
|
||||
.toFixed(0)
|
||||
);
|
||||
|
||||
await pool.connect(user.signer).borrow(dai.address, amountDAIToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle} = testEnv;
|
||||
const user = users[1];
|
||||
const liquidator = users[4];
|
||||
|
||||
// Sets DAI Price higher to decrease health factor below 1
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
dai.address,
|
||||
new BigNumber(daiPrice.toString()).multipliedBy(1.4).toFixed(0)
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const amountToRepay = daiReserveDataBefore.totalVariableDebt.toString();
|
||||
|
||||
await waitForTx(await mockSwapAdapter.setTryReentrancy(true));
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('53');
|
||||
|
||||
// Resets DAI Price
|
||||
await oracle.setAssetPrice(dai.address, daiPrice);
|
||||
// Resets mock
|
||||
await waitForTx(await mockSwapAdapter.setTryReentrancy(false));
|
||||
});
|
||||
|
||||
it('User 5 tries to liquidate User 2 DAI Variable loan using his WETH collateral, with good HF', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter} = testEnv;
|
||||
const user = users[1];
|
||||
const liquidator = users[4];
|
||||
|
||||
const {reserveData: daiReserveDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
// First half
|
||||
const amountToRepay = daiReserveDataBefore.totalVariableDebt.dividedBy(2).toString();
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('38');
|
||||
});
|
||||
it('User 5 liquidates User 2 DAI Variable loan using his WETH collateral, half the amount', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[1];
|
||||
const liquidator = users[4];
|
||||
|
||||
// Sets DAI Price higher to decrease health factor below 1
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
dai.address,
|
||||
new BigNumber(daiPrice.toString()).multipliedBy(1.4).toFixed(0)
|
||||
);
|
||||
|
||||
const userGlobalData = await pool.getUserAccountData(user.address);
|
||||
|
||||
expect(userGlobalData.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
// First half
|
||||
const amountToRepay = daiReserveDataBefore.totalVariableDebt
|
||||
.multipliedBy(0.6)
|
||||
.toFixed(0)
|
||||
.toString();
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(dai.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
daiReserveDataBefore,
|
||||
daiUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(daiUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(daiUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(daiUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
)
|
||||
);
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
|
||||
// Resets DAI price
|
||||
await oracle.setAssetPrice(dai.address, daiPrice);
|
||||
});
|
||||
|
||||
it('User 2 tries to repay remaining DAI Variable loan using his WETH collateral', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[1];
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
await increaseTime(1000);
|
||||
// Repay the remaining DAI
|
||||
const amountToRepay = daiReserveDataBefore.totalVariableDebt.toString();
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
const receipt = await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = (await BRE.ethers.provider.getBlock(receipt.blockNumber))
|
||||
.timestamp;
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(dai.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
daiReserveDataBefore,
|
||||
daiUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(daiUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(daiUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(daiUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
);
|
||||
|
||||
expect(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
)
|
||||
).to.be.bignumber.equal(wethUserDataAfter.currentATokenBalance);
|
||||
});
|
||||
|
||||
it('Liquidator tries to repay 4 user a bigger amount that what can be swapped of a particular collateral, repaying only the maximum allowed by that collateral', async () => {
|
||||
const {pool, weth, dai, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[3];
|
||||
const liquidator = users[5];
|
||||
|
||||
const amountToDepositWeth = parseEther('0.1');
|
||||
const amountToDepositDAI = parseEther('500');
|
||||
const amountToBorrowVariable = parseUnits('80', '6');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDepositWeth);
|
||||
await dai.connect(user.signer).mint(amountToDepositDAI);
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
|
||||
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
|
||||
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.borrow(usdc.address, amountToBorrowVariable, 2, 0, user.address);
|
||||
|
||||
const amountToRepay = amountToBorrowVariable;
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
// Set HF below 1
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
await oracle.setAssetPrice(
|
||||
dai.address,
|
||||
new BigNumber(daiPrice.toString()).multipliedBy(0.1).toFixed(0)
|
||||
);
|
||||
const userGlobalDataPrior = await pool.getUserAccountData(user.address);
|
||||
expect(userGlobalDataPrior.healthFactor.toString()).to.be.bignumber.lt(oneEther, INVALID_HF);
|
||||
|
||||
// Execute liquidation
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralConfig = await helpersContract.getReserveConfigurationData(weth.address);
|
||||
|
||||
const collateralDecimals = collateralConfig.decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
const collateralLiquidationBonus = collateralConfig.liquidationBonus.toString();
|
||||
|
||||
const expectedDebtCovered = new BigNumber(collateralPrice.toString())
|
||||
.times(new BigNumber(wethUserDataBefore.currentATokenBalance.toString()))
|
||||
.times(new BigNumber(10).pow(principalDecimals))
|
||||
.div(
|
||||
new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||
)
|
||||
.div(new BigNumber(collateralLiquidationBonus).div(10000).toString())
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(expectedDebtCovered.toString())
|
||||
.plus(expectedVariableDebtIncrease),
|
||||
'INVALID_VARIABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.false;
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(0);
|
||||
|
||||
// Resets DAI Price
|
||||
await oracle.setAssetPrice(dai.address, daiPrice);
|
||||
});
|
||||
|
||||
it('User 4 deposits WETH, LEND and DAI, then borrows USDC at Variable, then disables WETH as collateral', async () => {
|
||||
const {pool, weth, dai, usdc, users} = testEnv;
|
||||
const user = users[4];
|
||||
const amountWETHToDeposit = parseEther('10');
|
||||
const amountDAIToDeposit = parseEther('100');
|
||||
|
||||
const amountToBorrow = parseUnits('75', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountWETHToDeposit);
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0');
|
||||
|
||||
await dai.connect(user.signer).mint(amountDAIToDeposit);
|
||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('Liquidator tries to liquidate User 5 USDC loan by swapping his WETH collateral, should revert due WETH collateral disabled', async () => {
|
||||
const {pool, weth, dai, usdc, users, mockSwapAdapter, oracle} = testEnv;
|
||||
const user = users[4];
|
||||
const liquidator = users[5];
|
||||
|
||||
const amountToRepay = parseUnits('65', 6);
|
||||
|
||||
// User 5 Disable WETH as collateral
|
||||
await pool.connect(user.signer).setUserUseReserveAsCollateral(weth.address, false);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
expect(wethUserDataBefore.usageAsCollateralEnabled).to.be.false;
|
||||
|
||||
//drop the price to set the HF below 1
|
||||
const daiPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
await oracle.setAssetPrice(
|
||||
dai.address,
|
||||
new BigNumber(daiPrice.toString()).multipliedBy(0.9).toFixed(0)
|
||||
);
|
||||
|
||||
// Liquidator should NOT be able to liquidate himself with WETH, even if is disabled
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith(COLLATERAL_CANNOT_BE_LIQUIDATED);
|
||||
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.false;
|
||||
});
|
||||
});
|
|
@ -155,22 +155,6 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
await configurator.setPoolPause(false);
|
||||
});
|
||||
|
||||
it('Swap liquidity', async () => {
|
||||
const {pool, dai, weth, users, configurator} = testEnv;
|
||||
|
||||
const user = users[1];
|
||||
// Pause the pool
|
||||
await configurator.setPoolPause(true);
|
||||
|
||||
// Try to execute liquidation
|
||||
await expect(
|
||||
pool.connect(user.signer).swapLiquidity(user.address, dai.address, weth.address, '1', '0x')
|
||||
).revertedWith(IS_PAUSED);
|
||||
|
||||
// Unpause the pool
|
||||
await configurator.setPoolPause(false);
|
||||
});
|
||||
|
||||
it('Repay', async () => {
|
||||
const {pool, dai, users, configurator} = testEnv;
|
||||
|
||||
|
@ -187,32 +171,6 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
await configurator.setPoolPause(false);
|
||||
});
|
||||
|
||||
it('Repay with collateral', async () => {
|
||||
const {pool, weth, dai, usdc, users, mockSwapAdapter, oracle, configurator} = testEnv;
|
||||
const user = users[6];
|
||||
const liquidator = users[5];
|
||||
|
||||
// Pause the pool
|
||||
await configurator.setPoolPause(true);
|
||||
|
||||
// Try to execute liquidation
|
||||
await expect(
|
||||
pool
|
||||
.connect(liquidator.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
'1',
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).revertedWith(IS_PAUSED);
|
||||
|
||||
// Unpause the pool
|
||||
await configurator.setPoolPause(false);
|
||||
});
|
||||
|
||||
it('Flash loan', async () => {
|
||||
const {dai, pool, weth, users, configurator} = testEnv;
|
||||
|
||||
|
|
|
@ -1,682 +0,0 @@
|
|||
import {TestEnv, makeSuite} from './helpers/make-suite';
|
||||
import {APPROVAL_AMOUNT_LENDING_POOL} from '../helpers/constants';
|
||||
import {ethers} from 'ethers';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import {
|
||||
calcExpectedVariableDebtTokenBalance,
|
||||
calcExpectedStableDebtTokenBalance,
|
||||
} from './helpers/utils/calculations';
|
||||
import {getContractsData} from './helpers/actions';
|
||||
import {timeLatest, waitForTx} from '../helpers/misc-utils';
|
||||
import {ProtocolErrors, tEthereumAddress} from '../helpers/types';
|
||||
|
||||
const {expect} = require('chai');
|
||||
const {parseUnits, parseEther} = ethers.utils;
|
||||
|
||||
export const expectRepayWithCollateralEvent = (
|
||||
events: ethers.Event[],
|
||||
pool: tEthereumAddress,
|
||||
collateral: tEthereumAddress,
|
||||
borrowing: tEthereumAddress,
|
||||
user: tEthereumAddress
|
||||
) => {
|
||||
if (!events || events.length < 16) {
|
||||
expect(false, 'INVALID_EVENTS_LENGTH_ON_REPAY_COLLATERAL');
|
||||
}
|
||||
|
||||
const repayWithCollateralEvent = events[15];
|
||||
|
||||
expect(repayWithCollateralEvent.address).to.be.equal(pool);
|
||||
expect(`0x${repayWithCollateralEvent.topics[1].slice(26)}`.toLowerCase()).to.be.equal(
|
||||
collateral.toLowerCase()
|
||||
);
|
||||
expect(`0x${repayWithCollateralEvent.topics[2].slice(26)}`).to.be.equal(borrowing.toLowerCase());
|
||||
expect(`0x${repayWithCollateralEvent.topics[3].slice(26)}`.toLowerCase()).to.be.equal(
|
||||
user.toLowerCase()
|
||||
);
|
||||
};
|
||||
|
||||
makeSuite('LendingPool. repayWithCollateral()', (testEnv: TestEnv) => {
|
||||
const {IS_PAUSED} = ProtocolErrors;
|
||||
it("It's not possible to repayWithCollateral() on a non-active collateral or a non active principal", async () => {
|
||||
const {configurator, weth, pool, users, dai, mockSwapAdapter} = testEnv;
|
||||
const user = users[1];
|
||||
await configurator.deactivateReserve(weth.address);
|
||||
|
||||
await expect(
|
||||
pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
parseEther('100'),
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('2');
|
||||
|
||||
await configurator.activateReserve(weth.address);
|
||||
|
||||
await configurator.deactivateReserve(dai.address);
|
||||
|
||||
await expect(
|
||||
pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
parseEther('100'),
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('2');
|
||||
|
||||
await configurator.activateReserve(dai.address);
|
||||
});
|
||||
|
||||
it('User 1 provides some liquidity for others to borrow', async () => {
|
||||
const {pool, weth, dai, usdc, deployer} = testEnv;
|
||||
|
||||
await weth.mint(parseEther('200'));
|
||||
await weth.approve(pool.address, parseEther('200'));
|
||||
await pool.deposit(weth.address, parseEther('200'), deployer.address, 0);
|
||||
await dai.mint(parseEther('20000'));
|
||||
await dai.approve(pool.address, parseEther('20000'));
|
||||
await pool.deposit(dai.address, parseEther('20000'), deployer.address, 0);
|
||||
await usdc.mint(parseEther('20000'));
|
||||
await usdc.approve(pool.address, parseEther('20000'));
|
||||
await pool.deposit(usdc.address, parseEther('20000'), deployer.address, 0);
|
||||
});
|
||||
|
||||
it('User 2 deposit WETH and borrows DAI at Variable', async () => {
|
||||
const {pool, weth, dai, users} = testEnv;
|
||||
const user = users[1];
|
||||
const amountToDeposit = ethers.utils.parseEther('1');
|
||||
const amountToBorrow = ethers.utils.parseEther('20');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(dai.address, amountToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('It is not possible to do reentrancy on repayWithCollateral()', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle} = testEnv;
|
||||
const user = users[1];
|
||||
|
||||
const amountToRepay = parseEther('10');
|
||||
|
||||
await waitForTx(await mockSwapAdapter.setTryReentrancy(true));
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await expect(
|
||||
pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('53');
|
||||
});
|
||||
|
||||
it('User 2 tries to repay his DAI Variable loan using his WETH collateral. First half the amount, after that, the rest', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[1];
|
||||
|
||||
const amountToRepay = parseEther('10');
|
||||
|
||||
await waitForTx(await mockSwapAdapter.setTryReentrancy(false));
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(dai.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
daiReserveDataBefore,
|
||||
daiUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(daiUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(daiUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(daiUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
)
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
});
|
||||
|
||||
it('User 3 deposits WETH and borrows USDC at Variable', async () => {
|
||||
const {pool, weth, usdc, users} = testEnv;
|
||||
const user = users[2];
|
||||
const amountToDeposit = parseEther('10');
|
||||
const amountToBorrow = parseUnits('40', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('User 3 repays completely his USDC loan by swapping his WETH collateral', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
|
||||
const amountToRepay = parseUnits('10', 6);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
});
|
||||
|
||||
it('Revert expected. User 3 tries to repay with his collateral a currency he havent borrow', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter} = testEnv;
|
||||
const user = users[2];
|
||||
|
||||
const amountToRepay = parseUnits('10', 6);
|
||||
|
||||
await expect(
|
||||
pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
).to.be.revertedWith('40');
|
||||
});
|
||||
|
||||
it('User 3 tries to repay with his collateral all his variable debt and part of the stable', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[2];
|
||||
|
||||
const amountToDeposit = parseEther('20');
|
||||
const amountToBorrowStable = parseUnits('40', 6);
|
||||
const amountToBorrowVariable = parseUnits('40', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDeposit);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDeposit, user.address, '0');
|
||||
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.borrow(usdc.address, amountToBorrowVariable, 2, 0, user.address);
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrowStable, 1, 0, user.address);
|
||||
|
||||
const amountToRepay = parseUnits('80', 6);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
const txReceipt = await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
const expectedStableDebtIncrease = calcExpectedStableDebtTokenBalance(
|
||||
usdcUserDataBefore.principalStableDebt,
|
||||
usdcUserDataBefore.stableBorrowRate,
|
||||
usdcUserDataBefore.stableRateLastUpdated,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentStableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.equal(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.gte(0)
|
||||
? new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString()
|
||||
: '0',
|
||||
'INVALID_VARIABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
const stableDebtRepaid = new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.abs();
|
||||
|
||||
expect(usdcUserDataAfter.currentStableDebt).to.be.bignumber.equal(
|
||||
new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||
.minus(stableDebtRepaid)
|
||||
.plus(expectedStableDebtIncrease)
|
||||
.gte(0)
|
||||
? new BigNumber(usdcUserDataBefore.currentStableDebt)
|
||||
.minus(stableDebtRepaid)
|
||||
.plus(expectedStableDebtIncrease)
|
||||
.toString()
|
||||
: '0',
|
||||
'INVALID_STABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
const eventsEmitted = txReceipt.events || [];
|
||||
|
||||
expectRepayWithCollateralEvent(
|
||||
eventsEmitted,
|
||||
pool.address,
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.true;
|
||||
});
|
||||
|
||||
it('User 4 tries to repay a bigger amount that what can be swapped of a particular collateral, repaying only the maximum allowed by that collateral', async () => {
|
||||
const {pool, weth, dai, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[3];
|
||||
|
||||
const amountToDepositWeth = parseEther('0.1');
|
||||
const amountToDepositDAI = parseEther('500');
|
||||
const amountToBorrowVariable = parseEther('80');
|
||||
|
||||
await weth.connect(user.signer).mint(amountToDepositWeth);
|
||||
await dai.connect(user.signer).mint(amountToDepositDAI);
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
|
||||
await pool.connect(user.signer).deposit(weth.address, amountToDepositWeth, user.address, '0');
|
||||
await pool.connect(user.signer).deposit(dai.address, amountToDepositDAI, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(dai.address, amountToBorrowVariable, 2, 0, user.address);
|
||||
|
||||
const amountToRepay = parseEther('80');
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {reserveData: daiReserveDataBefore, userData: daiUserDataBefore} = await getContractsData(
|
||||
dai.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
await waitForTx(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
dai.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: daiUserDataAfter} = await getContractsData(dai.address, user.address, testEnv);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(dai.address);
|
||||
|
||||
const collateralConfig = await helpersContract.getReserveConfigurationData(weth.address);
|
||||
|
||||
const collateralDecimals = collateralConfig.decimals.toString();
|
||||
const collateralLiquidationBonus = collateralConfig.liquidationBonus.toString();
|
||||
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(dai.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedDebtCovered = new BigNumber(collateralPrice.toString())
|
||||
.times(new BigNumber(wethUserDataBefore.currentATokenBalance.toString()))
|
||||
.times(new BigNumber(10).pow(principalDecimals))
|
||||
.div(
|
||||
new BigNumber(principalPrice.toString()).times(new BigNumber(10).pow(collateralDecimals))
|
||||
)
|
||||
.div(new BigNumber(collateralLiquidationBonus).div(10000).toString())
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
daiReserveDataBefore,
|
||||
daiUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(daiUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(daiUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(daiUserDataBefore.currentVariableDebt)
|
||||
.minus(expectedDebtCovered.toString())
|
||||
.plus(expectedVariableDebtIncrease),
|
||||
'INVALID_VARIABLE_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.almostEqual(0);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.false;
|
||||
});
|
||||
|
||||
it('User 5 deposits WETH and DAI, then borrows USDC at Variable, then disables WETH as collateral', async () => {
|
||||
const {pool, weth, dai, usdc, users} = testEnv;
|
||||
const user = users[4];
|
||||
const amountWETHToDeposit = parseEther('10');
|
||||
const amountDAIToDeposit = parseEther('120');
|
||||
const amountToBorrow = parseUnits('65', 6);
|
||||
|
||||
await weth.connect(user.signer).mint(amountWETHToDeposit);
|
||||
await weth.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool.connect(user.signer).deposit(weth.address, amountWETHToDeposit, user.address, '0');
|
||||
|
||||
await dai.connect(user.signer).mint(amountDAIToDeposit);
|
||||
await dai.connect(user.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
|
||||
await pool.connect(user.signer).deposit(dai.address, amountDAIToDeposit, user.address, '0');
|
||||
|
||||
await pool.connect(user.signer).borrow(usdc.address, amountToBorrow, 2, 0, user.address);
|
||||
});
|
||||
|
||||
it('User 5 tries to repay his USDC loan by swapping his WETH collateral, should not revert even with WETH collateral disabled', async () => {
|
||||
const {pool, weth, usdc, users, mockSwapAdapter, oracle, helpersContract} = testEnv;
|
||||
const user = users[4];
|
||||
|
||||
const amountToRepay = parseUnits('65', 6);
|
||||
|
||||
// Disable WETH as collateral
|
||||
await pool.connect(user.signer).setUserUseReserveAsCollateral(weth.address, false);
|
||||
|
||||
const {userData: wethUserDataBefore} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {
|
||||
reserveData: usdcReserveDataBefore,
|
||||
userData: usdcUserDataBefore,
|
||||
} = await getContractsData(usdc.address, user.address, testEnv);
|
||||
|
||||
expect(wethUserDataBefore.usageAsCollateralEnabled).to.be.false;
|
||||
|
||||
// User 5 should be able to liquidate himself with WETH, even if is disabled
|
||||
await mockSwapAdapter.setAmountToReturn(amountToRepay);
|
||||
expect(
|
||||
await pool
|
||||
.connect(user.signer)
|
||||
.repayWithCollateral(
|
||||
weth.address,
|
||||
usdc.address,
|
||||
user.address,
|
||||
amountToRepay,
|
||||
mockSwapAdapter.address,
|
||||
'0x'
|
||||
)
|
||||
);
|
||||
const repayWithCollateralTimestamp = await timeLatest();
|
||||
|
||||
const {userData: wethUserDataAfter} = await getContractsData(
|
||||
weth.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const {userData: usdcUserDataAfter} = await getContractsData(
|
||||
usdc.address,
|
||||
user.address,
|
||||
testEnv
|
||||
);
|
||||
|
||||
const collateralPrice = await oracle.getAssetPrice(weth.address);
|
||||
const principalPrice = await oracle.getAssetPrice(usdc.address);
|
||||
|
||||
const collateralDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(weth.address)
|
||||
).decimals.toString();
|
||||
const principalDecimals = (
|
||||
await helpersContract.getReserveConfigurationData(usdc.address)
|
||||
).decimals.toString();
|
||||
|
||||
const expectedCollateralLiquidated = new BigNumber(principalPrice.toString())
|
||||
.times(new BigNumber(amountToRepay.toString()).times(105))
|
||||
.times(new BigNumber(10).pow(collateralDecimals))
|
||||
.div(
|
||||
new BigNumber(collateralPrice.toString()).times(new BigNumber(10).pow(principalDecimals))
|
||||
)
|
||||
.div(100)
|
||||
.decimalPlaces(0, BigNumber.ROUND_DOWN);
|
||||
|
||||
const expectedVariableDebtIncrease = calcExpectedVariableDebtTokenBalance(
|
||||
usdcReserveDataBefore,
|
||||
usdcUserDataBefore,
|
||||
new BigNumber(repayWithCollateralTimestamp)
|
||||
).minus(usdcUserDataBefore.currentVariableDebt);
|
||||
|
||||
expect(usdcUserDataAfter.currentVariableDebt).to.be.bignumber.almostEqual(
|
||||
new BigNumber(usdcUserDataBefore.currentVariableDebt)
|
||||
.minus(amountToRepay.toString())
|
||||
.plus(expectedVariableDebtIncrease)
|
||||
.toString(),
|
||||
'INVALID_DEBT_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.currentATokenBalance).to.be.bignumber.equal(
|
||||
new BigNumber(wethUserDataBefore.currentATokenBalance).minus(
|
||||
expectedCollateralLiquidated.toString()
|
||||
),
|
||||
'INVALID_COLLATERAL_POSITION'
|
||||
);
|
||||
|
||||
expect(wethUserDataAfter.usageAsCollateralEnabled).to.be.false;
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user