Aave for DeFi Developers – A Simple Guide with Video

This is in continuation of our DeFi series. In this post, we look at yet another decentralized lending and borrowing platform Aave.

πŸͺ™ Full DeFi Course: Click the link to access our free full DeFi course that’ll show you the ins and outs of decentralized finance (DeFi).

Aave

Aave launched in 2017 is a DeFi protocol similar to Compound with a lot of upgrades.

Beyond what Compound provides, Aave gives several extra tokens for supply and borrowing. As of now, Compound offered nine different tokens (various ERC- 20 Ethereum- based assets).

Aave provides these nine besides an additional 13 that Compound does not.

Depositors give the market liquidity to generate a passive income, while borrowers can borrow if they have an over-collateralized token or can avail flash loans for under-collateralized (one-block liquidity).

Currently, we can see two major markets on Aave.

  • The first is for ERC-20 tokens that are more frequently used, like those of Compound, and their underlying assets, such as ETH, DAI and USDC.
  • The latter is only available with Uniswap LP tokens.

For instance, a user receives an LP token signifying market ownership when they deposit collateral into a liquidity pool on the Uniswap platform. To provide additional benefits, the LP tokens can be sold on the Uniswap market of Aave.

As per DeFi pulse, Aave has a TVL (Total Value Locked) of $4.09B as of today.

Fig: Defi Pulse for Aave

Aave Versions

Aave has released three versions (v1, v2 and v3) as of now and the Governance token of Aave is β€˜AAVE’. Version 1 or v1 is the base version launched in 2017 and then there have been upgrades with multiple new features added. Below is a short comparison of when to use v2 or v3.

You should use Aave V2 when:

  • You won’t need to borrow many tokens.
  • Little profit
  • You borrow important tokens like WETH, USDC, etc.

You should use Aave V3 when:

  • Many tokens must be borrowed
  • High profit margin
  • You borrow mid-cap tokens like LINK, etc.

Borrow and Lending in Aave

Fig: Aave borrow and lend (pic credit: https://docs.aave.com)

Borrow

You must deposit any asset to be used as collateral before borrowing.

πŸ’‘ The amount you can borrow up to depends on the value you have deposited and the readily available liquidity.

For instance, if there isn’t enough liquidity or if your health factor (minimum threshold of the collateral = 1, below this value, liquidation of your collateral is triggered) prevents it, you can’t borrow an asset.

πŸ’‘ The loan is repaid with the same asset that you borrowed.

For instance, if you borrow 1 ETH, you’ll need to pay back 1 ETH plus interest.

In the updated Version 2 of the Aave Protocol, you can also use your collateral to make payments. You can borrow any of the stable coins like USDC, DAI, USDT, etc. if you want to repay the loan based on the price of the USD.

Stable vs Variable Interest Rate

In the short-term, stable rates function as a fixed rate, but they can be rebalanced in the long run in reaction to alterations in the market environment. Depending on supply and demand in Aave, the variable rate can change.

The stable rate is the better choice for forecasting how much interest you will have to pay because, as its name suggests, it will remain fairly stable. The variable rate changes over time and, depending on market conditions, could be the optimal rate.

Through your dashboard, you can switch between the stable and variable rate at any time.

Deposit/Lending

Lenders share the interest payments made by borrowers based on the utilization rate multiplied by the average borrowing rate. The yield for depositors increases as reserve utilization increases.

Lenders are also entitled to a portion of the Flash Loan fees, equal to .09% of the Flash Loan volume.

There is no minimum or maximum deposit amount; you may deposit any amount you choose.

Flash Loans in Aave

Flash Loans are unique business agreements that let you borrow an asset as long as you repay the borrowed money (plus a fee) before the deal expires (also called One Block Borrows). Users are not required to provide collateral for these transactions in order to proceed.

Flash Loans have no counterpart in the real world, so understanding how state is controlled within blocks in blockchains is a prerequisite.

πŸ’‘ Flash-loan enables users to access pool liquidity for a single transaction as long as the amount borrowed plus fees are returned or (if permitted) a debt position is opened by the end of the transaction.

For flash loans, Aave V3 provides two choices:

(1) β€œflashLoan”: enables borrowers to access the liquidity of several reserves in a single flash loan transaction. In this situation, the borrower also has the choice to open a fixed or variable-rate loan position secured by provided collateral.

πŸ‘‰ The fee for flashloan is waived for approved flash borrowers.

(2) β€œflashLoanSimple”: enables the borrower to access a single reserve’s liquidity for the transaction. For individuals looking to take advantage of a straightforward flash loan with a single reserve asset, this approach is gas-efficient.

πŸ‘‰ The fee for flashloanSimple is not waived for the flash borrowers. The Flashloan fee on Aave V3 is 0.05%.

Let’s Code a Simple Flash Loan

Let’s code a simple flash loan in Aave, where we buy and repay the asset in the same transaction without having to provide any collateral. First, we set up the environment for writing the code.

▢️ Note: It is recommended to follow the video along for a better understanding.

$npm install -g truffle  # in case truffle not installed
$mkdir aave_flashloan 
$cd aave_flashloan
$truffle init
$npm install @aave/core-v3
$npm install @openzeppelin/contracts
$npm install @openzeppelin/test-helpers 

Note: Aave3 is currently available on Polygon, Arbitrum, Avalanche, and other L2 chains. As of now, it is not available on the Ethereum mainnet. Thus, we will fork the Polygon mainnet for our tests.

Set up a new app in Alchemy with the chain as Polygon mainnet and note down the API key.

Create a new file .env and enter the below info.

$WEB3_ALCHEMY_POLYGON_ID=<API key noted above>
$USDC_WHALE=0x075e72a5eDf65F0A5f44699c7654C1a76941Ddc8

πŸ‘‰ Recommended Tutorial: Solidity Crash Course — Your First Smart Contract

Simple Flash Loan Contract

The contract code (FlashLoanPolygon.sol) in the contracts folder.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@aave/core-v3/contracts/interfaces/IPool.sol";
import "@aave/core-v3/contracts/flashloan/base/FlashLoanSimpleReceiverBase.sol";

contract AaveFlashloan is FlashLoanSimpleReceiverBase {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;
    event Log(string message, uint256 val);
    constructor(IPoolAddressesProvider provider)
        FlashLoanSimpleReceiverBase(provider)
    {}

    function aaveFlashloan(address loanToken, uint256 loanAmount) external {
        IPool(address(POOL)).flashLoanSimple(
            address(this),
            loanToken,
            loanAmount,
            "0x",
            0
        );
    }

    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes memory
    ) public override returns (bool) {
        require(
            amount <= IERC20(asset).balanceOf(address(this)),
            "Invalid balance for the contract"
        );
        // pay back the loan amount and the premium (flashloan fee)
        uint256 amountToReturn = amount.add(premium);
        require(
            IERC20(asset).balanceOf(address(this)) >= amountToReturn,
            "Not enough amount to return loan"
        );

        approveToken(asset, address(POOL), amountToReturn);
        emit Log("borrowed amount", amount);
        emit Log("flashloan fee", premium);
        emit Log("amountToReturn", amountToReturn);

        return true;
    }
}

