# v2 Distributor

## **Reasons for changes**

The Aave protocol, when calling `supply()`, may return a smaller amount of `aToken` than was deposited. This leads to the fact that the `lastUnderlyingBalance` is less than the `deposited` amount. This is unexpected behavior that causes an error in the `Distributor` contract code during staking, withdrawal, or claiming operations. An example of such a transaction will be provided below.

Transaction - [here](https://etherscan.io/tx/0xf14e2d9def4683f8b4eddc26be0de86b3931055d85d3746d43db038b4cfd68bb).

Location of error, `Distributor.sol -> _withdrawYield()` :

```solidity
 uint256 yield_ = depositPool.lastUnderlyingBalance - depositPool.deposited;
```

## **Yield**

Yield is calculated based on the amount of `aToken`, which may be a **few wei higher or lower** than the amount of tokens deposited. This leads to minimal inaccuracies in yield calculation, which is acceptable.&#x20;

{% hint style="info" %}
In practice, this means that if a user stake, for example, 100 USDC, the contract may receive 100.1 aUSDC. The yield will be calculated as 0.1 aUSDC and will not be available to the user immediately. This means that if the user stakes and then immediately tries to withdraw the deposited tokens, they may encounter an error on the smart contract, because 100 aUSDC will not be exactly equal to 100 USDC. The solution is to wait a few minutes for the yield from the Aave protocol to be accrued. **This will only occur if the user is the last staker and attempts to withdraw all of their tokens after the stake.**
{% endhint %}

## **Solution**

It is necessary to track the `aToken` balance independently from the deposit token, separating the `amount_` specified during deposit and withdrawal, and the one recorded in `underlyingAmount_`.

Pull request: <https://github.com/MorpheusAIs/SmartContracts/pull/60>.

## Consequences

We do not see any critical consequences from this issue. Only one deposit pool (wBTC) was affected and is currently unable to operate due to the error described above. Other deposit pools are functioning since the amount of `aToken` is greater than the deposited amount, which does not violate the contract logic. After the update, the `distributeRewards()` function will overwrite the `lastUnderlyingBalance` variable to the most recent value.

## Code changelog

#### supply()

A calculation of the actual amount of `aToken` received by the `Distributor` contract has been added to the `supply()` function. Thus, in the case of the Aave pool, we will add the actual `aTokens` received to the last calculated balance. The deposit token is deducted in the amount specified by the user (without changes). For stETH, the `underlyingAmount_` variable is set equal to the deposit amount (without changes).

```solidity
function supply(
  uint256 rewardPoolIndex_, 
  address holder_, 
  uint256 amount_
) external returns (uint256) {
  ...

  uint256 underlyingAmount_ = amount_;
  if (depositPool.strategy == Strategy.AAVE) {
    ...
    uint256 underlyingTokenBalanceBefore_ = IERC20(depositPool.aToken).balanceOf(address(this));
    AaveIPool(aavePool_).supply(depositPool.token, amount_, address(this), 0);
    uint256 underlyingTokenBalanceAfter_ = IERC20(depositPool.aToken).balanceOf(address(this));
    underlyingAmount_ = underlyingTokenBalanceAfter_ - underlyingTokenBalanceBefore_;
  }

  ...
  depositPool.lastUnderlyingBalance += underlyingAmount_;

  return amount_;
}

```

#### withdraw()

When withdrawing the deposit token, the exact amount check, which was previously implemented only for stETH, has now been applied to all deposit pools. This was done to improve security, as Aave declares the "exact" withdrawal amount in the return value, and it can be assumed that this amount may differ. Additionally, a calculation has been added to determine the precise amount of `aToken` spent.

```solidity
function withdraw(
  uint256 rewardPoolIndex_,
  address receiver_,
  uint256 amount_
) external returns (uint256) {
  ...
  uint256 underlyingAmount_ = amount_;

  uint256 depositTokenBalanceBefore_ = IERC20(depositPool.token).balanceOf(receiver_);
  if (depositPool.strategy == Strategy.AAVE) {
    uint256 underlyingTokenBalanceBefore_ = IERC20(depositPool.aToken).balanceOf(address(this));
    AaveIPool(AaveIPoolAddressesProvider(aavePoolAddressesProvider).getPool()).withdraw(
      depositPool.token,
      amount_,
      receiver_
    );
    uint256 underlyingTokenBalanceAfter_ = IERC20(depositPool.aToken).balanceOf(address(this));
    underlyingAmount_ = underlyingTokenBalanceBefore_ - underlyingTokenBalanceAfter_;
  } else {
    IERC20(depositPool.token).safeTransfer(receiver_, amount_);
  }
  uint256 depositTokenBalanceAfter_ = IERC20(depositPool.token).balanceOf(receiver_);
  amount_ = depositTokenBalanceAfter_ - depositTokenBalanceBefore_;

  depositPool.deposited -= amount_;
  depositPool.lastUnderlyingBalance -= underlyingAmount_;

  ...
}
```

#### \_withdrawYield()

Processing has been added to ensure that the `lastUnderlyingBalance_` amount must be greater than the `deposited_` amount.

```solidity
function _withdrawYield(
  uint256 rewardPoolIndex_, 
  address depositPoolAddress_
) private {
  ...
  uint256 lastUnderlyingBalance_ = depositPool.lastUnderlyingBalance;
  uint256 deposited_ = depositPool.deposited;
  if (lastUnderlyingBalance_ <= deposited_) {
    return;
  }

  uint256 yield_ = lastUnderlyingBalance_ - deposited_;
  if (depositPool.strategy == Strategy.AAVE) {
    uint256 underlyingTokenBalanceBefore_ = IERC20(depositPool.aToken).balanceOf(address(this));
    AaveIPool(AaveIPoolAddressesProvider(aavePoolAddressesProvider).getPool()).withdraw(
      depositPool.token,
      yield_,
      l1Sender
    );
    uint256 underlyingTokenBalanceAfter_ = IERC20(depositPool.aToken).balanceOf(address(this));
    yield_ = underlyingTokenBalanceBefore_ - underlyingTokenBalanceAfter_;
  } else {
    ...
  }
  ...
}

```

#### Other

Some internal variable names were also changed, and the version and name of the smart contract were updated (Distributor -> DistributorV2).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://gitbook.mor.org/smart-contracts/documentation/distribution-protocol/v7-protocol/contracts/distributor/v2-distributor.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
