mirror of
https://github.com/Instadapp/aave-protocol-v2.git
synced 2024-07-29 21:47:30 +00:00
Merge pull request #165 from aave/feat/protocol-2.5/borrow-repay-same-block
Feat/protocol 2.5/disable borrow repay same block
This commit is contained in:
commit
5a33536b5f
51
contracts/mocks/tests/BorrowRepayTestMock.sol
Normal file
51
contracts/mocks/tests/BorrowRepayTestMock.sol
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
pragma solidity 0.6.12;
|
||||||
|
|
||||||
|
import {ILendingPool} from '../../interfaces/ILendingPool.sol';
|
||||||
|
import {MintableERC20} from '../tokens/MintableERC20.sol';
|
||||||
|
|
||||||
|
contract BorrowRepayTestMock {
|
||||||
|
ILendingPool _pool;
|
||||||
|
address _weth;
|
||||||
|
address _dai;
|
||||||
|
|
||||||
|
constructor(ILendingPool pool, address weth, address dai) public {
|
||||||
|
_pool = pool;
|
||||||
|
_weth = weth;
|
||||||
|
_dai = dai;
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeBorrowRepayVariable() external {
|
||||||
|
//mints 1 eth
|
||||||
|
MintableERC20(_weth).mint(1e18);
|
||||||
|
//deposits weth in the protocol
|
||||||
|
MintableERC20(_weth).approve(address(_pool),type(uint256).max);
|
||||||
|
_pool.deposit(_weth, 1e18, address(this),0);
|
||||||
|
//borrow 1 wei of weth at variable
|
||||||
|
_pool.borrow(_weth, 1, 2, 0, address(this));
|
||||||
|
//repay 1 wei of weth (expected to fail)
|
||||||
|
_pool.repay(_weth, 1, 2, address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeBorrowRepayStable() external {
|
||||||
|
//mints 1 eth
|
||||||
|
MintableERC20(_weth).mint(1e18);
|
||||||
|
//mints 1 dai
|
||||||
|
MintableERC20(_dai).mint(1e18);
|
||||||
|
//deposits weth in the protocol
|
||||||
|
MintableERC20(_weth).approve(address(_pool),type(uint256).max);
|
||||||
|
_pool.deposit(_weth, 1e18, address(this),0);
|
||||||
|
|
||||||
|
//deposits dai in the protocol
|
||||||
|
MintableERC20(_dai).approve(address(_pool),type(uint256).max);
|
||||||
|
_pool.deposit(_dai, 1e18, address(this),0);
|
||||||
|
|
||||||
|
//disabling dai as collateral so it can be borrowed at stable
|
||||||
|
_pool.setUserUseReserveAsCollateral(_dai, false);
|
||||||
|
//borrow 1 wei of dai at stable
|
||||||
|
_pool.borrow(_dai, 1, 1, 0, address(this));
|
||||||
|
|
||||||
|
//repay 1 wei of dai (expected to fail)
|
||||||
|
_pool.repay(_dai, 1, 1, address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -240,7 +240,6 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
|
|
||||||
///@inheritdoc ILendingPool
|
///@inheritdoc ILendingPool
|
||||||
function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused {
|
function rebalanceStableBorrowRate(address asset, address user) external override whenNotPaused {
|
||||||
|
|
||||||
DataTypes.ReserveData storage reserve = _reserves[asset];
|
DataTypes.ReserveData storage reserve = _reserves[asset];
|
||||||
DataTypes.ReserveCache memory reserveCache = reserve.cache();
|
DataTypes.ReserveCache memory reserveCache = reserve.cache();
|
||||||
|
|
||||||
|
@ -793,6 +792,9 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
vars.releaseUnderlying ? vars.amount : 0
|
vars.releaseUnderlying ? vars.amount : 0
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_lastBorrower = vars.user;
|
||||||
|
_lastBorrowTimestamp = uint40(block.timestamp);
|
||||||
|
|
||||||
if (vars.releaseUnderlying) {
|
if (vars.releaseUnderlying) {
|
||||||
IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
|
IAToken(reserveCache.aTokenAddress).transferUnderlyingTo(vars.user, vars.amount);
|
||||||
}
|
}
|
||||||
|
@ -908,6 +910,8 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
|
||||||
DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
|
DataTypes.InterestRateMode interestRateMode = DataTypes.InterestRateMode(rateMode);
|
||||||
|
|
||||||
ValidationLogic.validateRepay(
|
ValidationLogic.validateRepay(
|
||||||
|
_lastBorrower,
|
||||||
|
_lastBorrowTimestamp,
|
||||||
reserveCache,
|
reserveCache,
|
||||||
amount,
|
amount,
|
||||||
interestRateMode,
|
interestRateMode,
|
||||||
|
|
|
@ -33,4 +33,8 @@ contract LendingPoolStorage {
|
||||||
mapping(address => bool) _authorizedFlashBorrowers;
|
mapping(address => bool) _authorizedFlashBorrowers;
|
||||||
|
|
||||||
uint256 internal _flashLoanPremiumToProtocol;
|
uint256 internal _flashLoanPremiumToProtocol;
|
||||||
|
|
||||||
|
address internal _lastBorrower;
|
||||||
|
|
||||||
|
uint40 internal _lastBorrowTimestamp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,6 +113,7 @@ library Errors {
|
||||||
string public constant RL_STABLE_DEBT_NOT_ZERO = '89';
|
string public constant RL_STABLE_DEBT_NOT_ZERO = '89';
|
||||||
string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90';
|
string public constant RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90';
|
||||||
string public constant LP_CALLER_NOT_EOA = '91';
|
string public constant LP_CALLER_NOT_EOA = '91';
|
||||||
|
string public constant VL_SAME_BLOCK_BORROW_REPAY = '94';
|
||||||
string public constant LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95';
|
string public constant LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95';
|
||||||
string public constant LPC_FLASHLOAN_PREMIUM_INVALID = '96';
|
string public constant LPC_FLASHLOAN_PREMIUM_INVALID = '96';
|
||||||
|
|
||||||
|
|
|
@ -255,6 +255,8 @@ library ValidationLogic {
|
||||||
* @param variableDebt The borrow balance of the user
|
* @param variableDebt The borrow balance of the user
|
||||||
*/
|
*/
|
||||||
function validateRepay(
|
function validateRepay(
|
||||||
|
address lastBorrower,
|
||||||
|
uint40 lastBorrowTimestamp,
|
||||||
DataTypes.ReserveCache memory reserveCache,
|
DataTypes.ReserveCache memory reserveCache,
|
||||||
uint256 amountSent,
|
uint256 amountSent,
|
||||||
DataTypes.InterestRateMode rateMode,
|
DataTypes.InterestRateMode rateMode,
|
||||||
|
@ -268,6 +270,11 @@ library ValidationLogic {
|
||||||
|
|
||||||
require(amountSent > 0, Errors.VL_INVALID_AMOUNT);
|
require(amountSent > 0, Errors.VL_INVALID_AMOUNT);
|
||||||
|
|
||||||
|
require(
|
||||||
|
lastBorrower != onBehalfOf || lastBorrowTimestamp != uint40(block.timestamp),
|
||||||
|
Errors.VL_SAME_BLOCK_BORROW_REPAY
|
||||||
|
);
|
||||||
|
|
||||||
require(
|
require(
|
||||||
(stableDebt > 0 &&
|
(stableDebt > 0 &&
|
||||||
DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) ||
|
DataTypes.InterestRateMode(rateMode) == DataTypes.InterestRateMode.STABLE) ||
|
||||||
|
@ -347,7 +354,6 @@ library ValidationLogic {
|
||||||
IERC20 variableDebtToken,
|
IERC20 variableDebtToken,
|
||||||
address aTokenAddress
|
address aTokenAddress
|
||||||
) external view {
|
) external view {
|
||||||
|
|
||||||
// to avoid potential abuses using flashloans, the rebalance stable rate must happen through an EOA
|
// to avoid potential abuses using flashloans, the rebalance stable rate must happen through an EOA
|
||||||
require(!address(msg.sender).isContract(), Errors.LP_CALLER_NOT_EOA);
|
require(!address(msg.sender).isContract(), Errors.LP_CALLER_NOT_EOA);
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,7 @@ export enum ProtocolErrors {
|
||||||
RL_ATOKEN_SUPPLY_NOT_ZERO = '88',
|
RL_ATOKEN_SUPPLY_NOT_ZERO = '88',
|
||||||
RL_STABLE_DEBT_NOT_ZERO = '89',
|
RL_STABLE_DEBT_NOT_ZERO = '89',
|
||||||
RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90',
|
RL_VARIABLE_DEBT_SUPPLY_NOT_ZERO = '90',
|
||||||
|
VL_SAME_BLOCK_BORROW_REPAY = '94',
|
||||||
LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95',
|
LPC_FLASHLOAN_PREMIUMS_MISMATCH = '95',
|
||||||
LPC_FLASHLOAN_PREMIUM_INVALID = '96',
|
LPC_FLASHLOAN_PREMIUM_INVALID = '96',
|
||||||
|
|
||||||
|
|
52
test-suites/test-aave/borrow-repay-same-tx.ts
Normal file
52
test-suites/test-aave/borrow-repay-same-tx.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { TestEnv, makeSuite } from './helpers/make-suite';
|
||||||
|
import {
|
||||||
|
APPROVAL_AMOUNT_LENDING_POOL,
|
||||||
|
MAX_UINT_AMOUNT,
|
||||||
|
RAY,
|
||||||
|
MAX_BORROW_CAP,
|
||||||
|
MAX_SUPPLY_CAP,
|
||||||
|
} from '../../helpers/constants';
|
||||||
|
import { ProtocolErrors } from '../../helpers/types';
|
||||||
|
import {
|
||||||
|
BorrowRepayTestMock,
|
||||||
|
BorrowRepayTestMockFactory,
|
||||||
|
MintableERC20,
|
||||||
|
WETH9,
|
||||||
|
WETH9Mocked,
|
||||||
|
} from '../../types';
|
||||||
|
import { parseEther } from '@ethersproject/units';
|
||||||
|
import { BigNumber } from '@ethersproject/bignumber';
|
||||||
|
import { waitForTx } from '../../helpers/misc-utils';
|
||||||
|
import { getFirstSigner } from '../../helpers/contracts-getters';
|
||||||
|
|
||||||
|
const { expect } = require('chai');
|
||||||
|
|
||||||
|
makeSuite('Borrow/repay in the same tx', (testEnv: TestEnv) => {
|
||||||
|
const { VL_SAME_BLOCK_BORROW_REPAY } = ProtocolErrors;
|
||||||
|
const unitParse = async (token: WETH9Mocked | MintableERC20, nb: string) =>
|
||||||
|
BigNumber.from(nb).mul(BigNumber.from('10').pow((await token.decimals()) - 3));
|
||||||
|
|
||||||
|
let testContract: BorrowRepayTestMock;
|
||||||
|
|
||||||
|
it('Deploys the test contract', async () => {
|
||||||
|
const { weth, dai, pool } = testEnv;
|
||||||
|
|
||||||
|
testContract = await (
|
||||||
|
await new BorrowRepayTestMockFactory(await getFirstSigner())
|
||||||
|
).deploy(pool.address, weth.address, dai.address);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Executes a test borrow/repay in the same transaction at variable (revert expected)', async () => {
|
||||||
|
await expect(testContract.executeBorrowRepayVariable()).to.be.revertedWith(
|
||||||
|
VL_SAME_BLOCK_BORROW_REPAY,
|
||||||
|
'Borrow/repay in the same transaction did not revert as expected'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Executes a test borrow/repay in the same transaction at stabke (revert expected)', async () => {
|
||||||
|
await expect(testContract.executeBorrowRepayStable()).to.be.revertedWith(
|
||||||
|
VL_SAME_BLOCK_BORROW_REPAY,
|
||||||
|
'Borrow/repay in the same transaction did not revert as expected'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user