Merge branch 'master' of gitlab.com:aave-tech/protocol-v2 into 97-create-a-utility-contract-to-deposit-withdraw-repay-with-eth

This commit is contained in:
David Racero 2020-10-29 16:20:12 +01:00
commit 2892e69df0
5 changed files with 133 additions and 10 deletions

View File

@ -99,6 +99,8 @@ interface ILendingPool {
/** /**
* @dev emitted when a flashloan is executed * @dev emitted when a flashloan is executed
* @param target the address of the flashLoanReceiver * @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 assets the address of the assets being flashborrowed
* @param amounts the amount requested * @param amounts the amount requested
* @param premiums the total fee on the amount * @param premiums the total fee on the amount
@ -107,6 +109,7 @@ interface ILendingPool {
event FlashLoan( event FlashLoan(
address indexed target, address indexed target,
uint256 mode, uint256 mode,
address indexed onBehalfOf,
address[] assets, address[] assets,
uint256[] amounts, uint256[] amounts,
uint256[] premiums, uint256[] premiums,
@ -295,6 +298,7 @@ interface ILendingPool {
address[] calldata assets, address[] calldata assets,
uint256[] calldata amounts, uint256[] calldata amounts,
uint256 mode, uint256 mode,
address onBehalfOf,
bytes calldata params, bytes calldata params,
uint16 referralCode uint16 referralCode
) external; ) external;

View File

@ -503,6 +503,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
* @param assets The addresss of the assets being flashborrowed * @param assets The addresss of the assets being flashborrowed
* @param amounts The amounts requested for this flashloan for each asset * @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 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 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 params Variadic packed params to pass to the receiver as extra information
* @param referralCode Referral code of the flash loan * @param referralCode Referral code of the flash loan
**/ **/
@ -511,6 +512,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
address[] calldata assets, address[] calldata assets,
uint256[] calldata amounts, uint256[] calldata amounts,
uint256 mode, uint256 mode,
address onBehalfOf,
bytes calldata params, bytes calldata params,
uint16 referralCode uint16 referralCode
) external override { ) external override {
@ -568,13 +570,23 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
vars.currentAmountPlusPremium vars.currentAmountPlusPremium
); );
} else { } else {
if (msg.sender != onBehalfOf) {
address debtToken = _reserves[vars.currentAsset].getDebtTokenAddress(mode);
_borrowAllowance[debtToken][onBehalfOf][msg
.sender] = _borrowAllowance[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 //if the user didn't choose to return the funds, the system checks if there
//is enough collateral and eventually open a position //is enough collateral and eventually open a position
_executeBorrow( _executeBorrow(
ExecuteBorrowParams( ExecuteBorrowParams(
vars.currentAsset, vars.currentAsset,
msg.sender, msg.sender,
msg.sender, onBehalfOf,
vars.currentAmount, vars.currentAmount,
mode, mode,
vars.currentATokenAddress, vars.currentATokenAddress,
@ -583,7 +595,7 @@ contract LendingPool is VersionedInitializable, ILendingPool, LendingPoolStorage
) )
); );
} }
emit FlashLoan(receiverAddress, mode, assets, amounts, premiums, referralCode); emit FlashLoan(receiverAddress, mode, onBehalfOf, assets, amounts, premiums, referralCode);
} }
} }

View File

@ -94,6 +94,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'
BORROW_ALLOWANCE_ARE_NOT_ENOUGH = '54', // User borrows on behalf, but allowance are too small
INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60', // The flash loan received returned 0 (EOA) INVALID_FLASH_LOAN_EXECUTOR_RETURN = '60', // The flash loan received returned 0 (EOA)
// require error messages - aToken // require error messages - aToken

View File

