Merge branch 'fix/60-flashloan-attack' into 'master'

Fixes #60

Closes #60

See merge request aave-tech/protocol-v2!68
This commit is contained in:
The-3D 2020-10-12 13:11:38 +00:00
commit fc9b096f23
6 changed files with 43 additions and 4 deletions

View File

@ -13,5 +13,5 @@ interface IFlashLoanReceiver {
uint256 amount, uint256 amount,
uint256 fee, uint256 fee,
bytes calldata params bytes calldata params
) external; ) external returns(bool);
} }

View File

@ -572,7 +572,10 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
IAToken(vars.aTokenAddress).transferUnderlyingTo(receiverAddress, amount); IAToken(vars.aTokenAddress).transferUnderlyingTo(receiverAddress, amount);
//execute action of the receiver //execute action of the receiver
vars.receiver.executeOperation(asset, amount, vars.premium, params); require(
vars.receiver.executeOperation(asset, amount, vars.premium, params),
Errors.INVALID_FLASH_LOAN_EXECUTOR_RETURN
);
vars.amountPlusPremium = amount.add(vars.premium); vars.amountPlusPremium = amount.add(vars.premium);

View File

@ -44,6 +44,7 @@ library Errors {
string public constant FAILED_COLLATERAL_SWAP = '55'; string public constant FAILED_COLLATERAL_SWAP = '55';
string public constant INVALID_EQUAL_ASSETS_TO_SWAP = '56'; string public constant INVALID_EQUAL_ASSETS_TO_SWAP = '56';
string public constant NO_MORE_RESERVES_ALLOWED = '59'; string public constant NO_MORE_RESERVES_ALLOWED = '59';
string public constant INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60';
// require error messages - aToken - DebtTokens // require error messages - aToken - DebtTokens
string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool' string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool'

View File

@ -20,6 +20,7 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
bool _failExecution; bool _failExecution;
uint256 _amountToApprove; uint256 _amountToApprove;
bool _simulateEOA;
constructor(ILendingPoolAddressesProvider provider) public FlashLoanReceiverBase(provider) {} constructor(ILendingPoolAddressesProvider provider) public FlashLoanReceiverBase(provider) {}
@ -31,16 +32,25 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
_amountToApprove = amountToApprove; _amountToApprove = amountToApprove;
} }
function setSimulateEOA(bool flag) public {
_simulateEOA = flag;
}
function amountToApprove() public view returns (uint256) { function amountToApprove() public view returns (uint256) {
return _amountToApprove; return _amountToApprove;
} }
function simulateEOA() public view returns(bool) {
return _simulateEOA;
}
function executeOperation( function executeOperation(
address reserve, address reserve,
uint256 amount, uint256 amount,
uint256 fee, uint256 fee,
bytes memory params bytes memory params
) public override { ) public override returns(bool) {
params;
//mint to this contract the specific amount //mint to this contract the specific amount
MintableERC20 token = MintableERC20(reserve); MintableERC20 token = MintableERC20(reserve);
@ -51,7 +61,7 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
if (_failExecution) { if (_failExecution) {
emit ExecutedWithFail(reserve, amount, fee); emit ExecutedWithFail(reserve, amount, fee);
return; return !_simulateEOA;
} }
//execution does not fail - mint tokens and return them to the _destination //execution does not fail - mint tokens and return them to the _destination
@ -62,5 +72,7 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase {
IERC20(reserve).approve(_addressesProvider.getLendingPool(), amountToReturn); IERC20(reserve).approve(_addressesProvider.getLendingPool(), amountToReturn);
emit ExecutedWithSuccess(reserve, amount, fee); emit ExecutedWithSuccess(reserve, amount, fee);
return true;
} }
} }

View File

@ -77,6 +77,7 @@ export enum ProtocolErrors {
REQUESTED_AMOUNT_TOO_SMALL = '25', // 'The requested amount is too small for a FlashLoan.' REQUESTED_AMOUNT_TOO_SMALL = '25', // 'The requested amount is too small for a FlashLoan.'
INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26', // 'The actual balance of the protocol is inconsistent' INCONSISTENT_PROTOCOL_ACTUAL_BALANCE = '26', // 'The actual balance of the protocol is inconsistent'
CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27', // 'The actual balance of the protocol is inconsistent' CALLER_NOT_LENDING_POOL_CONFIGURATOR = '27', // 'The actual balance of the protocol is inconsistent'
INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60', // The flash loan received returned 0 (EOA)
// require error messages - aToken // require error messages - aToken
CALLER_MUST_BE_LENDING_POOL = '28', // 'The caller of this function must be a lending pool' CALLER_MUST_BE_LENDING_POOL = '28', // 'The caller of this function must be a lending pool'

View File

@ -24,6 +24,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
INVALID_FLASHLOAN_MODE, INVALID_FLASHLOAN_MODE,
SAFEERC20_LOWLEVEL_CALL, SAFEERC20_LOWLEVEL_CALL,
IS_PAUSED, IS_PAUSED,
INVALID_FLASH_LOAN_EXECUTOR_RETURN
} = ProtocolErrors; } = ProtocolErrors;
before(async () => { before(async () => {
@ -116,9 +117,30 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE); ).to.be.revertedWith(TRANSFER_AMOUNT_EXCEEDS_BALANCE);
}); });
it('Takes WETH flashloan, simulating a receiver as EOA (revert expected)', async () => {
const {pool, weth, users} = testEnv;
const caller = users[1];
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
await _mockFlashLoanReceiver.setSimulateEOA(true)
await expect(
pool
.connect(caller.signer)
.flashLoan(
_mockFlashLoanReceiver.address,
weth.address,
ethers.utils.parseEther('0.8'),
0,
'0x10',
'0'
)
).to.be.revertedWith(INVALID_FLASH_LOAN_EXECUTOR_RETURN);
});
it('Takes a WETH flashloan with an invalid mode. (revert expected)', async () => { it('Takes a WETH flashloan with an invalid mode. (revert expected)', async () => {
const {pool, weth, users} = testEnv; const {pool, weth, users} = testEnv;
const caller = users[1]; const caller = users[1];
await _mockFlashLoanReceiver.setSimulateEOA(false)
await _mockFlashLoanReceiver.setFailExecutionTransfer(true); await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
await expect( await expect(