Aave Protocol Adapter

The Aave adapter enables the Yieldinator Facet to integrate with Aave's lending protocol, allowing vaults to earn yield by depositing assets into Aave's liquidity pools.

Overview

Aave is a decentralized non-custodial liquidity protocol where users can participate as depositors or borrowers. Depositors provide liquidity to the market to earn a passive income, while borrowers can borrow in an overcollateralized or undercollateralized way.

The Aave adapter facilitates deposits into Aave's lending pools, manages aToken balances, and harvests yield generated from lending interest.

Implementation Details

Contract: AaveAdapter.sol

The adapter implements the standard YieldinatorAdapter interface with Aave-specific functionality:

contract AaveAdapter is YieldinatorAdapter {
    // Aave contracts
    IAaveLendingPool public lendingPool;
    
    // Mapping from underlying asset to aToken
    mapping(address => address) public assetToAToken;
    
    // Deposited amounts per token
    mapping(address => uint256) public depositedAmount;
    
    // Constructor and core functions...
}

Key Functions

Asset Registration

Before using the adapter for a specific token, the asset must be registered with its corresponding aToken:

function registerAsset(address _asset, address _aToken) external onlyRole(ADAPTER_ADMIN_ROLE)

Deposit

Deposits tokens into Aave's lending pool:

function deposit(address _token, uint256 _amount) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (bool)

The function:

  1. Transfers tokens from the caller to the adapter

  2. Approves the lending pool to spend the tokens

  3. Deposits tokens into Aave

  4. Updates the deposited amount tracking

Withdraw

Withdraws tokens from Aave's lending pool:

function withdraw(address _token, uint256 _amount) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (bool)

Harvest Yield

Harvests accrued interest without withdrawing the principal:

function harvestYield(address _token) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (uint256)

The function calculates yield as the difference between the current aToken balance and the original deposited amount.

Emergency Withdraw

Provides emergency withdrawal functionality:

function emergencyWithdraw(address _token) external override onlyRole(YIELDINATOR_ROLE) nonReentrant returns (uint256)

APY Calculation

Returns the current APY for a token in Aave:

function getCurrentAPY(address _token) external view override returns (uint256)

The APY is calculated based on Aave's liquidityRate, converted from ray (27 decimals) to a percentage with 2 decimal places (scaled by 10000).

Usage Example

// Initialize the adapter
AaveAdapter aaveAdapter = new AaveAdapter(lendingPoolAddress, adminAddress);

// Register an asset
aaveAdapter.registerAsset(daiAddress, aDaiAddress);

// Deposit tokens
aaveAdapter.deposit(daiAddress, 1000 * 1e18);

// After some time, harvest yield
uint256 harvestedYield = aaveAdapter.harvestYield(daiAddress);

// Withdraw tokens
aaveAdapter.withdraw(daiAddress, 500 * 1e18);

// In case of emergency
aaveAdapter.emergencyWithdraw(daiAddress);

Security Considerations

  • The adapter uses OpenZeppelin's SafeERC20 for token transfers to prevent common ERC20 vulnerabilities.

  • Non-reentrancy guards protect against reentrancy attacks during external calls.

  • Role-based access control ensures only authorized addresses can call sensitive functions.

  • The adapter maintains accurate accounting of deposited amounts to prevent yield manipulation.

Gas Optimization

  • The adapter minimizes the number of external calls to Aave contracts.

  • Token approvals are set exactly to the amount being deposited to avoid unnecessary approvals.

  • Yield harvesting is designed to be gas-efficient by calculating yield based on token balances.

Integration Requirements

To use the Aave adapter, the following components are required:

  1. An Aave lending pool address for the target network

  2. The corresponding aToken addresses for each asset

  3. Proper role assignments for the Yieldinator Facet