Developers

Hamburger

What is Aave

Aave is a decentralized non-custodial money market protocol where users can participate as depositors or borrowers. Depositors provide liquidity to the market to earn a passive income, while borrowers are able to borrow in an overcollateralized (perpetually) or undercollateralized (one-block liquidity) fashion.

The following documentation describes the fundamentals of the protocol and how to interact with it. Please join the #development room in the Aave community Discord server; our team and members of the community look forward to help you building an application on top of Aave. Your questions help us improve, so please don't hesitate to ask if you can't find what you are looking for here.



How it works

The protocol is implemented as a set of smart contracts on top of the Ethereum blockchain. Smart contracts guarantee safety, and avoid the need of a middleman.

The Aave protocol is developed with security as reference: the protocol has been audited by multiple auditors and formally verified. Please find more details about the security of the Aave protocol in the security section.



Smart Contracts

The LendingPoolAddressesProvider

The smart contracts in the Aave protocol are upgradeable through governance. The protocol keeps a global register of addresses that holds all the addresses of the components deployed on the particular network. Therefore, whenever it's required to access the LendingPool contract (description below), it is recommended to fetch the correct address from the LendingPoolAddressesProvider smart contract.

Retrieve contract instances

The following table sums up all the addresses of the protocol, grouped by network.

A description of the main contracts of the protocol follows.



LendingPoolAddressesProvider

/// Retrieve LendingPool address

LendingPoolAddressesProvider provider = LendingPoolAddressesProvider(/*contract_address*/);
LendingPool lendingPool = LendingPool(provider.getLendingPool());

Global addresses register of the protocol. This contract is immutable and his address will never change. Needs to be used whenever it is needed to fetch the address of the latest implementation of the LendingPool contract.

LendingPool

The LendingPool contract is the main contract of the protocol. It exposes all the user-oriented actions that can be invoked using either Solidity or web3 libraries.



deposit()

/**
* Deposit of 1000 DAI
*/

/// Retrieve LendingPool address
LendingPoolAddressesProvider provider = LendingPoolAddressesProvider(/*contract_address*/);
LendingPool lendingPool = LendingPool(provider.getLendingPool());

/// Input variables

address daiAddress = "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359";
uint256 amount = 1000 * 1e18;
uint16 referral = 0;

/// Deposit method call
lendingPool.deposit(daiAddress, amount, referral);

function deposit( address _reserve, uint256 _amount, uint16 _referralCode)

Deposits a certain _amount of an asset specified by the _reserve parameter. The caller receives a certain amount of corresponding aTokens in exchange. The amount of aTokens received depends on the corresponding aToken exchange rate.

Parameter Name Type Description
_reserve address address of the asset _reserve
_amount uint256 amount deposited, expressed in decimals units
_referralCode uint256 Referral code for affiliation programs




setUserUseReserveAsCollateral()

/**
* Enable usage of the DAI reserve as collateral for the user
*/

/// Retrieve LendingPoolAddress

LendingPoolAddressesProvider provider = LendingPoolAddressesProvider(/*contract_address*/);
LendingPool lendingPool = LendingPool(provider.getLendingPool());

/// Input variables

address daiAddress = "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359";
bool useAsCollateral = true;

/// setUserUseReserveAsCollateral method call
lendingPool.setUserUseReserveAsCollateral(daiAddress, useAsCollateral);

function setUserUseReserveAsCollateral(address _reserve, bool _useAsCollateral)

Using this function users can decide whether or not use a specific deposit as collateral. Users will only be able to disable deposits that are not being used as collateral yet.

Parameter Name Type Description
_reserve address address of the asset _reserve
_useAsCollateral bool if true, the asset is allowed as a collateral for borrow




borrow()

/**
* Deposit of 1000 DAI
*/

/// Retrieve LendingPool address

LendingPoolAddressesProvider provider = LendingPoolAddressesProvider(/*contract_address*/);
LendingPool lendingPool = LendingPool(provider.getLendingPool());

