I’m working on a project which involves tracking and withdrawing AAVE yield on chain while maintaining the original deposit balance. I’m confused as to how to accurately calculate AAVE yield at a specific point in time. My initial approach was to just save the deposited amount, and then calculate yield by:

Yield = aToken_balance - deposited_amount

But this doesn’t seem to work. I’ve found that after calculating this yield value, if we attempt to withdraw that value our resulting aToken balance will be off (either above or below) our initial deposit amount. I’m aware that aTokens have dynamic supply/balanceOf, but there must be a way to accurately calculate: “I have X money at this point in time”

I'm testing on a fork of the Polygon Mainnet, I'm wondering if it has something to do with my test environment (i.e. simulating time passing on a fork of mainnet where nobody is actually interacting with AAVE)? A test contract and Screenshots of the issue are below. Any insights are much appreciated.

pragma solidity ^0.8.4;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IPool.sol";

contract AaveTest2 {
    IPool public aavePoolManager = IPool(0x794a61358D6845594F94dc1DB02A252b5b4814aD);
    address public usdc = 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174;
    address public ausdc = 0x625E7708f30cA75bfd92586e17077590C60eb4cD;
    address public dai = 0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063;
    address public adai = 0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE;

    uint public totalStakedUSDC;
    uint public totalStakedDAI;

    constructor() {}

    function aaveDepositUSDC(uint amount) public {
        IERC20(usdc).approve(address(aavePoolManager), amount);
        aavePoolManager.supply(usdc, amount, address(this), 0);
        totalStakedUSDC += amount;

    function calculateYieldUSDC() public view returns (uint) {
        uint yield = IERC20(ausdc).balanceOf(address(this)) - totalStakedUSDC;
        return yield;
    function withdrawYieldUSDC() public {
        uint yield = calculateYieldUSDC();
        aavePoolManager.withdraw(usdc, yield, address(this));

    function withdrawUSDC(uint amount) public {
        aavePoolManager.withdraw(usdc, amount, address(this));
        totalStakedUSDC -= amount;

    function aaveDepositDAI(uint amount) public {
        IERC20(dai).approve(address(aavePoolManager), amount);
        aavePoolManager.supply(dai, amount, address(this), 0);
        totalStakedDAI += amount;

    function calculateYieldDAI() public view returns (uint) {
        uint yield = IERC20(adai).balanceOf(address(this)) - totalStakedDAI;
        return yield;
    function withdrawYieldDAI() public {
        uint yield = calculateYieldDAI();
        aavePoolManager.withdraw(dai, yield, address(this));

    function withdrawDAI(uint amount) public {
        aavePoolManager.withdraw(dai, amount, address(this));
        totalStakedDAI -= amount;

Example 1

Example 2

Example 3

1 Answer 1


If I remember correctly. Aave uses an index to track the value of your aToken.

Step 1, retreive the index

(, , , , , , ,uint256 liquidityIndex, , ) = IdataProvider.getReserveData(asset_address);

This should return a number in ray

Step 2, retrieve your scaled balance

uint256 scaledBalance = AToken(asset_address).scaledBalanceOf(address(this));

Step 3, do the maths to get your current value

uint256 value_now = (scaledBalance * liquidityIndex) * 1e18 / 1e27;
  • Exactly what I needed thank you!
    – xde
    Commented Sep 29, 2022 at 16:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.