mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge branch 'master' into fix/51
This commit is contained in:
commit
96d00edd00
|
@ -33,11 +33,11 @@ interface ILendingPool {
|
|||
event Withdraw(address indexed reserve, address indexed user, address indexed to, uint256 amount);
|
||||
|
||||
event BorrowAllowanceDelegated(
|
||||
address indexed asset,
|
||||
address indexed fromUser,
|
||||
address indexed toUser,
|
||||
uint256 interestRateMode,
|
||||
uint256 amount
|
||||
address[] assets,
|
||||
uint256[] interestRateModes,
|
||||
uint256[] amounts
|
||||
);
|
||||
/**
|
||||
* @dev emitted on borrow
|
||||
|
@ -100,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
|
||||
);
|
||||
|
||||
|
@ -198,17 +196,17 @@ interface ILendingPool {
|
|||
) external;
|
||||
|
||||
/**
|
||||
* @dev Sets allowance to borrow on a certain type of debt asset for a certain user address
|
||||
* @param asset The underlying asset of the debt token
|
||||
* @dev Sets allowance to borrow on a certain type of debt assets for a certain user address
|
||||
* @param assets The underlying asset of each debt token
|
||||
* @param user The user to give allowance to
|
||||
* @param interestRateMode Type of debt: 1 for stable, 2 for variable
|
||||
* @param amount Allowance amount to borrow
|
||||
* @param interestRateModes Types of debt: 1 for stable, 2 for variable
|
||||
* @param amounts Allowance amounts to borrow
|
||||
**/
|
||||
function delegateBorrowAllowance(
|
||||
address asset,
|
||||
address[] calldata assets,
|
||||
address user,
|
||||
uint256 interestRateMode,
|
||||
uint256 amount
|
||||
uint256[] calldata interestRateModes,
|
||||
uint256[] calldata amounts
|
||||
) external;
|
||||
|
||||
function getBorrowAllowance(
|
||||
|
@ -295,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
|
||||
**/
|
||||
|
@ -303,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
|
||||
|
|
|
@ -186,23 +186,32 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
}
|
||||
|
||||
/**
|
||||
* @dev Sets allowance to borrow on a certain type of debt asset for a certain user address
|
||||
* @param asset The underlying asset of the debt token
|
||||
* @dev Sets allowance to borrow on a certain type of debt assets for a certain user address
|
||||
* @param assets The underlying asset of each debt token
|
||||
* @param user The user to give allowance to
|
||||
* @param interestRateMode Type of debt: 1 for stable, 2 for variable
|
||||
* @param amount Allowance amount to borrow
|
||||
* @param interestRateModes Types of debt: 1 for stable, 2 for variable
|
||||
* @param amounts Allowance amounts to borrow
|
||||
**/
|
||||
function delegateBorrowAllowance(
|
||||
address asset,
|
||||
address[] calldata assets,
|
||||
address user,
|
||||
uint256 interestRateMode,
|
||||
uint256 amount
|
||||
uint256[] calldata interestRateModes,
|
||||
uint256[] calldata amounts
|
||||
) external override {
|
||||
_whenNotPaused();
|
||||
address debtToken = _reserves[asset].getDebtTokenAddress(interestRateMode);
|
||||
|
||||
_borrowAllowance[debtToken][msg.sender][user] = amount;
|
||||
emit BorrowAllowanceDelegated(asset, msg.sender, user, interestRateMode, amount);
|
||||
uint256 countAssets = assets.length;
|
||||
require(
|
||||
countAssets == interestRateModes.length && countAssets == amounts.length,
|
||||
Errors.INCONSISTENT_PARAMS_LENGTH
|
||||
);
|
||||
|
||||
for (uint256 i = 0; i < countAssets; i++) {
|
||||
address debtToken = _reserves[assets[i]].getDebtTokenAddress(interestRateModes[i]);
|
||||
_borrowAllowance[debtToken][msg.sender][user] = amounts[i];
|
||||
}
|
||||
|
||||
emit BorrowAllowanceDelegated(msg.sender, user, assets, interestRateModes, amounts);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -493,13 +502,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -509,7 +518,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
|
||||
|
@ -518,7 +527,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
|
||||
|
@ -527,13 +536,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;
|
||||
|
@ -555,10 +563,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(),
|
||||
|
@ -578,13 +585,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
|
||||
|
@ -595,14 +600,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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -698,7 +710,13 @@ 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();
|
||||
}
|
||||
|
||||
|
@ -882,6 +900,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
|||
);
|
||||
|
||||
ValidationLogic.validateBorrow(
|
||||
vars.asset,
|
||||
reserve,
|
||||
vars.onBehalfOf,
|
||||
vars.amount,
|
||||
|
|
|
@ -155,7 +155,11 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param proxy the aToken proxy address
|
||||
* @param implementation the new aToken implementation
|
||||
**/
|
||||
event ATokenUpgraded(address indexed asset, address indexed proxy, address indexed implementation);
|
||||
event ATokenUpgraded(
|
||||
address indexed asset,
|
||||
address indexed proxy,
|
||||
address indexed implementation
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev emitted when the implementation of a stable debt token is upgraded
|
||||
|
@ -163,7 +167,11 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param proxy the stable debt token proxy address
|
||||
* @param implementation the new aToken implementation
|
||||
**/
|
||||
event StableDebtTokenUpgraded(address indexed asset, address indexed proxy, address indexed implementation);
|
||||
event StableDebtTokenUpgraded(
|
||||
address indexed asset,
|
||||
address indexed proxy,
|
||||
address indexed implementation
|
||||
);
|
||||
|
||||
/**
|
||||
* @dev emitted when the implementation of a variable debt token is upgraded
|
||||
|
@ -171,7 +179,11 @@ contract LendingPoolConfigurator is VersionedInitializable {
|
|||
* @param proxy the variable debt token proxy address
|
||||
* @param implementation the new aToken implementation
|
||||
**/
|
||||
event VariableDebtTokenUpgraded(address indexed asset, address indexed proxy, address indexed implementation);
|
||||
event VariableDebtTokenUpgraded(
|
||||
address indexed asset,
|
||||
address indexed proxy,
|
||||
address indexed implementation
|
||||
);
|
||||
|
||||
ILendingPoolAddressesProvider internal addressesProvider;
|
||||
ILendingPool internal pool;
|
||||
|
|
|
@ -99,6 +99,8 @@ library Errors {
|
|||
string public constant INVALID_DECIMALS = '73';
|
||||
string public constant INVALID_RESERVE_FACTOR = '74';
|
||||
|
||||
// Credit delegation
|
||||
string public constant INCONSISTENT_PARAMS_LENGTH = '75';
|
||||
|
||||
enum CollateralManagerErrors {
|
||||
NO_ERROR,
|
||||
|
|
|
@ -33,7 +33,7 @@ library ValidationLogic {
|
|||
* @param reserve the reserve state on which the user is depositing
|
||||
* @param amount the amount to be deposited
|
||||
*/
|
||||
function validateDeposit(ReserveLogic.ReserveData storage reserve, uint256 amount) internal view {
|
||||
function validateDeposit(ReserveLogic.ReserveData storage reserve, uint256 amount) external view {
|
||||
(bool isActive, bool isFreezed, , ) = reserve.configuration.getFlags();
|
||||
|
||||
require(amount > 0, Errors.AMOUNT_NOT_GREATER_THAN_0);
|
||||
|
@ -61,7 +61,7 @@ library ValidationLogic {
|
|||
mapping(uint256 => address) storage reserves,
|
||||
uint256 reservesCount,
|
||||
address oracle
|
||||
) internal view {
|
||||
) external view {
|
||||
require(amount > 0, Errors.AMOUNT_NOT_GREATER_THAN_0);
|
||||
|
||||
require(amount <= userBalance, Errors.NOT_ENOUGH_AVAILABLE_USER_BALANCE);
|
||||
|
@ -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);
|
||||
|
@ -283,8 +287,7 @@ library ValidationLogic {
|
|||
require(
|
||||
!userConfig.isUsingAsCollateral(reserve.id) ||
|
||||
reserve.configuration.getLtv() == 0 ||
|
||||
stableDebt.add(variableDebt) >
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ contract StableDebtToken is IStableDebtToken, DebtTokenBase {
|
|||
address user,
|
||||
uint256 amount,
|
||||
uint256 rate
|
||||
) external override onlyLendingPool returns(bool) {
|
||||
) external override onlyLendingPool returns (bool) {
|
||||
MintLocalVars memory vars;
|
||||
|
||||
//cumulates the user debt
|
||||
|
@ -288,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 returns(bool);
|
||||
) external returns (bool);
|
||||
|
||||
/**
|
||||
* @dev burns debt of the target user.
|
||||
|
|
|
@ -266,4 +266,4 @@
|
|||
"deployer": "0xc783df8a850f42e7F7e57013759C285caa701eB6"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,7 +140,6 @@ export const deployAaveLibraries = async (
|
|||
return {
|
||||
['__$5201a97c05ba6aa659e2f36a933dd51801$__']: validationLogic.address,
|
||||
['__$d3b4366daeb9cadc7528af6145b50b2183$__']: reserveLogic.address,
|
||||
['__$4c26be947d349222af871a3168b3fe584b$__']: genericLogic.address,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
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";
|
||||
}
|
||||
|
||||
|
||||
|
214
specs/harness/LendingPoolHarnessForVariableDebtToken.sol
Normal file
214
specs/harness/LendingPoolHarnessForVariableDebtToken.sol
Normal file
|
@ -0,0 +1,214 @@
|
|||
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);
|
||||
}
|
||||
}
|
26
specs/harness/StableDebtTokenHarness.sol
Normal file
26
specs/harness/StableDebtTokenHarness.sol
Normal file
|
@ -0,0 +1,26 @@
|
|||
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);
|
||||
}
|
||||
}
|
56
specs/harness/UserConfigurationHarness.sol
Normal file
56
specs/harness/UserConfigurationHarness.sol
Normal file
|
@ -0,0 +1,56 @@
|
|||
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'
|
||||
|
@ -448,7 +464,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
// Deposited for onBehalfOf user already, delegate borrow allowance
|
||||
await pool
|
||||
.connect(onBehalfOf.signer)
|
||||
.delegateBorrowAllowance(weth.address, caller.address, 1, flashAmount);
|
||||
.delegateBorrowAllowance([weth.address], caller.address, [1], [flashAmount]);
|
||||
|
||||
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
|
||||
|
||||
|
@ -458,7 +474,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[flashAmount],
|
||||
1,
|
||||
[1],
|
||||
onBehalfOf.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
|
|
@ -277,9 +277,9 @@ export const withdraw = async (
|
|||
};
|
||||
|
||||
export const delegateBorrowAllowance = async (
|
||||
reserveSymbol: string,
|
||||
amount: string,
|
||||
interestRateMode: string,
|
||||
reserveSymbols: string[],
|
||||
amounts: string[],
|
||||
interestRateModes: string[],
|
||||
user: SignerWithAddress,
|
||||
receiver: tEthereumAddress,
|
||||
expectedResult: string,
|
||||
|
@ -288,20 +288,32 @@ export const delegateBorrowAllowance = async (
|
|||
) => {
|
||||
const {pool} = testEnv;
|
||||
|
||||
const reserve = await getReserveAddressFromSymbol(reserveSymbol);
|
||||
const amountToDelegate = await convertToCurrencyDecimals(reserve, amount);
|
||||
const reserves: tEthereumAddress[] = [];
|
||||
const amountsToDelegate: tEthereumAddress[] = [];
|
||||
for (const reserveSymbol of reserveSymbols) {
|
||||
const newLength = reserves.push(await getReserveAddressFromSymbol(reserveSymbol));
|
||||
amountsToDelegate.push(
|
||||
await (
|
||||
await convertToCurrencyDecimals(reserves[newLength - 1], amounts[newLength - 1])
|
||||
).toString()
|
||||
);
|
||||
}
|
||||
|
||||
const delegateAllowancePromise = pool
|
||||
.connect(user.signer)
|
||||
.delegateBorrowAllowance(reserve, receiver, interestRateMode, amountToDelegate.toString());
|
||||
.delegateBorrowAllowance(reserves, receiver, interestRateModes, amountsToDelegate);
|
||||
if (expectedResult === 'revert') {
|
||||
await expect(delegateAllowancePromise, revertMessage).to.be.reverted;
|
||||
return;
|
||||
} else {
|
||||
await delegateAllowancePromise;
|
||||
expect(
|
||||
(await pool.getBorrowAllowance(user.address, receiver, reserve, interestRateMode)).toString()
|
||||
).to.be.equal(amountToDelegate.toString(), 'borrowAllowance are set incorrectly');
|
||||
for (const [i, reserve] of reserves.entries()) {
|
||||
expect(
|
||||
(
|
||||
await pool.getBorrowAllowance(user.address, receiver, reserve, interestRateModes[i])
|
||||
).toString()
|
||||
).to.be.equal(amountsToDelegate[i], 'borrowAllowance are set incorrectly');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -121,9 +121,9 @@ const executeAction = async (action: Action, users: SignerWithAddress[], testEnv
|
|||
}
|
||||
|
||||
await delegateBorrowAllowance(
|
||||
reserve,
|
||||
amount,
|
||||
rateMode,
|
||||
[reserve],
|
||||
[amount],
|
||||
[rateMode],
|
||||
user,
|
||||
toUser,
|
||||
expected,
|
||||
|
|
|
@ -133,7 +133,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
|
||||
// Try to execute liquidation
|
||||
await expect(
|
||||
pool.connect(user.signer).delegateBorrowAllowance(dai.address, toUser.address, '1', '1')
|
||||
pool.connect(user.signer).delegateBorrowAllowance([dai.address], toUser.address, ['1'], ['1'])
|
||||
).revertedWith(IS_PAUSED);
|
||||
|
||||
// Unpause the pool
|
||||
|
@ -191,7 +191,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
|
|||
_mockFlashLoanReceiver.address,
|
||||
[weth.address],
|
||||
[flashAmount],
|
||||
1,
|
||||
[1],
|
||||
caller.address,
|
||||
'0x10',
|
||||
'0'
|
||||
|
|
Loading…
Reference in New Issue
Block a user