/// Input variables

address daiAddress = "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359";
uint256 amount = 1000 * 1e18;
/// 0 is stable rate, 1 is variable rate
bool variableRate = 1;
uint16 referral = 0;

/// Borrow method call
lendingPool.borrow(daiAddress, amount, variableRate, referral);

function borrow( address _reserve, uint256 _amount, uint256 _interestRateMode, uint16 _referralCode)

The borrow() function transfer a specific amount of the asset identified by the _reserve parameter to the msg.sender, provided that the caller has preemptively deposited enough collateral to cover the borrow.

Every borrow can be opened with a stable or variable rate mode. Borrows have infinite duration and there is no repayment schedule. In case of price fluctuations, a borrow position is liquidated if if the price of the collateral drops below a certain threshold. Please refer to the White Paper to understand how the stable rate economy works.

NOTE:

stable terminology is replacing the deprecated fixed terminology. As the Aave contracts ecosystem is audited, fixed term remains in the code.

Parameter Name Type Description
_reserve address address of the asset _reserve
_amount uint256 amount of to borrow, expressed in decimal units
_interestRateMode uint256 Stable or Variable rates borrow mode selection. take note that stable rate int is 0, variable rate is 1.
_referralCode uint256 Referral code for affiliation programs




repay()

/// Retrieve the LendingPool address

LendingPoolAddressesProvider provider = LendingPoolAddressesProvider(/*contract_address*/);
LendingPool lendingPool = LendingPool(provider.getLendingPool());

/// Input variables

address daiAddress = "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359";
uint256 amount = 1000 * 1e18;
address myAddress = /*repayer_address*/;

/// Repay method call
lendingPool.repay(daiAddress, amount, myAddress);

function repay( address _reserve, uint256 _amount, address payable _onBehalfOf)

This function allows the user to fully or partially repay a borrowed asset. The _onBehalfOf parameter can be used to repay the debt of a different user. NOTE: When the user sending the transaction is repaying his own debt, _onBehalfOf needs to be equal to msg.sender.

Parameter Name Type Description
_reserve address address of the asset _reserve
_amount uint256 amount to repay, expressed in decimal units. To repay the whole borrowed amount, the function accepts uint(-1) as value for _amount, ONLY when the repayment is not executed on behalf of a 3rd party. In case of repayments on behalf of another user, it's recommended to send an _amount slightly higher than the current borrowed amount.
_onBehalfOf address payable address to repay on behalf of. Needs to be equal to msg.sender when the caller is repaying for himself.




rebalanceFixedBorrowRate()

/// Retrieve the LendingPool address

LendingPoolAddressesProvider provider = LendingPoolAddressesProvider(/*contract_address*/);
LendingPool lendingPool = LendingPool(provider.getLendingPool());

/// Input variables

address daiAddress = "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359";
address rebalancedUser = /*address_to_rebalance*/;

/// rebalanceFixedBorrowRate method call
lendingPool.rebalanceFixedBorrowRate(daiAddress, rebalancedUser);

function rebalanceFixedBorrowRate(address _reserve, address _user)

This function rebalances the Stable rate of _user. If the user is not borrowing at a Stable rate or the conditions for the rebalance are not satisfied, the transaction gets reverted. Please refer to the White Paper for details on how and when an user position can be rebalanced.

Parameter Name Type Description
_reserve address address of the asset _reserve
_user address address of the user to rebalance




liquidationCall()

/// Retrieve the LendingPool address

LendingPoolAddressesProvider provider = LendingPoolAddressesProvider(/*contract_address*/);
LendingPool lendingPool = LendingPool(provider.getLendingPool());

/// Input variables

address linkAddress = "0x514910771af9ca656af840dff83e8264ecf986ca";
address userliquidated = /*user_address_being_liquidated*/;
uint256 purchaseAmount = 12 * 1e18;
address daiAddress = "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"


/// liquidationCall method call
lendingPool.liquidationCall(linkAddress, userliquidated, purchaseAmount, daiAddress);

