... in your project

Reference Implementation

If you're familiar with Solidity, the best place to start is to copy the contracts in our Flash Loan Truffle box. The contract you call is Flashloan.sol with function flashloan().

If you want to follow a step by step procedure, see below.

Step by step

To interact with the Flash Loan functionality of the Aave protocol, a basic understanding of Solidity and its inheritance techniques are needed.

Your contract should inherit from the FlashLoanReceiverBase contract, which can be found here (Solidity ^0.5.0) or here (Solidity ^0.6.6).

Your contract should call the flashLoan() function of the LendingPool. For more information on the flashLoan() function, see the documentation here.

An example of your contract calling the LendingPool:

/**
* Flash Loan of 1000 DAI
*/
address receiver = address(this) // Can also be a separate contract
address asset = "0x6b175474e89094c44da98b954eedeac495271d0f"; // Dai
uint256 amount = 1000 * 1e18;
// If no params are needed, use an empty params:
bytes memory params = "";
// Else encode the params like below (bytes encoded param of type `address` and `uint`)
// bytes memory params = abi.encode(address(this), 1234);
ILendingPool lendingPool = ILendingPool(addressesProvider.getLendingPool());
lendingPool.flashLoan(address(this), asset, amount, params);

If you need to pass in arbitrary parameters for the executeOperation() to use, then you can pass in any bytes encoded data via the params_ input. Otherwise, you can pass in an empty bytes encoded value as shown above.

When your contract calls lendingPool.flashLoan(), our LendingPool contract completes some sanity checks, transfers the requested _amount of _reserve asset to the receiver you specified, then calls executeOperation() on the receiver contract your specified.

Your receiver contract must have a function with the exact function signature as the following:

function executeOperation(address _reserve, uint256 _amount, uint256 _fee, bytes memory _params) external

Before the end of your executeOperation() function, you must payback the Flash Loan amount and the fee . If this action is not performed, the transaction will be reverted.

In our example below, we have inherited from the FlashLoanReceiverBase contract so can simply call transferFundsBackToPoolInternal() to repay the loan and fee amount.

An example implementation of your receiver contract that receives the Flash Loan:

solidity ^0.6.6
Solidity ^0.5.0
solidity ^0.6.6
pragma solidity ^0.6.6;
import "./FlashLoanReceiverBase.sol";
import "./ILendingPoolAddressesProvider.sol";
import "./ILendingPool.sol";
contract Flashloan is FlashLoanReceiverBase {
constructor(address _addressProvider) FlashLoanReceiverBase(_addressProvider) public {}
/**
This function is called after your contract has received the flash loaned amount
*/
function executeOperation(
address _reserve,
uint256 _amount,
uint256 _fee,
bytes calldata _params
)
external
override
{
require(_amount <= getBalanceInternal(address(this), _reserve), "Invalid balance, was the flashLoan successful?");
//
// Your logic goes here.
// !! Ensure that *this contract* has enough of `_reserve` funds to payback the `_fee` !!
//
uint totalDebt = _amount.add(_fee);
transferFundsBackToPoolInternal(_reserve, totalDebt);
}
function flashloan() public onlyOwner {
/**
* Flash Loan of 1000 DAI
*/
address receiver = address(this) // Can also be a separate contract
address asset = "0x6b175474e89094c44da98b954eedeac495271d0f"; // Dai
uint256 amount = 1000 * 1e18;
// If no params are needed, use an empty params:
bytes memory params = "";
// Else encode the params like below (bytes encoded param of type `address` and `uint`)
// bytes memory params = abi.encode(address(this), 1234);
ILendingPool lendingPool = ILendingPool(addressesProvider.getLendingPool());
lendingPool.flashLoan(address(this), asset, amount, params);
}
}
Solidity ^0.5.0
pragma solidity ^0.5.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "../tokens/MintableERC20.sol";
import "../../flashloan/base/FlashLoanReceiverBase.sol";
import "../../configuration/LendingPoolAddressesProvider.sol";
import "../../configuration/NetworkMetadataProvider.sol";
contract FlashLoanReceiverExample is FlashLoanReceiverBase {
using SafeMath for uint256;
constructor(LendingPoolAddressesProvider _provider)
FlashLoanReceiverBase(_provider)
public {}
function executeOperation(
address _reserve,
uint256 _amount,
uint256 _fee,
bytes memory _params) external {
//check the contract has the specified balance
require(_amount <= getBalanceInternal(address(this), _reserve),
"Invalid balance for the contract");
/**
CUSTOM ACTION TO PERFORM WITH THE BORROWED LIQUIDITY
Example of decoding bytes param of type `address` and `uint`
(address sampleAddress, uint sampleAmount) = abi.decode(_params, (address, uint));
*/
transferFundsBackToPoolInternal(_reserve, _amount.add(_fee));
}
}