... with Remix

Remix is a powerful, open source tool that helps you write solidity contracts in the browser, and is supported by the Ethereum Foundation.

If you are unfamiliar with Remix, we'd recommend you complete some of the workshops/tutorials here.

If you already have basic understanding of Remix, then follow the tutorial below. Remember to activate the Compiler, Debugger, and Deploy & run transactions plugins.

Setting up your code

To begin, you'll need to create a new contract that you will call and which will be called by the Aave Protocol's LendingPool contract. This can be two separate contracts, however for ease of understanding, we'll consolidate those functions into one contract.

In Remix, add a new file and name it, e.g. MyFlashloanContract.sol and add the following code to it:

pragma solidity ^0.6.6;

import "https://github.com/aave/flashloan-box/blob/Remix/contracts/aave/FlashLoanReceiverBase.sol";
import "https://github.com/aave/flashloan-box/blob/Remix/contracts/aave/ILendingPoolAddressesProvider.sol";
import "https://github.com/aave/flashloan-box/blob/Remix/contracts/aave/ILendingPool.sol";

contract Flashloan is FlashLoanReceiverBase {
    constructor(address _addressProvider) FlashLoanReceiverBase(_addressProvider) public {}

    // Rest of your code goes here
}

Remix is able to import solidity source files directly from Github. In the first 3 lines, we are importing the files necessary to interface with the Aave Protocol. You can look at these files individually by visiting them directly in Github.

On line 8, we are passing in a constructor argument to the inherited FlashLoanReceiverBase contract, which determines which LendingPoolAddressProvider to use on the network the contract is deployed.

You'll be passing in the relevant LendingPoolAddressProvider value at deployment as listed here, via the Remix deployment plugin.

Calling your Flash Loan function

Next you'll want to add the function that you'll be calling to start the Flash Loan process. We'll call the function flashloan() and make sure that only the owner (i.e. you) can call this function. We will request to Flash Loan an amount of 1 Dai.

In the below code, 1 ether refers to 1000000000000000000 wei worth of Dai, not 1 ethereum token. I.e. it represents 1e18 in solidity.

pragma solidity ^0.6.6;

import "https://github.com/aave/flashloan-box/blob/Remix/contracts/aave/FlashLoanReceiverBase.sol";
import "https://github.com/aave/flashloan-box/blob/Remix/contracts/aave/ILendingPoolAddressesProvider.sol";
import "https://github.com/aave/flashloan-box/blob/Remix/contracts/aave/ILendingPool.sol";

contract Flashloan is FlashLoanReceiverBase {

    constructor(address _addressProvider) FlashLoanReceiverBase(_addressProvider) public {}

    /**
        Flash loan 1000000000000000000 wei (1 ether) worth of `_asset`
     */
    function flashloan(address _asset) public onlyOwner {
        bytes memory data = "";
        uint amount = 1 ether;

        ILendingPool lendingPool = ILendingPool(addressesProvider.getLendingPool());
        lendingPool.flashLoan(address(this), _asset, amount, data);
    }
}

Executing your operation with the Flash Loaned amount

Now if we have requested a valid amount from a valid reserve, then the LendingPool contract will call the executeOperation() function in our contract.

The executeOperation() function signature must match exactly:

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

We can add our executeOperation() function as below:

pragma solidity ^0.6.6;

import "https://github.com/aave/flashloan-box/blob/Remix/contracts/aave/FlashLoanReceiverBase.sol";
import "https://github.com/aave/flashloan-box/blob/Remix/contracts/aave/ILendingPoolAddressesProvider.sol";
import "https://github.com/aave/flashloan-box/blob/Remix/contracts/aave/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);
    }

    /**
        Flash loan 1000000000000000000 wei (1 ether) worth of `_asset`
     */
    function flashloan(address _asset) public onlyOwner {
        bytes memory data = "";
        uint amount = 1 ether;

        ILendingPool lendingPool = ILendingPool(addressesProvider.getLendingPool());
        lendingPool.flashLoan(address(this), _asset, amount, data);
    }
}

Note that when the LendingPool contract calls your executeOperation() function, it will pass in the reserve, amount, fee, and params for you to use in your operation.

Before the end of the execution, you must payback the borrowed funds. Failure to do so will cause the transaction to revert.

Compiling and deploying your contract

On the navigation bar on the right, select the 'Compile' option, and compile your contract. Once the contract has finished compiling, you may receive some warnings in yellow boxes at the bottom. You can safely ignore these warnings for now. However if you receive errors in red boxes, then those must be resolved before continuing.

Next go to the 'Deploy' navigation option, and choose the environment you wish to deploy in.

If you choose to deploy in the JavaScript VM, then your contract will not work correctly. This is because the JavaScript VM is a local blockchain instance running in your browser, and you have not set up all the necessary Aave Protocol contracts (such as the LendingPool). However this can be useful if you only need to call specific functions in your contract, without testing the Flash Loan part of your contract.

The better option for deployment would be either 'Injected Web3' (i.e. Metamask in your browser) or 'Web3 Provider' (i.e. an infura endpoint). In either case, you will be able to choose a test network to deploy to, which should be one of the networks listed on the Deployed Contracts page.

When deploying your contracts to mainnet or a testnet, ensure that the account being used for deployment has sufficient ETH to cover the costs of the deployment transaction, as well as enough left over to call the functions in your contract.

For more information on deployment options in Remix, see their documentation.

Call your contract

If you execute your contract at this stage, the transaction will fail. This is because your MyFlashloanContract.sol contract does not have enough Dai to pay back the Flash Loan (as the executeOperation() function does not produce any extra funds to payback the loan). To solve this, ensure your contract has some Dai to pay back the Flash Loan.

Now that you have deployed your contract, send some Dai amount to your contract address (see above warning).

Next, call your flashloan() function with a sufficient gas limit (>210k). If successful, your contract will receive and payback the Flash Loan successfully. An example transaction using the above code is on Ropsten here.

If you need development support, join the #developers channel on our Aave community Discord server.

Last updated