The contract code contains two important functions.

  • aaveFlashloan() which calls the pools function flashLoanSimple(). Our test code must call this function to initiate a flash loan.
  • executeOperation() is a callback function of the pool which pays the loan + interest back to the pool.

Testing Contract

In the test folder, create a JavaScript file config.js with the following content:

const USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174";

const aavePoolAddressesProvider =
  "0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb";

const USDC_WHALE = process.env.USDC_WHALE;

module.exports = {
    USDC,
    USDC_WHALE,
    aavePoolAddressesProvider,
}

In the test folder, create another file testAaveFlashLoanSimple.js:

const BN = require("bn.js");
const IERC20 = artifacts.require("IERC20");
const AaveFlashLoan = artifacts.require("AaveFlashloan");
const {USDC,aavePoolAddressesProvider,USDC_WHALE} = require("./config");


function sendEther(web3, from, to, amount) {
  return web3.eth.sendTransaction({
    from,
    to,
    value: web3.utils.toWei(amount.toString(), "ether"),
  });
}

contract("AaveFlashLoan", (accounts) => {
  const WHALE = USDC_WHALE
  const TOKEN_BORROW = USDC
  const DECIMALS = 6  // USDC uses 6 decimal places and not 18 like other ERC20
  // We fund more because we need to pay back along with the fees during Flash loan.
  // So let us fund extra (2 million round figure to the calculations simple)
  const FUND_AMOUNT = new BN(10).pow(new BN(DECIMALS)).mul(new BN(500)); 500 USDC
  const BORROW_AMOUNT = new BN(10).pow(new BN(DECIMALS)).mul(new BN(1000000)); // 1 million USDC

  let aaveFlashLoanInstance
  let token

  beforeEach(async () => {
    token = await IERC20.at(TOKEN_BORROW) // USDC token
    aaveFlashLoanInstance = await AaveFlashLoan.new(aavePoolAddressesProvider)

    // send ether to USDC WHALE contract to cover tx fees
    await sendEther(web3, accounts[0], WHALE, 1)

    // send enough token to cover fee
    const bal = await token.balanceOf(WHALE)
    assert(bal.gte(FUND_AMOUNT), "balance < FUND")
    // Send USDC tokens to AaveFlashLoan contract
    await token.transfer(aaveFlashLoanInstance.address, FUND_AMOUNT, {
      from: WHALE,
    })
    console.log("balance of USDC in AAveFlashLoan contract:", bal2.toString())

  })
    it("aave simple flash loan", async () => {
        const tx = await aaveFlashLoanInstance.aaveFlashloan(token.address, BORROW_AMOUNT)
        console.log("token address:",token.address)
        for (const log of tx.logs) {
        console.log(log.args.message, log.args.val.toString())
        }
    })

});

Again, feel free to watch the video at the beginning of this tutorial to understand the testing process.

Open two terminals.

In terminal 1:

$source .env
$npx ganache-cli i --fork https://polygon-mainnet.g.alchemy.com/v2/$WEB3_ALCHEMY_POLYGON_ID \
--unlock $USDC_WHALE \
--networkId 999

This should start a local fork of the Polygon mainnet.

In terminal 2:

$ source .env
$ env $(cat .env) npx truffle test --network polygon_main_fork test/testAaveFlashLoanSimple.js

This should run the flash loan test case, and it must pass.

Conclusion

This tutorial discussed Aave, a leading Dapp lending and borrowing provider.

It covered some basic functionalities supported in Aave, such as lending, borrowing, and flash loans.

There are many other features Aave supports and it is not possible to cover it in one post, such as governance, liquidation, and advanced features such as Siloed Borrowing, Credit Delegation, and many more.

The post also examined a simple flash loan contract using Aave API.

You can explore more about borrowing and lending with Aave in this link. It has a full stack Defi Aave dapp with frontend to perform borrowing and lending.

πŸ‘‰ Recommended Tutorial: Crypto Trading Bot Developer — Income and Opportunity