function liquidationCall(address _collateral, address _reserve, address _user, uint256 _purchaseAmount, bool _receiveAToken)

The liquidationCall() function allows everybody to liquidate positions that present a health factor below 1. Whenever this happens, liquidators will repay part or all of the outstanding borrowed amount on behalf of the borrower, while receiving a discounted amount of collateral in return. Liquidators can decide if they want to receive an equivalent amount of collateral aTokens, or the underlying asset directly. When the liquidation happens, the health factor of the position is increased, bringing it above 1 again.

Liquidators can only close a certain amount of collateral defined by a close factor. Across all the protocol, the close factor is 0.5 (liquidators can liquidate 50% of every position at once).

Parameter Name Type Description
_collateral address address of the liquidated collateral reserve
_reserve address address of the asset reserve of the loan principal
_user address address of the user borrowing
_purchaseAmount uint256 amount of the discounted purchase
_receiveAToken bool if true, the user receives the aTokens equivalent of the purchased collateral. If false, the user receives the underlying asset directly




flashLoan()


/**
* Flash Loan of 1000 DAI
*/

/// Retrieve the LendingPool address

LendingPoolAddressesProvider provider = LendingPoolAddressesProvider(/*contract_address*/);
LendingPool lendingPool = LendingPool(provider.getLendingPool());

/// Input variables

/* the receiver is a contract that implements the IFLashLoanReceiver interface */
address receiver = /*contract_address*/;
address daiAddress = "0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359";
uint256 amount = 1000 * 1e18;

/// flashLoan method call
lendingPool.flashLoan(receiver, daiAddress, amount);

function flashLoan( address payable _receiver, address _reserve, uint _amount) external

Allows contracts to borrow in an undercollateralized fashion from the reserve specified by the parameter _reserve a certain _amount of liquidity, that must be returned within the same transaction. Please refer to the Tutorials section on how to create and implement on top of the Flash Loan functionality.

Parameter Name Type Description
_receiver address, payable address of the receiver of the borrowed assets
_reserve address address of deposit asset _reserve
_amount uint256 amount to be received




getReserveConfigurationData()

function getReserveConfigurationData(address _reserve)

Returns specific reserve's configuration parameters.

return name Type Description
ltv uint256 Loan-to-value. Value in percentage
liquidationThreshold uint256 liquidation threshold. Value in percentage
liquidationDiscount uint256 liquidation discount. Value in percentage
interestRateStrategyAddress address address of the contract defining the interest rate strategy
usageasCollateralEnabled bool if true, reserve asset can be used as collateral for borrowing
borrowingEnabled bool if true, reserve asset can be borrowed
fixedBorrowRateEnabled bool if true, reserve asset can be borrowed with stable rate mode
isActive bool if true, users can interact with reserve asset




getReserveData()

function getReserveData(address _reserve)

Returns global information on any asset reserve pool

return name Type Description
totalLiquidity uint256 reserve total liquidity
availableLiquidity uint256 reserve available liquidity for borrowing
totalBorrowsFixed uint256 total amount of outstanding borrows at Stable rate
totalBorrowsVariable uint256 total amount of outstanding borrows at Variable rate
liquidityRate uint256 current yearly interest earned by the depositors. In Ray units.
variableBorrowRate uint256 current Variable rate APR of the reserve pool
fixedBorrowRate uint256 current stable rate APR of the reserve pool
averageFixedBorrowRate uint256 current average stable borrow rate
utilizationRate uint256 Espressed as total borrows/total liquidity.
liquidityIndexRate uint256 cumulative liquidity index
variableBorrowIndex uint256 cumulative variable borrow index
aTokenAddress address aTokens contract address for the specific _reserve
lastUpdateTimestamp uint40 timestamp of the last update of reserve data




getUserAccountData()

function getUserAccountData(address _user)

Returns information of a reserve exclusively related with a particular user address

