mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'master' into feat/uniswap-adapter-flashloan
This commit is contained in:
commit
8278d2e6d8
|
@ -19,16 +19,11 @@ contract LendingPoolAddressesProvider is Ownable, ILendingPoolAddressesProvider
|
|||
mapping(bytes32 => address) private _addresses;
|
||||
|
||||
bytes32 private constant LENDING_POOL = 'LENDING_POOL';
|
||||
bytes32 private constant LENDING_POOL_CORE = 'LENDING_POOL_CORE';
|
||||
bytes32 private constant LENDING_POOL_CONFIGURATOR = 'LENDING_POOL_CONFIGURATOR';
|
||||
bytes32 private constant AAVE_ADMIN = 'AAVE_ADMIN';
|
||||
bytes32 private constant LENDING_POOL_COLLATERAL_MANAGER = 'COLLATERAL_MANAGER';
|
||||
bytes32 private constant LENDING_POOL_FLASHLOAN_PROVIDER = 'FLASHLOAN_PROVIDER';
|
||||
bytes32 private constant DATA_PROVIDER = 'DATA_PROVIDER';
|
||||
bytes32 private constant ETHEREUM_ADDRESS = 'ETHEREUM_ADDRESS';
|
||||
bytes32 private constant PRICE_ORACLE = 'PRICE_ORACLE';
|
||||
bytes32 private constant LENDING_RATE_ORACLE = 'LENDING_RATE_ORACLE';
|
||||
bytes32 private constant WALLET_BALANCE_PROVIDER = 'WALLET_BALANCE_PROVIDER';
|
||||
|
||||
/**
|
||||
* @dev Sets an address for an id, allowing to cover it or not with a proxy
|
||||
|
|
|
@ -12,6 +12,7 @@ interface IFlashLoanReceiver {
|
|||
address[] calldata assets,
|
||||
uint256[] calldata amounts,
|
||||
uint256[] calldata premiums,
|
||||
address initiator,
|
||||
bytes calldata params
|
||||
) external returns (bool);
|
||||
}
|
||||
|
|
|
@ -27,9 +27,10 @@ interface ILendingPool {
|
|||
* @dev emitted during a withdraw action.
|
||||
* @param reserve the address of the reserve
|
||||
* @param user the address of the user
|
||||
* @param to address that will receive the underlying
|
||||
* @param amount the amount to be withdrawn
|
||||
**/
|
||||
event Withdraw(address indexed reserve, address indexed user, uint256 amount);
|
||||
event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
|
||||
|
||||
event BorrowAllowanceDelegated(
|
||||
address indexed asset,
|
||||
|
@ -74,7 +75,7 @@ interface ILendingPool {
|
|||
* @param reserve the address of the reserve
|
||||
* @param user the address of the user executing the swap
|
||||
**/
|
||||
event Swap(address indexed reserve, address indexed user);
|
||||
event Swap(address indexed reserve, address indexed user, uint256 rateMode);
|
||||
|
||||
/**
|
||||
* @dev emitted when a user enables a reserve as collateral
|
||||
|
@ -99,20 +100,18 @@ interface ILendingPool {
|
|||
/**
|
||||
* @dev emitted when a flashloan is executed
|
||||
* @param target the address of the flashLoanReceiver
|
||||
* @param mode Type of the debt to open if the flash loan is not returned. 0 -> Don't open any debt, just revert, 1 -> stable, 2 -> variable
|
||||
* @param onBehalfOf the address incurring the debt, if borrow mode is not 0
|
||||
* @param assets the address of the assets being flashborrowed
|
||||
* @param amounts the amount requested
|
||||
* @param premiums the total fee on the amount
|
||||
* @param initiator the address initiating the flash loan
|
||||
* @param asset the address of the asset being flashborrowed
|
||||
* @param amount the amount requested
|
||||
* @param premium the total fee on the amount
|
||||
* @param referralCode the referral code of the caller
|
||||
**/
|
||||
event FlashLoan(
|
||||
address indexed target,
|
||||
uint256 mode,
|
||||
address indexed onBehalfOf,
|
||||
address[] assets,
|
||||
uint256[] amounts,
|
||||
uint256[] premiums,
|
||||
address indexed initiator,
|
||||
address indexed asset,
|
||||
uint256 amount,
|
||||
uint256 premium,
|
||||
uint16 referralCode
|
||||
);
|
||||
|
||||
|
@ -188,8 +187,13 @@ interface ILendingPool {
|
|||
* @dev withdraws the assets of user.
|
||||
* @param reserve the address of the reserve
|
||||
* @param amount the underlying amount to be redeemed
|
||||
* @param to address that will receive the underlying
|
||||
**/
|
||||
function withdraw(address reserve, uint256 amount) external;
|
||||
function withdraw(
|
||||
address reserve,
|
||||
uint256 amount,
|
||||
address to
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @dev Sets allowance to borrow on a certain type of debt asset for a certain user address
|
||||
|
@ -289,7 +293,7 @@ interface ILendingPool {
|
|||
* @param receiver The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
|
||||
* @param assets the address of the principal reserve
|
||||
* @param amounts the amount requested for this flashloan
|
||||
* @param mode the flashloan mode
|
||||
* @param modes the flashloan borrow modes
|
||||
* @param params a bytes array to be sent to the flashloan executor
|
||||
* @param referralCode the referral code of the caller
|
||||
**/
|
||||
|
@ -297,7 +301,7 @@ interface ILendingPool {
|
|||
address receiver,
|
||||
address[] calldata assets,
|
||||
uint256[] calldata amounts,
|
||||
uint256 mode,
|
||||
uint256[] calldata modes,
|
||||
address onBehalfOf,
|
||||
bytes calldata params,
|
||||
uint16 referralCode
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
// SPDX-License-Identifier: agpl-3.0
|
||||
pragma solidity ^0.6.8;
|
||||
|
||||
interface ISwapAdapter {
|
||||
/**
|
||||
* @dev Swaps an `amountToSwap` of an asset to another, approving a `fundsDestination` to pull the funds
|
||||
* @param assetToSwapFrom Origin asset
|
||||
* @param assetToSwapTo Destination asset
|
||||
* @param amountToSwap How much `assetToSwapFrom` needs to be swapped
|
||||
* @param fundsDestination Address that will be pulling the swapped funds
|
||||
* @param params Additional variadic field to include extra params
|
||||
*/
|
||||
function executeOperation(
|
||||
address assetToSwapFrom,
|
||||
address assetToSwapTo,
|
||||
uint256 amountToSwap,
|
||||
address fundsDestination,
|
||||
bytes calldata params
|
||||
) external;
|
||||
}
|
|
@ -20,7 +20,6 @@ import {IStableDebtToken} from '../tokenization/interfaces/IStableDebtToken.sol'
|
|||
import {IVariableDebtToken} from '../tokenization/interfaces/IVariableDebtToken.sol';
|
||||
import {DebtTokenBase} from '../tokenization/base/DebtTokenBase.sol';
|
||||
import {IFlashLoanReceiver} from '../flashloan/interfaces/IFlashLoanReceiver.sol';
|
||||
import {ISwapAdapter} from '../interfaces/ISwapAdapter.sol';
|
||||
import {LendingPoolCollateralManager} from './LendingPoolCollateralManager.sol';
|
||||
import {IPriceOracleGetter} from '../interfaces/IPriceOracleGetter.sol';
|
||||
import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
|
@ -120,8 +119,13 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
* @dev withdraws the _reserves of user.
|
||||
* @param asset the address of the reserve
|
||||
* @param amount the underlying amount to be redeemed
|
||||
* @param to address that will receive the underlying
|
||||
**/
|
||||
function withdraw(address asset, uint256 amount) external override {
|
||||
function withdraw(
|
||||
address asset,
|
||||
uint256 amount,
|
||||
address to
|
||||
) external override {
|
||||
_whenNotPaused();
|
||||
ReserveLogic.ReserveData storage reserve = _reserves[asset];
|
||||
|
||||
|
@ -155,9 +159,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
_usersConfig[msg.sender].setUsingAsCollateral(reserve.id, false);
|
||||
}
|
||||
|
||||
IAToken(aToken).burn(msg.sender, msg.sender, amountToWithdraw, reserve.liquidityIndex);
|
||||
IAToken(aToken).burn(msg.sender, to, amountToWithdraw, reserve.liquidityIndex);
|
||||
|
||||
emit Withdraw(asset, msg.sender, amountToWithdraw);
|
||||
emit Withdraw(asset, msg.sender, to, amountToWithdraw);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -352,7 +356,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
|
||||
reserve.updateInterestRates(asset, reserve.aTokenAddress, 0, 0);
|
||||
|
||||
emit Swap(asset, msg.sender);
|
||||
emit Swap(asset, msg.sender, rateMode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -486,13 +490,13 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
struct FlashLoanLocalVars {
|
||||
IFlashLoanReceiver receiver;
|
||||
address oracle;
|
||||
ReserveLogic.InterestRateMode debtMode;
|
||||
uint256 i;
|
||||
address currentAsset;
|
||||
address currentATokenAddress;
|
||||
uint256 currentAmount;
|
||||
uint256 currentPremium;
|
||||
uint256 currentAmountPlusPremium;
|
||||
address debtToken;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -502,7 +506,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
* @param receiverAddress The address of the contract receiving the funds. The receiver should implement the IFlashLoanReceiver interface.
|
||||
* @param assets The addresss of the assets being flashborrowed
|
||||
* @param amounts The amounts requested for this flashloan for each asset
|
||||
* @param mode Type of the debt to open if the flash loan is not returned. 0 -> Don't open any debt, just revert, 1 -> stable, 2 -> variable
|
||||
* @param modes Types of the debt to open if the flash loan is not returned. 0 -> Don't open any debt, just revert, 1 -> stable, 2 -> variable
|
||||
* @param onBehalfOf If mode is not 0, then the address to take the debt onBehalfOf. The onBehalfOf address must already have approved `msg.sender` to incur the debt on their behalf.
|
||||
* @param params Variadic packed params to pass to the receiver as extra information
|
||||
* @param referralCode Referral code of the flash loan
|
||||
|
@ -511,7 +515,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
address receiverAddress,
|
||||
address[] calldata assets,
|
||||
uint256[] calldata amounts,
|
||||
uint256 mode,
|
||||
uint256[] calldata modes,
|
||||
address onBehalfOf,
|
||||
bytes calldata params,
|
||||
uint16 referralCode
|
||||
|
@ -520,13 +524,12 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
|
||||
FlashLoanLocalVars memory vars;
|
||||
|
||||
ValidationLogic.validateFlashloan(assets, amounts, mode);
|
||||
ValidationLogic.validateFlashloan(assets, amounts);
|
||||
|
||||
address[] memory aTokenAddresses = new address[](assets.length);
|
||||
uint256[] memory premiums = new uint256[](assets.length);
|
||||
|
||||
vars.receiver = IFlashLoanReceiver(receiverAddress);
|
||||
vars.debtMode = ReserveLogic.InterestRateMode(mode);
|
||||
|
||||
for (vars.i = 0; vars.i < assets.length; vars.i++) {
|
||||
aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
|
||||
|
@ -539,7 +542,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
|
||||
//execute action of the receiver
|
||||
require(
|
||||
vars.receiver.executeOperation(assets, amounts, premiums, params),
|
||||
vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params),
|
||||
Errors.INVALID_FLASH_LOAN_EXECUTOR_RETURN
|
||||
);
|
||||
|
||||
|
@ -548,10 +551,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
vars.currentAmount = amounts[vars.i];
|
||||
vars.currentPremium = premiums[vars.i];
|
||||
vars.currentATokenAddress = aTokenAddresses[vars.i];
|
||||
|
||||
vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium);
|
||||
|
||||
if (vars.debtMode == ReserveLogic.InterestRateMode.NONE) {
|
||||
if (ReserveLogic.InterestRateMode(modes[vars.i]) == ReserveLogic.InterestRateMode.NONE) {
|
||||
_reserves[vars.currentAsset].updateState();
|
||||
_reserves[vars.currentAsset].cumulateToLiquidityIndex(
|
||||
IERC20(vars.currentATokenAddress).totalSupply(),
|
||||
|
@ -571,13 +573,11 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
);
|
||||
} else {
|
||||
if (msg.sender != onBehalfOf) {
|
||||
address debtToken = _reserves[vars.currentAsset].getDebtTokenAddress(mode);
|
||||
vars.debtToken = _reserves[vars.currentAsset].getDebtTokenAddress(modes[vars.i]);
|
||||
|
||||
_borrowAllowance[debtToken][onBehalfOf][msg
|
||||
.sender] = _borrowAllowance[debtToken][onBehalfOf][msg.sender].sub(
|
||||
vars.currentAmount,
|
||||
Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH
|
||||
);
|
||||
_borrowAllowance[vars.debtToken][onBehalfOf][msg.sender] = _borrowAllowance[vars
|
||||
.debtToken][onBehalfOf][msg.sender]
|
||||
.sub(vars.currentAmount, Errors.BORROW_ALLOWANCE_ARE_NOT_ENOUGH);
|
||||
}
|
||||
|
||||
//if the user didn't choose to return the funds, the system checks if there
|
||||
|
@ -588,14 +588,21 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
msg.sender,
|
||||
onBehalfOf,
|
||||
vars.currentAmount,
|
||||
mode,
|
||||
modes[vars.i],
|
||||
vars.currentATokenAddress,
|
||||
referralCode,
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
emit FlashLoan(receiverAddress, mode, onBehalfOf, assets, amounts, premiums, referralCode);
|
||||
emit FlashLoan(
|
||||
receiverAddress,
|
||||
msg.sender,
|
||||
vars.currentAsset,
|
||||
vars.currentAmount,
|
||||
vars.currentPremium,
|
||||
referralCode
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -691,7 +698,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
* @param asset the address of the reserve
|
||||
* @return the reserve normalized income
|
||||
*/
|
||||
function getReserveNormalizedIncome(address asset) external override view returns (uint256) {
|
||||
function getReserveNormalizedIncome(address asset) external virtual override view returns (uint256) {
|
||||
return _reserves[asset].getNormalizedIncome();
|
||||
}
|
||||
|
||||
|
@ -875,6 +882,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
);
|
||||
|
||||
ValidationLogic.validateBorrow(
|
||||
vars.asset,
|
||||
reserve,
|
||||
vars.onBehalfOf,
|
||||
vars.amount,
|
||||
|
@ -888,34 +896,34 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
oracle
|
||||
);
|
||||
|
||||
uint256 reserveId = reserve.id;
|
||||
if (!userConfig.isBorrowing(reserveId)) {
|
||||
userConfig.setBorrowing(reserveId, true);
|
||||
}
|
||||
|
||||
reserve.updateState();
|
||||
|
||||
//caching the current stable borrow rate
|
||||
uint256 currentStableRate = 0;
|
||||
|
||||
bool isFirstBorrowing = false;
|
||||
if (
|
||||
ReserveLogic.InterestRateMode(vars.interestRateMode) == ReserveLogic.InterestRateMode.STABLE
|
||||
) {
|
||||
currentStableRate = reserve.currentStableBorrowRate;
|
||||
|
||||
IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
||||
isFirstBorrowing = IStableDebtToken(reserve.stableDebtTokenAddress).mint(
|
||||
vars.onBehalfOf,
|
||||
vars.amount,
|
||||
currentStableRate
|
||||
);
|
||||
} else {
|
||||
IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
|
||||
isFirstBorrowing = IVariableDebtToken(reserve.variableDebtTokenAddress).mint(
|
||||
vars.onBehalfOf,
|
||||
vars.amount,
|
||||
reserve.variableBorrowIndex
|
||||
);
|
||||
}
|
||||
|
||||
if (isFirstBorrowing) {
|
||||
userConfig.setBorrowing(reserve.id, true);
|
||||
}
|
||||
|
||||
reserve.updateInterestRates(
|
||||
vars.asset,
|
||||
vars.aTokenAddress,
|
||||
|
|
|
@ -15,7 +15,6 @@ import {Helpers} from '../libraries/helpers/Helpers.sol';
|
|||
import {WadRayMath} from '../libraries/math/WadRayMath.sol';
|
||||
import {PercentageMath} from '../libraries/math/PercentageMath.sol';
|
||||
import {SafeERC20} from '../dependencies/openzeppelin/contracts/SafeERC20.sol';
|
||||
import {ISwapAdapter} from '../interfaces/ISwapAdapter.sol';
|
||||
import {Errors} from '../libraries/helpers/Errors.sol';
|
||||
import {ValidationLogic} from '../libraries/logic/ValidationLogic.sol';
|
||||
import {LendingPoolStorage} from './LendingPoolStorage.sol';
|
||||
|
|
|
@ -47,7 +47,7 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param asset the address of the reserve
|
||||
* @param stableRateEnabled true if stable rate borrowing is enabled, false otherwise
|
||||
**/
|
||||
event BorrowingEnabledOnReserve(address asset, bool stableRateEnabled);
|
||||
event BorrowingEnabledOnReserve(address indexed asset, bool stableRateEnabled);
|
||||
|
||||
/**
|
||||
* @dev emitted when borrowing is disabled on a reserve
|
||||
|
@ -116,42 +116,42 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param asset the address of the reserve
|
||||
* @param ltv the new value for the loan to value
|
||||
**/
|
||||
event ReserveBaseLtvChanged(address asset, uint256 ltv);
|
||||
event ReserveBaseLtvChanged(address indexed asset, uint256 ltv);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve factor is updated
|
||||
* @param asset the address of the reserve
|
||||
* @param factor the new reserve factor
|
||||
**/
|
||||
event ReserveFactorChanged(address asset, uint256 factor);
|
||||
event ReserveFactorChanged(address indexed asset, uint256 factor);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve liquidation threshold is updated
|
||||
* @param asset the address of the reserve
|
||||
* @param threshold the new value for the liquidation threshold
|
||||
**/
|
||||
event ReserveLiquidationThresholdChanged(address asset, uint256 threshold);
|
||||
event ReserveLiquidationThresholdChanged(address indexed asset, uint256 threshold);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve liquidation bonus is updated
|
||||
* @param asset the address of the reserve
|
||||
* @param bonus the new value for the liquidation bonus
|
||||
**/
|
||||
event ReserveLiquidationBonusChanged(address asset, uint256 bonus);
|
||||
event ReserveLiquidationBonusChanged(address indexed asset, uint256 bonus);
|
||||
|
||||
/**
|
||||
* @dev emitted when the reserve decimals are updated
|
||||
* @param asset the address of the reserve
|
||||
* @param decimals the new decimals
|
||||
**/
|
||||
event ReserveDecimalsChanged(address asset, uint256 decimals);
|
||||
event ReserveDecimalsChanged(address indexed asset, uint256 decimals);
|
||||
|
||||
/**
|
||||
* @dev emitted when a reserve interest strategy contract is updated
|
||||
* @param asset the address of the reserve
|
||||
* @param strategy the new address of the interest strategy contract
|
||||
**/
|
||||
event ReserveInterestRateStrategyChanged(address asset, address strategy);
|
||||
event ReserveInterestRateStrategyChanged(address indexed asset, address strategy);
|
||||
|
||||
/**
|
||||
* @dev emitted when an aToken implementation is upgraded
|
||||
|
@ -159,7 +159,11 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param proxy the aToken proxy address
|
||||
* @param implementation the new aToken implementation
|
||||
**/
|
||||
event ATokenUpgraded(address asset, address proxy, address implementation);
|
||||
event ATokenUpgraded(
|
||||
address indexed asset,
|
||||
address indexed proxy,
|
||||
address indexed implementation
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev emitted when the implementation of a stable debt token is upgraded
|
||||
|
@ -167,7 +171,11 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param proxy the stable debt token proxy address
|
||||
* @param implementation the new aToken implementation
|
||||
**/
|
||||
event StableDebtTokenUpgraded(address asset, address proxy, address implementation);
|
||||
event StableDebtTokenUpgraded(
|
||||
address indexed asset,
|
||||
address indexed proxy,
|
||||
address indexed implementation
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev emitted when the implementation of a variable debt token is upgraded
|
||||
|
@ -175,7 +183,11 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param proxy the variable debt token proxy address
|
||||
* @param implementation the new aToken implementation
|
||||
**/
|
||||
event VariableDebtTokenUpgraded(address asset, address proxy, address implementation);
|
||||
event VariableDebtTokenUpgraded(
|
||||
address indexed asset,
|
||||
address indexed proxy,
|
||||
address indexed implementation
|
||||
);
|
||||
|
||||
ILendingPoolAddressesProvider internal addressesProvider;
|
||||
ILendingPool internal pool;
|
||||
|
|
|
@ -98,7 +98,6 @@ library Errors {
|
|||
string public constant INVALID_DECIMALS = '73';
|
||||
string public constant INVALID_RESERVE_FACTOR = '74';
|
||||
|
||||
|
||||
enum CollateralManagerErrors {
|
||||
NO_ERROR,
|
||||
NO_COLLATERAL_AVAILABLE,
|
||||
|
|
|
@ -104,6 +104,7 @@ library ValidationLogic {
|
|||
|
||||
/**
|
||||
* @dev validates a borrow.
|
||||
* @param asset the address of the asset to borrow
|
||||
* @param reserve the reserve state from which the user is borrowing
|
||||
* @param userAddress the address of the user
|
||||
* @param amount the amount to be borrowed
|
||||
|
@ -117,6 +118,7 @@ library ValidationLogic {
|
|||
*/
|
||||
|
||||
function validateBorrow(
|
||||
address asset,
|
||||
ReserveLogic.ReserveData storage reserve,
|
||||
address userAddress,
|
||||
uint256 amount,
|
||||
|
@ -203,6 +205,8 @@ library ValidationLogic {
|
|||
Errors.CALLATERAL_SAME_AS_BORROWING_CURRENCY
|
||||
);
|
||||
|
||||
vars.availableLiquidity = IERC20(asset).balanceOf(reserve.aTokenAddress);
|
||||
|
||||
//calculate the max available loan size in stable rate mode as a percentage of the
|
||||
//available liquidity
|
||||
uint256 maxLoanSizeStable = vars.availableLiquidity.percentMul(maxStableLoanPercent);
|
||||
|
@ -251,15 +255,15 @@ library ValidationLogic {
|
|||
* @dev validates a swap of borrow rate mode.
|
||||
* @param reserve the reserve state on which the user is swapping the rate
|
||||
* @param userConfig the user reserves configuration
|
||||
* @param stableBorrowBalance the stable borrow balance of the user
|
||||
* @param variableBorrowBalance the stable borrow balance of the user
|
||||
* @param stableDebt the stable debt of the user
|
||||
* @param variableDebt the variable debt of the user
|
||||
* @param currentRateMode the rate mode of the borrow
|
||||
*/
|
||||
function validateSwapRateMode(
|
||||
ReserveLogic.ReserveData storage reserve,
|
||||
UserConfiguration.Map storage userConfig,
|
||||
uint256 stableBorrowBalance,
|
||||
uint256 variableBorrowBalance,
|
||||
uint256 stableDebt,
|
||||
uint256 variableDebt,
|
||||
ReserveLogic.InterestRateMode currentRateMode
|
||||
) external view {
|
||||
(bool isActive, bool isFreezed, , bool stableRateEnabled) = reserve.configuration.getFlags();
|
||||
|
@ -268,9 +272,9 @@ library ValidationLogic {
|
|||
require(!isFreezed, Errors.NO_UNFREEZED_RESERVE);
|
||||
|
||||
if (currentRateMode == ReserveLogic.InterestRateMode.STABLE) {
|
||||
require(stableBorrowBalance > 0, Errors.NO_STABLE_RATE_LOAN_IN_RESERVE);
|
||||
require(stableDebt > 0, Errors.NO_STABLE_RATE_LOAN_IN_RESERVE);
|
||||
} else if (currentRateMode == ReserveLogic.InterestRateMode.VARIABLE) {
|
||||
require(variableBorrowBalance > 0, Errors.NO_VARIABLE_RATE_LOAN_IN_RESERVE);
|
||||
require(variableDebt > 0, Errors.NO_VARIABLE_RATE_LOAN_IN_RESERVE);
|
||||
/**
|
||||
* user wants to swap to stable, before swapping we need to ensure that
|
||||
* 1. stable borrow rate is enabled on the reserve
|
||||
|
@ -283,8 +287,7 @@ library ValidationLogic {
|
|||
require(
|
||||
!userConfig.isUsingAsCollateral(reserve.id) ||
|
||||
reserve.configuration.getLtv() == 0 ||
|
||||
stableBorrowBalance.add(variableBorrowBalance) >
|
||||
IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
|
||||
stableDebt.add(variableDebt) > IERC20(reserve.aTokenAddress).balanceOf(msg.sender),
|
||||
Errors.CALLATERAL_SAME_AS_BORROWING_CURRENCY
|
||||
);
|
||||
} else {
|
||||
|
@ -331,16 +334,10 @@ library ValidationLogic {
|
|||
|
||||
/**
|
||||
* @dev validates a flashloan action
|
||||
* @param mode the flashloan mode (0 = classic flashloan, 1 = open a stable rate loan, 2 = open a variable rate loan)
|
||||
* @param assets the assets being flashborrowed
|
||||
* @param amounts the amounts for each asset being borrowed
|
||||
**/
|
||||
function validateFlashloan(
|
||||
address[] memory assets,
|
||||
uint256[] memory amounts,
|
||||
uint256 mode
|
||||
) internal pure {
|
||||
require(mode <= uint256(ReserveLogic.InterestRateMode.VARIABLE), Errors.INVALID_FLASHLOAN_MODE);
|
||||
function validateFlashloan(address[] memory assets, uint256[] memory amounts) internal pure {
|
||||
require(assets.length == amounts.length, Errors.INCONSISTENT_FLASHLOAN_PARAMS);
|
||||
}
|
||||
|
||||
|
|
|
@ -47,9 +47,11 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
|
|||
address[] memory assets,
|
||||
uint256[] memory amounts,
|
||||
uint256[] memory premiums,
|
||||
address initiator,
|
||||
bytes memory params
|
||||
) public override returns (bool) {
|
||||
params;
|
||||
initiator;
|
||||
|
||||
if (_failExecution) {
|
||||
emit ExecutedWithFail(assets, amounts, premiums);
|
||||
|
|
|
@ -97,7 +97,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
|||
address user,
|
||||
uint256 amount,
|
||||
uint256 rate
|
||||
) external override onlyLendingPool {
|
||||
) external override onlyLendingPool returns (bool) {
|
||||
MintLocalVars memory vars;
|
||||
|
||||
//cumulates the user debt
|
||||
|
@ -148,6 +148,8 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
|||
vars.newStableRate,
|
||||
vars.currentAvgStableRate
|
||||
);
|
||||
|
||||
return currentBalance == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -286,7 +288,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
|||
* @param avgRate the average rate at which calculate the total supply
|
||||
* @return The debt balance of the user since the last burn/mint action
|
||||
**/
|
||||
function _calcTotalSupply(uint256 avgRate) internal view returns (uint256) {
|
||||
function _calcTotalSupply(uint256 avgRate) internal virtual view returns (uint256) {
|
||||
uint256 principalSupply = super.totalSupply();
|
||||
|
||||
if (principalSupply == 0) {
|
||||
|
|
|
@ -62,7 +62,7 @@ interface IStableDebtToken {
|
|||
address user,
|
||||
uint256 amount,
|
||||
uint256 rate
|
||||
) external;
|
||||
) external returns (bool);
|
||||
|
||||
/**
|
||||
* @dev burns debt of the target user.
|
||||
|
|
|
@ -163,25 +163,26 @@
|
|||
},
|
||||
"ReserveLogic": {
|
||||
"buidlerevm": {
|
||||
"address": "0xFAe0fd738dAbc8a0426F47437322b6d026A9FD95",
|
||||
"address": "0x78Ee8Fb9fE5abD5e347Fc94c2fb85596d1f60e3c",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"GenericLogic": {
|
||||
"buidlerevm": {
|
||||
"address": "0x6082731fdAba4761277Fb31299ebC782AD3bCf24",
|
||||
"address": "0x920d847fE49E54C19047ba8bc236C45A8068Bca7",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"ValidationLogic": {
|
||||
"buidlerevm": {
|
||||
"address": "0x8456161947DFc1fC159A0B26c025cD2b4bba0c3e",
|
||||
"address": "0xA4765Ff72A9F3CfE73089bb2c3a41B838DF71574",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"LendingPool": {
|
||||
"buidlerevm": {
|
||||
"address": "0xD9273d497eDBC967F39d419461CfcF382a0A822e"
|
||||
"address": "0x35c1419Da7cf0Ff885B8Ef8EA9242FEF6800c99b",
|
||||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
},
|
||||
"LendingPoolConfigurator": {
|
||||
|
@ -265,4 +266,4 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
1
runStableTokenCLI.sh
Normal file
1
runStableTokenCLI.sh
Normal file
|
@ -0,0 +1 @@
|
|||
certoraRun specs/harness/StableDebtTokenHarness.sol:StableDebtTokenHarness --solc solc6.8 --verify StableDebtTokenHarness:specs/StableDebtToken.spec --settings -assumeUnwindCond --cache StableDebtToken --staging master
|
1
runUserConfigCLI.sh
Normal file
1
runUserConfigCLI.sh
Normal file
|
@ -0,0 +1 @@
|
|||
certoraRun specs/harness/UserConfigurationHarness.sol --solc solc6.8 --verify UserConfigurationHarness:specs/UserConfiguration.spec --settings -useBitVectorTheory --staging master
|
1
runVariableTokenCLI.sh
Normal file
1
runVariableTokenCLI.sh
Normal file
|
@ -0,0 +1 @@
|
|||
certoraRun contracts/tokenization/VariableDebtToken.sol:VariableDebtToken specs/harness/LendingPoolHarnessForVariableDebtToken.sol --solc solc6.8 --link VariableDebtToken:POOL=LendingPoolHarnessForVariableDebtToken --verify VariableDebtToken:specs/VariableDebtToken.spec --settings -assumeUnwindCond,-useNonLinearArithmetic --cache VariableDebtToken --staging
|
155
specs/StableDebtToken.spec
Normal file
155
specs/StableDebtToken.spec
Normal file
|
@ -0,0 +1,155 @@
|
|||
methods {
|
||||
getUserLastUpdated(address) returns uint40 envfree
|
||||
}
|
||||
|
||||
rule integrityTimeStamp(address user, method f) {
|
||||
env e;
|
||||
require sinvoke getIncentivesController(e) == 0;
|
||||
require getUserLastUpdated(user) <= e.block.timestamp;
|
||||
calldataarg arg;
|
||||
sinvoke f(e,arg);
|
||||
assert getUserLastUpdated(user) <= e.block.timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
TotalSupply is the sum of all users’ balances
|
||||
|
||||
totalSupply(t) = Σaddress u. balanceOf(u,t)
|
||||
|
||||
Check that each possible opertaion changes the balance of at most one user
|
||||
*/
|
||||
rule balanceOfChange(address a, address b, method f )
|
||||
{
|
||||
env e;
|
||||
require a!=b;
|
||||
require sinvoke getIncentivesController(e) == 0;
|
||||
uint256 balanceABefore = sinvoke balanceOf(e,a);
|
||||
uint256 balanceBBefore = sinvoke balanceOf(e,b);
|
||||
|
||||
calldataarg arg;
|
||||
sinvoke f(e, arg);
|
||||
|
||||
uint256 balanceAAfter = sinvoke balanceOf(e,a);
|
||||
uint256 balanceBAfter = sinvoke balanceOf(e,b);
|
||||
|
||||
assert ( balanceABefore == balanceAAfter || balanceBBefore == balanceBAfter );
|
||||
}
|
||||
|
||||
/**
|
||||
Check that the change to total supply is coherent with the changes to balance
|
||||
*/
|
||||
rule integirtyBalanceOfTotalSupply(address a, method f )
|
||||
{
|
||||
env e;
|
||||
require sinvoke getIncentivesController(e) == 0;
|
||||
uint256 balanceABefore = sinvoke balanceOf(e,a);
|
||||
uint256 totalSupplyBefore = sinvoke totalSupply(e);
|
||||
|
||||
calldataarg arg;
|
||||
sinvoke f(e, arg);
|
||||
require (f.selector != burn(address,uint256).selector );
|
||||
uint256 balanceAAfter = sinvoke balanceOf(e,a);
|
||||
uint256 totalSupplyAfter = sinvoke totalSupply(e);
|
||||
|
||||
assert (balanceAAfter != balanceABefore => ( balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore));
|
||||
}
|
||||
|
||||
/* Burn behaves differently and due to accumulation errors might have less total supply than the balance
|
||||
*/
|
||||
rule integirtyBalanceOfTotalSupplyOnBurn(address a, method f)
|
||||
{
|
||||
env e;
|
||||
require sinvoke getIncentivesController(e) == 0;
|
||||
uint256 balanceABefore = sinvoke balanceOf(e,a);
|
||||
uint256 totalSupplyBefore = sinvoke totalSupply(e);
|
||||
|
||||
uint256 x;
|
||||
sinvoke burn(e, a, x);
|
||||
uint256 balanceAAfter = sinvoke balanceOf(e,a);
|
||||
uint256 totalSupplyAfter = sinvoke totalSupply(e);
|
||||
if (totalSupplyBefore > x)
|
||||
assert (balanceAAfter != balanceABefore => ( balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore));
|
||||
else
|
||||
assert (totalSupplyAfter == 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
Mint inceases the balanceOf user a as expected
|
||||
*/
|
||||
rule integrityMint(address a, uint256 x) {
|
||||
env e;
|
||||
require sinvoke getIncentivesController(e) == 0;
|
||||
uint256 index;
|
||||
uint256 balancebefore = sinvoke balanceOf(e,a);
|
||||
sinvoke mint(e,a,x,index);
|
||||
|
||||
uint256 balanceAfter = sinvoke balanceOf(e,a);
|
||||
assert balanceAfter == balancebefore+x;
|
||||
}
|
||||
|
||||
/**
|
||||
Mint is additive, can performed either all at once or gradually
|
||||
mint(u,x); mint(u,y) ~ mint(u,x+y) at the same timestamp
|
||||
|
||||
Note: We assume that the stable rate of the user is 0.
|
||||
The case where the rate is non-zero takes much more time to prove,
|
||||
and therefore it is currently excluded from the CI.
|
||||
*/
|
||||
rule additiveMint(address a, uint256 x, uint256 y) {
|
||||
env e;
|
||||
require sinvoke getIncentivesController(e) == 0;
|
||||
require getUserStableRate(e,a) == 0;
|
||||
uint256 index;
|
||||
storage initialStorage = lastStorage;
|
||||
sinvoke mint(e,a,x,index);
|
||||
sinvoke mint(e,a,y,index);
|
||||
uint256 balanceScenario1 = sinvoke balanceOf(e,a);
|
||||
|
||||
uint256 t = x + y;
|
||||
sinvoke mint(e,a, t ,index) at initialStorage;
|
||||
|
||||
uint256 balanceScenario2 = sinvoke balanceOf(e,a);
|
||||
assert balanceScenario1 == balanceScenario2, "mint is not additive";
|
||||
}
|
||||
|
||||
rule integrityBurn(address a, uint256 x) {
|
||||
env e;
|
||||
require sinvoke getIncentivesController(e) == 0;
|
||||
uint256 index;
|
||||
uint256 balancebefore = sinvoke balanceOf(e,a);
|
||||
sinvoke burn(e,a,x);
|
||||
|
||||
uint256 balanceAfter = sinvoke balanceOf(e,a);
|
||||
assert balanceAfter == balancebefore - x;
|
||||
}
|
||||
|
||||
rule additiveBurn(address a, uint256 x, uint256 y) {
|
||||
env e;
|
||||
require sinvoke getIncentivesController(e) == 0;
|
||||
storage initialStorage = lastStorage;
|
||||
sinvoke burn(e, a, x);
|
||||
sinvoke burn(e, a, y);
|
||||
uint256 balanceScenario1 = balanceOf(e, a);
|
||||
uint256 t = x + y;
|
||||
sinvoke burn(e, a, t) at initialStorage;
|
||||
|
||||
uint256 balanceScenario2 = balanceOf(e, a);
|
||||
assert balanceScenario1 == balanceScenario2, "burn is not additive";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
mint and burn are inverse operations
|
||||
Thus, totalSupply is back to initial state
|
||||
BalanceOf user is back to initial state */
|
||||
rule inverseMintBurn(address a, uint256 x) {
|
||||
env e;
|
||||
require sinvoke getIncentivesController(e) == 0;
|
||||
uint256 index;
|
||||
uint256 balancebefore = sinvoke balanceOf(e,a);
|
||||
sinvoke mint(e,a,x,index);
|
||||
sinvoke burn(e,a,x);
|
||||
uint256 balanceAfter = sinvoke balanceOf(e,a);
|
||||
assert balancebefore == balanceAfter, "burn is not inverse of mint";
|
||||
}
|
||||
|
66
specs/UserConfiguration.spec
Normal file
66
specs/UserConfiguration.spec
Normal file
|
@ -0,0 +1,66 @@
|
|||
methods {
|
||||
setBorrowing(address, uint256, bool) envfree
|
||||
setUsingAsCollateral(address, uint256, bool) envfree
|
||||
isUsingAsCollateralOrBorrowing(address, uint256) returns bool envfree
|
||||
isBorrowing(address, uint256) returns bool envfree
|
||||
isUsingAsCollateral(address, uint256) returns bool envfree
|
||||
isBorrowingAny(address ) returns bool envfree
|
||||
isEmpty(address ) returns bool envfree
|
||||
}
|
||||
|
||||
invariant empty(address user, uint256 reserveIndex )
|
||||
isEmpty(user) => !isBorrowingAny(user) && !isUsingAsCollateralOrBorrowing(user, reserveIndex)
|
||||
|
||||
invariant notEmpty(address user, uint256 reserveIndex )
|
||||
( isBorrowingAny(user) || isUsingAsCollateral(user, reserveIndex)) => !isEmpty(user)
|
||||
|
||||
|
||||
invariant borrowing(address user, uint256 reserveIndex )
|
||||
isBorrowing(user, reserveIndex) => isBorrowingAny(user)
|
||||
|
||||
invariant collateralOrBorrowing(address user, uint256 reserveIndex )
|
||||
( isUsingAsCollateral(user, reserveIndex) || isBorrowing(user, reserveIndex) ) <=> isUsingAsCollateralOrBorrowing(user, reserveIndex)
|
||||
|
||||
|
||||
|
||||
rule setBorrowing(address user, uint256 reserveIndex, bool borrowing)
|
||||
{
|
||||
require reserveIndex < 128;
|
||||
|
||||
setBorrowing(user, reserveIndex, borrowing);
|
||||
assert isBorrowing(user, reserveIndex) == borrowing, "unexpected result";
|
||||
}
|
||||
|
||||
rule setBorrowingNoChangeToOther(address user, uint256 reserveIndex, uint256 reserveIndexOther, bool borrowing)
|
||||
{
|
||||
require reserveIndexOther != reserveIndex;
|
||||
require reserveIndexOther < 128 && reserveIndex < 128;
|
||||
bool otherReserveBorrowing = isBorrowing(user, reserveIndexOther);
|
||||
bool otherReserveCollateral = isUsingAsCollateral(user,reserveIndexOther);
|
||||
|
||||
setBorrowing(user, reserveIndex, borrowing);
|
||||
assert otherReserveBorrowing == isBorrowing(user, reserveIndexOther) &&
|
||||
otherReserveCollateral == isUsingAsCollateral(user,reserveIndexOther), "changed to other reserve";
|
||||
}
|
||||
|
||||
|
||||
rule setUsingAsCollateral(address user, uint256 reserveIndex, bool usingAsCollateral)
|
||||
{
|
||||
require reserveIndex < 128;
|
||||
|
||||
setUsingAsCollateral(user, reserveIndex, usingAsCollateral);
|
||||
assert isUsingAsCollateral(user, reserveIndex) == usingAsCollateral, "unexpected result";
|
||||
}
|
||||
|
||||
|
||||
rule setUsingAsCollateralNoChangeToOther(address user, uint256 reserveIndex, uint256 reserveIndexOther, bool usingAsCollateral)
|
||||
{
|
||||
require reserveIndexOther != reserveIndex;
|
||||
require reserveIndexOther < 128 && reserveIndex < 128;
|
||||
bool otherReserveBorrowing = isBorrowing(user, reserveIndexOther);
|
||||
bool otherReserveCollateral = isUsingAsCollateral(user,reserveIndexOther);
|
||||
|
||||
setUsingAsCollateral(user, reserveIndex, usingAsCollateral);
|
||||
assert otherReserveBorrowing == isBorrowing(user, reserveIndexOther) &&
|
||||
otherReserveCollateral == isUsingAsCollateral(user,reserveIndexOther), "changed to other reserve";
|
||||
}
|
173
specs/VariableDebtToken.spec
Normal file
173
specs/VariableDebtToken.spec
Normal file
|
@ -0,0 +1,173 @@
|
|||
using LendingPoolHarnessForVariableDebtToken as POOL
|
||||
/**
|
||||
TotalSupply is the sum of all users’ balances
|
||||
|
||||
totalSupply(t) = Σaddress u. balanceOf(u,t)
|
||||
|
||||
Check that each possible opertaion changes the balance of at most one user
|
||||
*/
|
||||
rule balanceOfChange(address a, address b, method f)
|
||||
{
|
||||
env e;
|
||||
require a!=b ;
|
||||
uint256 balanceABefore = sinvoke balanceOf(e, a);
|
||||
uint256 balanceBBefore = sinvoke balanceOf(e, b);
|
||||
|
||||
calldataarg arg;
|
||||
sinvoke f(e, arg);
|
||||
|
||||
uint256 balanceAAfter = sinvoke balanceOf(e, a);
|
||||
uint256 balanceBAfter = sinvoke balanceOf(e, b);
|
||||
|
||||
assert ( balanceABefore == balanceAAfter || balanceBBefore == balanceBAfter );
|
||||
}
|
||||
|
||||
/*
|
||||
Check that the changed to total supply is coherent with the changes to balance
|
||||
*/
|
||||
|
||||
rule integirtyBalanceOfTotalSupply(address a, method f )
|
||||
{
|
||||
env e;
|
||||
|
||||
uint256 balanceABefore = balanceOf(e, a);
|
||||
uint256 totalSupplyBefore = totalSupply(e);
|
||||
|
||||
calldataarg arg;
|
||||
sinvoke f(e, arg);
|
||||
require (f.selector != burn(address, uint256, uint256).selector &&
|
||||
f.selector != mint(address, uint256, uint256).selector ) ;
|
||||
uint256 balanceAAfter = balanceOf(e, a);
|
||||
uint256 totalSupplyAfter = totalSupply(e);
|
||||
|
||||
assert (balanceAAfter != balanceABefore => ( balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore));
|
||||
}
|
||||
|
||||
/* Burn behaves deferently and due to accumulation errors might hace less total supply then the balance
|
||||
*/
|
||||
|
||||
rule integirtyBalanceOfTotalSupplyOnBurn(address a, method f )
|
||||
{
|
||||
env e;
|
||||
|
||||
uint256 balanceABefore = balanceOf(e, a);
|
||||
uint256 totalSupplyBefore = totalSupply(e);
|
||||
|
||||
uint256 x;
|
||||
address asset;
|
||||
uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset);
|
||||
sinvoke burn(e, a, x, index);
|
||||
uint256 balanceAAfter = balanceOf(e, a);
|
||||
uint256 totalSupplyAfter = totalSupply(e);
|
||||
assert (balanceAAfter != balanceABefore => ( balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore));
|
||||
}
|
||||
|
||||
rule integirtyBalanceOfTotalSupplyOnMint(address a, method f )
|
||||
{
|
||||
env e;
|
||||
|
||||
uint256 balanceABefore = balanceOf(e, a);
|
||||
uint256 totalSupplyBefore = totalSupply(e);
|
||||
|
||||
uint256 x;
|
||||
address asset;
|
||||
uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset);
|
||||
sinvoke mint(e, a, x, index);
|
||||
uint256 balanceAAfter = balanceOf(e, a);
|
||||
uint256 totalSupplyAfter = totalSupply(e);
|
||||
assert (balanceAAfter != balanceABefore => ( balanceAAfter - balanceABefore == totalSupplyAfter - totalSupplyBefore));
|
||||
}
|
||||
|
||||
/**
|
||||
Minting an amount of x tokens for user u increases their balance by x, up to rounding errors.
|
||||
{ b= balanceOf(u,t) }
|
||||
mint(u,x,index)
|
||||
{ balanceOf(u,t) = b + x }
|
||||
|
||||
*/
|
||||
rule integrityMint(address a, uint256 x) {
|
||||
env e;
|
||||
address asset;
|
||||
uint256 index = POOL.getReserveNormalizedVariableDebt(e,asset);
|
||||
uint256 balancebefore = balanceOf(e, a);
|
||||
sinvoke mint(e, a, x, index);
|
||||
|
||||
uint256 balanceAfter = balanceOf(e, a);
|
||||
assert balanceAfter == balancebefore+x;
|
||||
}
|
||||
|
||||
/**
|
||||
Mint is additive, can performed either all at once or gradually
|
||||
mint(u,x); mint(u,y) ~ mint(u,x+y) at the same timestamp
|
||||
*/
|
||||
rule additiveMint(address a, uint256 x, uint256 y) {
|
||||
env e;
|
||||
address asset;
|
||||
uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset);
|
||||
storage initialStorage = lastStorage;
|
||||
sinvoke mint(e, a, x, index);
|
||||
sinvoke mint(e, a, y, index);
|
||||
uint256 balanceScenario1 = balanceOf(e, a);
|
||||
uint t = x + y;
|
||||
sinvoke mint(e, a, t ,index) at initialStorage;
|
||||
|
||||
uint256 balanceScenario2 = balanceOf(e, a);
|
||||
assert balanceScenario1 == balanceScenario2, "mint is not additive";
|
||||
}
|
||||
|
||||
/**
|
||||
Transfer of x amount of tokens from user u where receiver is user u’
|
||||
{bu = balanceOf(u) }
|
||||
burn(u, u’, x)
|
||||
{balanceOf(u) = bu - x }
|
||||
*/
|
||||
rule integrityBurn(address a, uint256 x) {
|
||||
env e;
|
||||
address asset;
|
||||
uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset);
|
||||
uint256 balancebefore = balanceOf(e, a);
|
||||
sinvoke burn(e, a, x, index);
|
||||
|
||||
uint256 balanceAfter = balanceOf(e, a);
|
||||
assert balanceAfter == balancebefore - x;
|
||||
}
|
||||
/**
|
||||
Minting is additive, i.e., it can be performed either all at once or in steps.
|
||||
|
||||
burn(u, u’, x); burn(u, u’, y) ~ burn(u, u’, x+y)
|
||||
*/
|
||||
rule additiveBurn(address a, uint256 x, uint256 y) {
|
||||
env e;
|
||||
address asset;
|
||||
uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset);
|
||||
storage initialStorage = lastStorage;
|
||||
sinvoke burn(e, a, x, index);
|
||||
sinvoke burn(e, a, y, index);
|
||||
uint256 balanceScenario1 = balanceOf(e, a);
|
||||
uint t = x + y;
|
||||
sinvoke burn(e, a, t ,index) at initialStorage;
|
||||
|
||||
uint256 balanceScenario2 = balanceOf(e, a);
|
||||
assert balanceScenario1 == balanceScenario2, "burn is not additive";
|
||||
}
|
||||
|
||||
/**
|
||||
Minting and burning are inverse operations.
|
||||
|
||||
{bu = balanceOf(u) }
|
||||
mint(u,x); burn(u, u, x)
|
||||
{balanceOf(u) = bu }
|
||||
*/
|
||||
rule inverseMintBurn(address a, uint256 x) {
|
||||
env e;
|
||||
address asset;
|
||||
uint256 index = POOL.getReserveNormalizedVariableDebt(e, asset);
|
||||
uint256 balancebefore = balanceOf(e, a);
|
||||
sinvoke mint(e, a, x, index);
|
||||
sinvoke burn(e, a, x, index);
|
||||
uint256 balanceAfter = balanceOf(e, a);
|
||||
assert balancebefore == balanceAfter, "burn is not inverse of mint";
|
||||
}
|
||||
|
||||
|
||||
|
198
specs/harness/LendingPoolHarnessForVariableDebtToken.sol
Normal file
198
specs/harness/LendingPoolHarnessForVariableDebtToken.sol
Normal file
|
@ -0,0 +1,198 @@
|
|||
pragma solidity ^0.6.8;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {ReserveConfiguration} from '../../contracts/libraries/configuration/ReserveConfiguration.sol';
|
||||
import {UserConfiguration} from '../../contracts/libraries/configuration/UserConfiguration.sol';
|
||||
import {ReserveLogic} from '../../contracts/libraries/logic/ReserveLogic.sol';
|
||||
import {ILendingPool} from '../../contracts/interfaces/ILendingPool.sol';
|
||||
import {LendingPool} from '../../contracts/lendingpool/LendingPool.sol';
|
||||
|
||||
/*
|
||||
Certora: Harness that delegates calls to the original LendingPool.
|
||||
Used for the verification of the VariableDebtToken contract.
|
||||
*/
|
||||
contract LendingPoolHarnessForVariableDebtToken is ILendingPool {
|
||||
|
||||
LendingPool private originalPool;
|
||||
|
||||
function deposit(
|
||||
address asset,
|
||||
uint256 amount,
|
||||
address onBehalfOf,
|
||||
uint16 referralCode
|
||||
) external override {
|
||||
originalPool.deposit(asset, amount, onBehalfOf, referralCode);
|
||||
}
|
||||
|
||||
function withdraw(address asset, uint256 amount) external override {
|
||||
originalPool.withdraw(asset, amount);
|
||||
}
|
||||
|
||||
function getBorrowAllowance(
|
||||
address fromUser,
|
||||
address toUser,
|
||||
address asset,
|
||||
uint256 interestRateMode
|
||||
) external override view returns (uint256) {
|
||||
return originalPool.getBorrowAllowance(fromUser, toUser, asset, interestRateMode);
|
||||
}
|
||||
|
||||
function delegateBorrowAllowance(
|
||||
address asset,
|
||||
address user,
|
||||
uint256 interestRateMode,
|
||||
uint256 amount
|
||||
) external override {
|
||||
originalPool.delegateBorrowAllowance(asset, user, interestRateMode, amount);
|
||||
}
|
||||
|
||||
function borrow(
|
||||
address asset,
|
||||
uint256 amount,
|
||||
uint256 interestRateMode,
|
||||
uint16 referralCode,
|
||||
address onBehalfOf
|
||||
) external override {
|
||||
originalPool.borrow(asset, amount, interestRateMode, referralCode, onBehalfOf);
|
||||
}
|
||||
|
||||
function repay(
|
||||
address asset,
|
||||
uint256 amount,
|
||||
uint256 rateMode,
|
||||
address onBehalfOf
|
||||
) external override {
|
||||
originalPool.repay(asset, amount, rateMode, onBehalfOf);
|
||||
}
|
||||
|
||||
function swapBorrowRateMode(address asset, uint256 rateMode) external override {
|
||||
originalPool.swapBorrowRateMode(asset, rateMode);
|
||||
}
|
||||
|
||||
function rebalanceStableBorrowRate(address asset, address user) external override {
|
||||
originalPool.rebalanceStableBorrowRate(asset, user);
|
||||
}
|
||||
|
||||
function setUserUseReserveAsCollateral(address asset, bool useAsCollateral) external override {
|
||||
originalPool.setUserUseReserveAsCollateral(asset, useAsCollateral);
|
||||
}
|
||||
|
||||
function liquidationCall(
|
||||
address collateral,
|
||||
address asset,
|
||||
address user,
|
||||
uint256 purchaseAmount,
|
||||
bool receiveAToken
|
||||
) external override {
|
||||
originalPool.liquidationCall(collateral, asset, user, purchaseAmount, receiveAToken);
|
||||
}
|
||||
|
||||
function getReservesList() external override view returns (address[] memory) {
|
||||
return originalPool.getReservesList();
|
||||
}
|
||||
|
||||
function getReserveData(address asset) external override view returns (ReserveLogic.ReserveData memory) {
|
||||
return originalPool.getReserveData(asset);
|
||||
}
|
||||
|
||||
function getUserConfiguration(address user) external override view returns (UserConfiguration.Map memory) {
|
||||
return originalPool.getUserConfiguration(user);
|
||||
}
|
||||
|
||||
function getUserAccountData(address user)
|
||||
external
|
||||
override
|
||||
view
|
||||
returns (
|
||||
uint256 totalCollateralETH,
|
||||
uint256 totalBorrowsETH,
|
||||
uint256 availableBorrowsETH,
|
||||
uint256 currentLiquidationThreshold,
|
||||
uint256 ltv,
|
||||
uint256 healthFactor
|
||||
)
|
||||
{
|
||||
return originalPool.getUserAccountData(user);
|
||||
}
|
||||
|
||||
function initReserve(
|
||||
address asset,
|
||||
address aTokenAddress,
|
||||
address stableDebtAddress,
|
||||
address variableDebtAddress,
|
||||
address interestRateStrategyAddress
|
||||
) external override {
|
||||
originalPool.initReserve(asset, aTokenAddress, stableDebtAddress, variableDebtAddress, interestRateStrategyAddress);
|
||||
}
|
||||
|
||||
function setReserveInterestRateStrategyAddress(address asset, address rateStrategyAddress)
|
||||
external
|
||||
override
|
||||
{
|
||||
originalPool.setReserveInterestRateStrategyAddress(asset, rateStrategyAddress);
|
||||
}
|
||||
|
||||
function setConfiguration(address asset, uint256 configuration) external override {
|
||||
originalPool.setConfiguration(asset, configuration);
|
||||
}
|
||||
|
||||
function getConfiguration(address asset)
|
||||
external
|
||||
override
|
||||
view
|
||||
returns (ReserveConfiguration.Map memory)
|
||||
{
|
||||
return originalPool.getConfiguration(asset);
|
||||
}
|
||||
|
||||
mapping(uint256 => uint256) private reserveNormalizedIncome;
|
||||
|
||||
function getReserveNormalizedIncome(address asset) external override view returns (uint256) {
|
||||
require(reserveNormalizedIncome[block.timestamp] == 1e27);
|
||||
return reserveNormalizedIncome[block.timestamp];
|
||||
}
|
||||
|
||||
mapping(uint256 => uint256) private reserveNormalizedVariableDebt;
|
||||
|
||||
function getReserveNormalizedVariableDebt(address asset)
|
||||
external
|
||||
override
|
||||
view
|
||||
returns (uint256)
|
||||
{
|
||||
require(reserveNormalizedVariableDebt[block.timestamp] == 1e27);
|
||||
return reserveNormalizedVariableDebt[block.timestamp];
|
||||
}
|
||||
|
||||
function setPause(bool val) external override {
|
||||
originalPool.setPause(val);
|
||||
}
|
||||
|
||||
function paused() external override view returns (bool) {
|
||||
return originalPool.paused();
|
||||
}
|
||||
|
||||
function flashLoan(
|
||||
address receiver,
|
||||
address[] calldata assets,
|
||||
uint256[] calldata amounts,
|
||||
uint256 mode,
|
||||
address onBehalfOf,
|
||||
bytes calldata params,
|
||||
uint16 referralCode
|
||||
) external override {
|
||||
originalPool.flashLoan(receiver, assets, amounts, mode, onBehalfOf, params, referralCode);
|
||||
}
|
||||
|
||||
function finalizeTransfer(
|
||||
address asset,
|
||||
address from,
|
||||
address to,
|
||||
uint256 amount,
|
||||
uint256 balanceFromAfter,
|
||||
uint256 balanceToBefore
|
||||
) external override {
|
||||
originalPool.finalizeTransfer(asset, from, to, amount, balanceFromAfter, balanceToBefore);
|
||||
}
|
||||
|
||||
}
|
28
specs/harness/StableDebtTokenHarness.sol
Normal file
28
specs/harness/StableDebtTokenHarness.sol
Normal file
|
@ -0,0 +1,28 @@
|
|||
pragma solidity ^0.6.8;
|
||||
|
||||
import {StableDebtToken} from '../../contracts/tokenization/StableDebtToken.sol';
|
||||
import {IncentivizedERC20} from '../../contracts/tokenization/IncentivizedERC20.sol';
|
||||
|
||||
contract StableDebtTokenHarness is StableDebtToken {
|
||||
|
||||
constructor(
|
||||
address pool,
|
||||
address underlyingAsset,
|
||||
string memory name,
|
||||
string memory symbol,
|
||||
address incentivesController
|
||||
) public StableDebtToken(pool, underlyingAsset, name, symbol, incentivesController) {}
|
||||
|
||||
function balanceOf(address account) public override view returns (uint256) {
|
||||
return IncentivizedERC20.balanceOf(account);
|
||||
}
|
||||
|
||||
function _calcTotalSupply(uint256 avgRate) internal override view returns (uint256) {
|
||||
return IncentivizedERC20.totalSupply();
|
||||
}
|
||||
|
||||
function getIncentivesController() public view returns (address) {
|
||||
return address(_incentivesController);
|
||||
}
|
||||
|
||||
}
|
77
specs/harness/UserConfigurationHarness.sol
Normal file
77
specs/harness/UserConfigurationHarness.sol
Normal file
|
@ -0,0 +1,77 @@
|
|||
pragma solidity ^0.6.8;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
import {UserConfiguration} from '../../contracts/libraries/configuration/UserConfiguration.sol';
|
||||
|
||||
/*
|
||||
A wrapper contract for calling functions from the library UserConfiguration.
|
||||
*/
|
||||
contract UserConfigurationHarness {
|
||||
|
||||
UserConfiguration.Map internal usersConfig;
|
||||
|
||||
function setBorrowing(
|
||||
address user,
|
||||
uint256 reserveIndex,
|
||||
bool borrowing
|
||||
) public {
|
||||
UserConfiguration.setBorrowing(usersConfig, reserveIndex, borrowing);
|
||||
}
|
||||
|
||||
function setUsingAsCollateral(
|
||||
address user,
|
||||
uint256 reserveIndex,
|
||||
bool _usingAsCollateral
|
||||
) public {
|
||||
UserConfiguration.setUsingAsCollateral(usersConfig, reserveIndex, _usingAsCollateral);
|
||||
}
|
||||
|
||||
function isUsingAsCollateralOrBorrowing(
|
||||
address user,
|
||||
uint256 reserveIndex)
|
||||
public
|
||||
view
|
||||
returns (bool) {
|
||||
return UserConfiguration.isUsingAsCollateralOrBorrowing(usersConfig, reserveIndex);
|
||||
}
|
||||
|
||||
function isBorrowing(
|
||||
address user,
|
||||
uint256 reserveIndex)
|
||||
public
|
||||
view
|
||||
returns (bool) {
|
||||
return UserConfiguration.isBorrowing(usersConfig, reserveIndex);
|
||||
}
|
||||
|
||||
function isUsingAsCollateral(
|
||||
address user,
|
||||
uint256 reserveIndex)
|
||||
public
|
||||
view
|
||||
returns (bool) {
|
||||
return UserConfiguration.isUsingAsCollateral(usersConfig, reserveIndex);
|
||||
}
|
||||
|
||||
function isBorrowingAny(
|
||||
address user)
|
||||
public
|
||||
view
|
||||
returns (bool) {
|
||||
return UserConfiguration.isBorrowingAny(usersConfig);
|
||||
}
|
||||
|
||||
function isEmpty(
|
||||
address user)
|
||||
public
|
||||
view
|
||||
returns (bool) {
|
||||
return UserConfiguration.isEmpty(usersConfig);
|
||||
}
|
||||
|
||||
/*
|
||||
Mimics the original constructor of the contract.
|
||||
*/
|
||||
function init_state() public { }
|
||||
}
|
||||
|
|
@ -71,8 +71,11 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
|||
users[1].address
|
||||
);
|
||||
|
||||
const userReserveData = await helpersContract.getUserReserveData(weth.address, users[1].address);
|
||||
|
||||
const userReserveData = await helpersContract.getUserReserveData(
|
||||
weth.address,
|
||||
users[1].address
|
||||
);
|
||||
|
||||
expect(userReserveData.currentStableDebt.toString()).to.be.eq(ethers.utils.parseEther('0.1'));
|
||||
});
|
||||
|
||||
|
@ -80,7 +83,7 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
|||
const {users, pool, aDai, dai, weth} = testEnv;
|
||||
|
||||
const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '1000');
|
||||
|
||||
|
||||
await expect(
|
||||
aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer),
|
||||
TRANSFER_NOT_ALLOWED
|
||||
|
@ -88,15 +91,14 @@ makeSuite('AToken: Transfer', (testEnv: TestEnv) => {
|
|||
});
|
||||
|
||||
it('User 1 tries to transfer a small amount of DAI used as collateral back to user 0', async () => {
|
||||
|
||||
const {users, pool, aDai, dai, weth} = testEnv;
|
||||
|
||||
const aDAItoTransfer = await convertToCurrencyDecimals(dai.address, '100');
|
||||
|
||||
|
||||
await aDai.connect(users[1].signer).transfer(users[0].address, aDAItoTransfer);
|
||||
|
||||
const user0Balance = await aDai.balanceOf(users[0].address);
|
||||
|
||||
expect(user0Balance.toString()).to.be.eq(aDAItoTransfer.toString());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -48,7 +48,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
0,
|
||||
[0],
|
||||
_mockFlashLoanReceiver.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -78,7 +78,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
['1000720000000000000'],
|
||||
0,
|
||||
[0],
|
||||
_mockFlashLoanReceiver.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -110,7 +110,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
0,
|
||||
[0],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -131,7 +131,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
0,
|
||||
[0],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -152,12 +152,12 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
4,
|
||||
[4],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
)
|
||||
).to.be.revertedWith(INVALID_FLASHLOAN_MODE);
|
||||
).to.be.reverted;
|
||||
});
|
||||
|
||||
it('Caller deposits 1000 DAI as collateral, Takes WETH flashloan with mode = 2, does not return the funds. A variable loan for caller is created', async () => {
|
||||
|
@ -181,7 +181,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[ethers.utils.parseEther('0.8')],
|
||||
2,
|
||||
[2],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -209,7 +209,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
['1004415000000000000'], //slightly higher than the available liquidity
|
||||
2,
|
||||
[2],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -223,7 +223,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
const caller = users[1];
|
||||
|
||||
await expect(
|
||||
pool.flashLoan(deployer.address, [weth.address], ['1000000000000000000'], 2, caller.address, '0x10', '0')
|
||||
pool.flashLoan(
|
||||
deployer.address,
|
||||
[weth.address],
|
||||
['1000000000000000000'],
|
||||
[2],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
)
|
||||
).to.be.reverted;
|
||||
});
|
||||
|
||||
|
@ -251,7 +259,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[usdc.address],
|
||||
[flashloanAmount],
|
||||
0,
|
||||
[0],
|
||||
_mockFlashLoanReceiver.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -294,7 +302,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[usdc.address],
|
||||
[flashloanAmount],
|
||||
2,
|
||||
[2],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -321,7 +329,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
|
||||
await pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(_mockFlashLoanReceiver.address, [usdc.address], [flashloanAmount], 2, caller.address, '0x10', '0');
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
[usdc.address],
|
||||
[flashloanAmount],
|
||||
[2],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
);
|
||||
const {variableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(
|
||||
usdc.address
|
||||
);
|
||||
|
@ -360,7 +376,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[flashAmount],
|
||||
0,
|
||||
[0],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -383,7 +399,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[flashAmount],
|
||||
1,
|
||||
[1],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -429,7 +445,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[flashAmount],
|
||||
1,
|
||||
[1],
|
||||
onBehalfOf.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
@ -458,7 +474,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[flashAmount],
|
||||
1,
|
||||
[1],
|
||||
onBehalfOf.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
|
|
@ -231,7 +231,7 @@ export const withdraw = async (
|
|||
|
||||
if (expectedResult === 'success') {
|
||||
const txResult = await waitForTx(
|
||||
await pool.connect(user.signer).withdraw(reserve, amountToWithdraw)
|
||||
await pool.connect(user.signer).withdraw(reserve, amountToWithdraw, user.address)
|
||||
);
|
||||
|
||||
const {
|
||||
|
@ -269,8 +269,10 @@ export const withdraw = async (
|
|||
// );
|
||||
// });
|
||||
} else if (expectedResult === 'revert') {
|
||||
await expect(pool.connect(user.signer).withdraw(reserve, amountToWithdraw), revertMessage).to.be
|
||||
.reverted;
|
||||
await expect(
|
||||
pool.connect(user.signer).withdraw(reserve, amountToWithdraw, user.address),
|
||||
revertMessage
|
||||
).to.be.reverted;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -116,7 +116,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
|
||||
// user tries to burn
|
||||
await expect(
|
||||
pool.connect(users[0].signer).withdraw(dai.address, amountDAItoDeposit)
|
||||
pool.connect(users[0].signer).withdraw(dai.address, amountDAItoDeposit, users[0].address)
|
||||
).to.revertedWith(IS_PAUSED);
|
||||
|
||||
// Configurator unpauses the pool
|
||||
|
@ -187,7 +187,15 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
await expect(
|
||||
pool
|
||||
.connect(caller.signer)
|
||||
.flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 1, caller.address, '0x10', '0')
|
||||
.flashLoan(
|
||||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[flashAmount],
|
||||
[1],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
)
|
||||
).revertedWith(IS_PAUSED);
|
||||
|
||||
// Unpause pool
|
||||
|
|
Loading…
Reference in New Issue
Block a user