diff --git a/contracts/flashloan/interfaces/IFlashLoanReceiver.sol b/contracts/flashloan/interfaces/IFlashLoanReceiver.sol index e3c2636c..68292aa1 100644 --- a/contracts/flashloan/interfaces/IFlashLoanReceiver.sol +++ b/contracts/flashloan/interfaces/IFlashLoanReceiver.sol @@ -13,5 +13,5 @@ interface IFlashLoanReceiver { uint256 amount, uint256 fee, bytes calldata params - ) external; + ) external returns(bool); } diff --git a/contracts/lendingpool/LendingPool.sol b/contracts/lendingpool/LendingPool.sol index 6016ee95..186de3a6 100644 --- a/contracts/lendingpool/LendingPool.sol +++ b/contracts/lendingpool/LendingPool.sol @@ -572,7 +572,10 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage IAToken(vars.aTokenAddress).transferUnderlyingTo(receiverAddress, amount); //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); diff --git a/contracts/libraries/helpers/Errors.sol b/contracts/libraries/helpers/Errors.sol index cf0923c7..ee71efaa 100644 --- a/contracts/libraries/helpers/Errors.sol +++ b/contracts/libraries/helpers/Errors.sol @@ -44,6 +44,7 @@ library Errors { string public constant FAILED_COLLATERAL_SWAP = '55'; string public constant INVALID_EQUAL_ASSETS_TO_SWAP = '56'; string public constant NO_MORE_RESERVES_ALLOWED = '59'; + string public constant INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60'; // require error messages - aToken - DebtTokens string public constant CALLER_MUST_BE_LENDING_POOL = '28'; // 'The caller of this function must be a lending pool' diff --git a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol index 112084a7..77e4d5a4 100644 --- a/contracts/mocks/flashloan/MockFlashLoanReceiver.sol +++ b/contracts/mocks/flashloan/MockFlashLoanReceiver.sol @@ -20,6 +20,7 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase { bool _failExecution; uint256 _amountToApprove; + bool _simulateEOA; constructor(ILendingPoolAddressesProvider provider) public FlashLoanReceiverBase(provider) {} @@ -31,16 +32,25 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase { _amountToApprove = amountToApprove; } + function setSimulateEOA(bool flag) public { + _simulateEOA = flag; + } + function amountToApprove() public view returns (uint256) { return _amountToApprove; } + function simulateEOA() public view returns(bool) { + return _simulateEOA; + } + function executeOperation( address reserve, uint256 amount, uint256 fee, bytes memory params - ) public override { + ) public override returns(bool) { + params; //mint to this contract the specific amount MintableERC20 token = MintableERC20(reserve); @@ -51,7 +61,7 @@ contract MockFlashLoanReceiver is FlashLoanReceiverBase { if (_failExecution) { emit ExecutedWithFail(reserve, amount, fee); - return; + return !_simulateEOA; } //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); emit ExecutedWithSuccess(reserve, amount, fee); + + return true; } } diff --git a/helpers/types.ts b/helpers/types.ts index a8d963f0..7ed1082a 100644 --- a/helpers/types.ts +++ b/helpers/types.ts @@ -77,6 +77,7 @@ export enum ProtocolErrors { 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' 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 CALLER_MUST_BE_LENDING_POOL = '28', // 'The caller of this function must be a lending pool' diff --git a/test/flashloan.spec.ts b/test/flashloan.spec.ts index c2be90ed..b82b6433 100644 --- a/test/flashloan.spec.ts +++ b/test/flashloan.spec.ts @@ -24,6 +24,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { INVALID_FLASHLOAN_MODE, SAFEERC20_LOWLEVEL_CALL, IS_PAUSED, + INVALID_FLASH_LOAN_EXECUTOR_RETURN } = ProtocolErrors; before(async () => { @@ -116,9 +117,30 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => { ).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 () => { const {pool, weth, users} = testEnv; const caller = users[1]; + await _mockFlashLoanReceiver.setSimulateEOA(false) await _mockFlashLoanReceiver.setFailExecutionTransfer(true); await expect(