return name Type Description
totalLiquidityETH uint256 user aggregated deposits across all the reserves. In Wei
totalCollateralETH uint256 user aggregated collateral across all the reserves. In Wei
availableBorrowsETH uitn256 user available amount to borrow in ETH
currentLiquidationThreshold uint256 user current average liquidation threshold across all the collaterals deposited
ltv uint256 user average Loan-to-Value between all the collaterals
healthFactor uint256 user current Health Factor




getUserReserveData()

function getUserReserveData(address _reserve, address _user)

Returns information related to the user data on a specific reserve

return name Type Description
currentATokenBalance uint256 user current reserve AToken balance
currentUnderlyingBalance uint256 user current reserve deposit amount
currentBorrowBalance uint256 user current reserve outstanding borrow balance
principalBorrowBalance uint256 user balance of borrowed asset
borrowRateMode uint256 user borrow rate mode either Stable or Variable
borrowRate uint256 user current borrow rate APR
liquidityRate uint256 user current earn rate on _reserve
originationFee uint256 user outstanding loan origination fee
variableBorrowIndex uint256 user variable cumulative index
lastUpdateTimestamp uint256 Timestamp of the last data update




getReserves()

function getReserves()

Returns an array of all the active reserves addresses.




aTokens

The aTokens are interest-bearing derivative tokens that are minted and burned upon deposit and redeem. The aTokens carry the underlying value of the deposited amount plus the accrued interest, and can be safely stored, transferred or traded. Since the contract interface is compatible with the ERC20 token interface, the documentation will only cover functions and behaviors that are not part of the ERC20 token standard.



redeem()

/// Instantiation of the AToken address
AToken aTokenInstance = AToken("/*aToken_address*/");

/// Input variables
uint256 amount = 1000 * 1e18;

/// redeem method call
aTokenInstance.redeem(amount)

function redeem(uint256 _amount)

Non-standard ERC20 function to redeem an _amount of aTokens for the underlying asset, burning the aTokens during the process. NOTE: redeem() will fail if the aTokens to be redeemed are being used as collateral. Please refer to the transferAllowed() function to understand how to check if a specific redeem/transfer action can be performed.

Parameter Name Type Description
_amount uint256 amount of ATokens to redeem




transfer()

/// Instantiation of the AToken address
AToken aTokenInstance = AToken("/*aToken_address*/");

/// Input variables
address recipient = /*transfer_recipient_address*/;
uint256 amount = 1000 * 1e18;

/// transfer method call
aTokenInstance.transfer(recipient, amount)

function transfer(address recipient, uint256 amount) public

Standard ERC20 function to transfer tokens from msg.sender to a specified recipient. NOTE: transfer() will fail if the aTokens to be redeemed are being used as collateral. Please refer to the transferAllowed() function to understand how to check if a specific redeem/transfer action can be performed.




transferFrom()

/// Instantiation of the AToken address
AToken aTokenInstance = AToken("/*aToken_address*/");

/// Input variables 
address from = /*transfer_source_address*/
address to = /*transfer_recipient_address*/;
uint256 amount = 1000 * 1e18;

/// transferFrom method call
aTokenInstance.transferFrom(from, to, amount)

function transferFrom(address from, address to, uint256 amount) public

Standard ERC20 function to transfer tokens from an address to another. NOTE: transferFrom() will fail if the aTokens to be redeemed are being used as collateral. Please refer to the transferAllowed() function to understand how to check if a specific redeem/transfer action can be performed.




isTransferAllowed()

function transferAllowed(address from, uint256 amount)

Non-standard ERC20 function that allows to check beforehand if a transfer or a redeem will fail. Specifically, a transfer/redeem will fail if the resulting Health Factor of the user performing the action will end up being below 1.

return name Type Description
transferAllowed bool true if the transfer is allowed, otherwise false




getExchangeRate()

function getExchangeRate()

Non-standard ERC20 function that returns the current exchange rate between the aToken and the corresponding underlying asset, in Ray.

return name Type Description
initialExchangeRate uint256 initial exchange rate expressed in rays




initialExchangeRate()