@ -22,6 +22,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
SAFEERC20_LOWLEVEL_CALL, SAFEERC20_LOWLEVEL_CALL,
IS_PAUSED, IS_PAUSED,
INVALID_FLASH_LOAN_EXECUTOR_RETURN, INVALID_FLASH_LOAN_EXECUTOR_RETURN,
BORROW_ALLOWANCE_ARE_NOT_ENOUGH,
} = ProtocolErrors; } = ProtocolErrors;
before(async () => { before(async () => {
@ -48,6 +49,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
[weth.address], [weth.address],
[ethers.utils.parseEther('0.8')], [ethers.utils.parseEther('0.8')],
0, 0,
_mockFlashLoanReceiver.address,
'0x10', '0x10',
'0' '0'
); );
@ -77,6 +79,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
[weth.address], [weth.address],
['1000720000000000000'], ['1000720000000000000'],
0, 0,
_mockFlashLoanReceiver.address,
'0x10', '0x10',
'0' '0'
); );
@ -108,6 +111,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
[weth.address], [weth.address],
[ethers.utils.parseEther('0.8')], [ethers.utils.parseEther('0.8')],
0, 0,
caller.address,
'0x10', '0x10',
'0' '0'
) )
@ -128,6 +132,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
[weth.address], [weth.address],
[ethers.utils.parseEther('0.8')], [ethers.utils.parseEther('0.8')],
0, 0,
caller.address,
'0x10', '0x10',
'0' '0'
) )
@ -148,6 +153,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
[weth.address], [weth.address],
[ethers.utils.parseEther('0.8')], [ethers.utils.parseEther('0.8')],
4, 4,
caller.address,
'0x10', '0x10',
'0' '0'
) )
@ -176,6 +182,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
[weth.address], [weth.address],
[ethers.utils.parseEther('0.8')], [ethers.utils.parseEther('0.8')],
2, 2,
caller.address,
'0x10', '0x10',
'0' '0'
); );
@ -194,14 +201,16 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => { it('tries to take a flashloan that is bigger than the available liquidity (revert expected)', async () => {
const {pool, weth} = testEnv; const {pool, weth, users} = testEnv;
const caller = users[1];
await expect( await expect(
pool.flashLoan( pool.connect(caller.signer).flashLoan(
_mockFlashLoanReceiver.address, _mockFlashLoanReceiver.address,
[weth.address], [weth.address],
['1004415000000000000'], //slightly higher than the available liquidity ['1004415000000000000'], //slightly higher than the available liquidity
2, 2,
caller.address,
'0x10', '0x10',
'0' '0'
), ),
@ -210,10 +219,11 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
}); });
it('tries to take a flashloan using a non contract address as receiver (revert expected)', async () => { it('tries to take a flashloan using a non contract address as receiver (revert expected)', async () => {
const {pool, deployer, weth} = testEnv; const {pool, deployer, weth, users} = testEnv;
const caller = users[1];
await expect( await expect(
pool.flashLoan(deployer.address, [weth.address], ['1000000000000000000'], 2, '0x10', '0') pool.flashLoan(deployer.address, [weth.address], ['1000000000000000000'], 2, caller.address, '0x10', '0')
).to.be.reverted; ).to.be.reverted;
}); });
@ -242,6 +252,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
[usdc.address], [usdc.address],
[flashloanAmount], [flashloanAmount],
0, 0,
_mockFlashLoanReceiver.address,
'0x10', '0x10',
'0' '0'
); );
@ -284,6 +295,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
[usdc.address], [usdc.address],
[flashloanAmount], [flashloanAmount],
2, 2,
caller.address,
'0x10', '0x10',
'0' '0'
) )
@ -309,7 +321,7 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await pool await pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan(_mockFlashLoanReceiver.address, [usdc.address], [flashloanAmount], 2, '0x10', '0'); .flashLoan(_mockFlashLoanReceiver.address, [usdc.address], [flashloanAmount], 2, caller.address, '0x10', '0');
const {variableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses( const {variableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(
usdc.address usdc.address
); );
@ -344,7 +356,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await expect( await expect(
pool pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 0, '0x10', '0') .flashLoan(
_mockFlashLoanReceiver.address,
[weth.address],
[flashAmount],
0,
caller.address,
'0x10',
'0'
)
).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL); ).to.be.revertedWith(SAFEERC20_LOWLEVEL_CALL);
}); });
@ -359,7 +379,15 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
await pool await pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 1, '0x10', '0'); .flashLoan(
_mockFlashLoanReceiver.address,
[weth.address],
[flashAmount],
1,
caller.address,
'0x10',
'0'
);
const {stableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(weth.address); const {stableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(weth.address);
@ -372,4 +400,82 @@ makeSuite('LendingPool FlashLoan function', (testEnv: TestEnv) => {
expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt'); expect(callerDebt.toString()).to.be.equal('800000000000000000', 'Invalid user debt');
}); });
it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user without allowance', async () => {
const {dai, pool, weth, users, helpersContract} = testEnv;
const caller = users[5];
const onBehalfOf = users[4];
// Deposit 1000 dai for onBehalfOf user
await dai.connect(onBehalfOf.signer).mint(await convertToCurrencyDecimals(dai.address, '1000'));
await dai.connect(onBehalfOf.signer).approve(pool.address, APPROVAL_AMOUNT_LENDING_POOL);
const amountToDeposit = await convertToCurrencyDecimals(dai.address, '1000');
await pool
.connect(onBehalfOf.signer)
.deposit(dai.address, amountToDeposit, onBehalfOf.address, '0');
const flashAmount = ethers.utils.parseEther('0.8');
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
await expect(
pool
.connect(caller.signer)
.flashLoan(
_mockFlashLoanReceiver.address,
[weth.address],
[flashAmount],
1,
onBehalfOf.address,
'0x10',
'0'
)
).to.be.revertedWith(BORROW_ALLOWANCE_ARE_NOT_ENOUGH);
});
it('Caller takes a WETH flashloan with mode = 1 onBehalfOf user with allowance. A loan for onBehalfOf is creatd.', async () => {
const {dai, pool, weth, users, helpersContract} = testEnv;
const caller = users[5];
const onBehalfOf = users[4];
const flashAmount = ethers.utils.parseEther('0.8');
// Deposited for onBehalfOf user already, delegate borrow allowance
await pool
.connect(onBehalfOf.signer)
.delegateBorrowAllowance(weth.address, caller.address, 1, flashAmount);
await _mockFlashLoanReceiver.setFailExecutionTransfer(true);
await pool
.connect(caller.signer)
.flashLoan(
_mockFlashLoanReceiver.address,
[weth.address],
[flashAmount],
1,
onBehalfOf.address,
'0x10',
'0'
);
const {stableDebtTokenAddress} = await helpersContract.getReserveTokensAddresses(weth.address);
const wethDebtToken = await getContract<StableDebtToken>(
eContractid.VariableDebtToken,
stableDebtTokenAddress
);
const onBehalfOfDebt = await wethDebtToken.balanceOf(onBehalfOf.address);
expect(onBehalfOfDebt.toString()).to.be.equal(
'800000000000000000',
'Invalid onBehalfOf user debt'
);
});
}); });

View File

@ -187,7 +187,7 @@ makeSuite('Pausable Pool', (testEnv: TestEnv) => {
await expect( await expect(
pool pool
.connect(caller.signer) .connect(caller.signer)
.flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 1, '0x10', '0') .flashLoan(_mockFlashLoanReceiver.address, [weth.address], [flashAmount], 1, caller.address, '0x10', '0')
).revertedWith(IS_PAUSED); ).revertedWith(IS_PAUSED);
// Unpause pool // Unpause pool