function initialExchangeRate()

Non-standard ERC20 functions that returns the initial exchange rate between the aToken and the corresponding underlying asset, in ray.

return name Type Description
initialExchangeRate uint256 initial amount of aTokens per underlying token a depositor receives. In Ray units.




underlyingAssetAddress()

function underlyingAssetAddress()

Non-standard ERC20 function that returns the address of the underlying asset.

return name Type Description
underlyingAssetAddress address the ERC20 token contract address of the underlying asset




balanceOfUnderlying()

function balanceOfUnderlying(address _user)

Non-standard ERC20 function that returns the current underlying balance of _user.

return name Type Description
balance uint256 balance of the user in the underlying asset




Tutorials

The following section provides simple code examples on how to implement some specific functionalities of the protocol as well as the basic interactions.



Implementing a FlashLoanReceiver contract



pragma solidity ^0.5.0;

import "openzeppelin-solidity/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) external returns(uint256 returnedAmount) {

        //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

        */

        transferFundsBackToPoolInternal(_reserve, _amount.add(_fee));
        return _amount.add(_fee);
    }
}

To interact with the Flash Loan functionality of the Aave protocol, a basic understanding of Solidity and its inheritance techniques are needed. Specifically, in order to be able to access the protocol's liquidity in an undercollateralized way, it's necessary to implement a separate smart contract inheriting from the FlashLoanReceiverBase contract which can be found here.

A basic implementation of the Flash Loan feature would need to keep into account the following conditions:

Security & Audits

Aave has been implemented with security as priority. The system has been designed to be safe and secure, and we have spent all the necessary resources in order to ensure that the protocol matches the highest security standards.

Currently, the code has already been audited by Trail of Bits. A second audit by OpenZeppelin and a Formal Verification procedure by ChainSecurity are in progress.

This section will be updated with the correspoding audit reports once all the code verification procedures are completed. As soon as the audit process is completed, the smart contracts code will be also open sourced and a bounty campaign will start.



Audits

Trail of Bits

The first audit by Trail of Bits can be found here.




Misc

Gas consumption

The gas consumption of the protocol depends on the specific action executed and the user. Specifically, it might be higher whenever an user executes an action for the first time, or depending on how many currencies the user has deposited/borrowed. Every currency added to the Aave protocol slightly increases the gas cost of all the operations that involve checking the account global liquidity. Gas cost of the flashLoan() function also depends on the specific flash loan implementation.

Function Typical gas cost
Deposit < 180K
Redeem < 300K
Borrow < 500K
Redeem < 150K
liquidationCall < 500K




Ray Math

For internal calculations and to reduce the impact of rounding errors, the protocol uses the concept of Ray Math. A Ray is a unit with 27 decimals of precision. All the rates (liquidity/borrow/utilization rates) as well as the cumulative indexes and the aTokens exchange rates are expressed in Ray. Please refer to the following article for further details about this concept.




Glossary

Term Description
Loan To Value Expressed in percentage, represents the maximum borrowing power expressed by a specific collateral. If a collateral has a Loan to Value of 75%, for every 1 ETH worth of collateral the user will be able to borrow 0.75 ETH worth of principal currency. The Loan To Value is specified per collateral.
Liquidation Threshold Expressed in percentage, represents the threshold at which a borrow position will be considered undercollateralized and subjected to liquidation. If a collateral has a liquidation threshold of 80%, it means that the loan will be liquidated when the collateral is worth 80% of the principal amount. The liquidation threshold is specified per collateral.
Liquidation Discount Expressed in percentage, is the discount at which liquidators will be able to purchase a specific collateral. The Liquidation Discount is specified per collateral.
Health factor Represents the ratio between total collateral multiplied by the liquidation threshold, and the borrowed principal. When the Health Factor goes below 1, the loan is undercollateralized and can be liquidated.
Stable rate A loan with a stable rate behaves like a fixed rate loan in the short term, but the rates can be rebalanced following sudden market changes in the medium/long term.