AAVE V1とV2のフラッシュローンの違い

### 自己紹介
こんにちは、ばくちーと申します。
Ethereumを中心にバウンティによく参加してます。 バウンティに取り組んでいる過程でAaveを使ったときに気づいたことでも書いてみようかなと思いました。

この記事ではAaveV1とV2のフラッシュローンの実装の違いを掘り下げ、V2が優れている点を説明します。「フラッシュローンに違いがあるの？」と疑問に思ってくれる方には興味を持っていただけるはずです! 内容はたぶんニッチだと思います!

### フラッシュローンって？
まず、フラッシュローンを簡単におさらいしておこうと思います。フラッシュローンは面白いので、好きな方も少なくないでしょう。知っている方は[ここ](#本題)から読み進められます。

フラッシュローンは、トランザクション終了時に返済さえすれば無担保でトークンを好きなだけ借りることができるローンです。
つまり、流動性があれば一億円相当のトークンも無担保で借りることが可能です、ただしトランザクションの最後に返済する必要があり、さもなれけばトランザクションは失敗します。

簡単な利用例を挙げるなら、アービトラージでしょうか。大量のトークンを借りてDEX間の鞘を抜き取ります。もしくは、借りた大量のトークンを使って、市場に歪みを作りそこから利益を上げるなどができます。その他の利用例は次の章で説明します。

Aaveはフラッシュローンを機能の一つとして提供してます。(他のDeFiでもフラッシュローンを提供しているところはあります）

### AAVEって？
おそらく読者の方はほとんどの方がご存知かと思いますが一言で言うと、Aaveはトークンのレンディングプロトコルです。つまり、
トークンを預けて金利収入を得たり、預けたトークンを担保に借り入れを行うことがノンカストディアルでできます。 例えば、Aaveではフラッシュローンをうまく使うことで、担保トークンを別の種類のトークンに変えることを、負債を返済せずに1回のトランザクションのみでできます。

## 本題
いよいよ本題です。V1とV2ではフラッシュローンの実装が違います。
**結論から言うと、返済時のチェックの条件が異なります。** V2の実装では面白いことができるようになります。

#### V1のフラッシュローン
Aaveの設計ではフラッシュローンのエントリーポイントはトークンに関わらず`LendingPool`コントラクトです。これはV2でも変わりません。

以下はV1の[LendlingPool.sol](https://github.com/aave/aave-protocol/blob/4b4545fb583fd4f400507b10f3c3114f45b8a037/contracts/lendingpool/LendingPool.sol#L888-L891)のflashLoan関数を簡略化したコードです。
`_receiver`は`_amount`量の借りるトークン`_reserve`の受け取りアドレスで、コールバック`executeOperation()`を受け取るために`IFlashLoanReceiver`インターフェースを実装している必要があります。

**次に、`require(...)`のところで借りたトークン+feeがきちんと返済されたかをチェックしています。** **これがV1の返済時のチェックの条件です。**

**つまり、V1ではフラッシュローンの前後で借りたトークン(例えば、USDC)の量が`LendingPool`内で不変でなければならないです。**

```solidity
    function flashLoan(address _receiver, address _reserve, uint256 _amount, bytes memory _params)
        public
        nonReentrant
        onlyActiveReserve(_reserve)
        onlyAmountGreaterThanZero(_amount)
    {
        ...
        //get the FlashLoanReceiver instance
        IFlashLoanReceiver receiver = IFlashLoanReceiver(_receiver);

        //transfer funds to the receiver
        core.transferToUser(_reserve, userPayable, _amount);

        //execute action of the receiver
        receiver.executeOperation(_reserve, _amount, amountFee, _params);

        //check that the actual balance of the core contract includes the returned amount
        uint256 availableLiquidityAfter = _reserve == EthAddressLib.ethAddress()
            ? address(core).balance
            : IERC20(_reserve).balanceOf(address(core));

        // ここで、借りたトークン+feeが返済されたかをチェック
        require(
            availableLiquidityAfter == availableLiquidityBefore.add(amountFee),
            "The actual balance of the protocol is inconsistent"
        );
        ...
    }
```

#### V2のフラッシュローン 
以下はV2の[LendlingPool.sol](https://github.com/aave/protocol-v2/blob/master/contracts/protocol/lendingpool/LendingPool.sol#L537)のflashLoan関数を簡略化したコードです。
実際、違いはかなり多いのですが今回では一点に絞ってお伝えします。
基本的にはV1と同じフラッシュローンのプロセスを踏みますが、V1では`require(...)`で借りたトークン+feeがきちんと返済されたかをチェックするのに対して、V2ではそれに相当する`require(...)`はありません。**代わりに、`safeTransferFrom()`でトークンを借りたアドレスから、返済すべき量(借りた量+fee)のトークンを回収します。**

**こうすることで、V2ではV1と違ってフラッシュローンの前後で借りたトークン(例えば、USDC)の量が`LendingPool`内で不変である必要はなくなります。**

```solidity
  function flashLoan(
    address receiverAddress,
    address[] calldata assets,
    uint256[] calldata amounts,
    uint256[] calldata modes,
    address onBehalfOf,
    bytes calldata params,
    uint16 referralCode
  ) external override whenNotPaused {
    FlashLoanLocalVars memory vars;

    address[] memory aTokenAddresses = new address[](assets.length);
    uint256[] memory premiums = new uint256[](assets.length);

    vars.receiver = IFlashLoanReceiver(receiverAddress);

    for (vars.i = 0; vars.i < assets.length; vars.i++) {
      aTokenAddresses[vars.i] = _reserves[assets[vars.i]].aTokenAddress;
      premiums[vars.i] = amounts[vars.i].mul(_flashLoanPremiumTotal).div(10000);
      IAToken(aTokenAddresses[vars.i]).transferUnderlyingTo(receiverAddress, amounts[vars.i]);
    }

    require(
      vars.receiver.executeOperation(assets, amounts, premiums, msg.sender, params),
      Errors.LP_INVALID_FLASH_LOAN_EXECUTOR_RETURN
    );

    for (vars.i = 0; vars.i < assets.length; vars.i++) {
      vars.currentAsset = assets[vars.i];
      vars.currentAmount = amounts[vars.i];
      vars.currentPremium = premiums[vars.i];
      vars.currentATokenAddress = aTokenAddresses[vars.i];
      vars.currentAmountPlusPremium = vars.currentAmount.add(vars.currentPremium);

        IERC20(vars.currentAsset).safeTransferFrom(
          receiverAddress,
          vars.currentATokenAddress,
          vars.currentAmountPlusPremium
        );
      } else {
       ...
      }
      ...
    }
  }
```
## 結論：何が嬉しいのか？
**V1ではフラッシュローンの前後で借りたトークン(例えば、USDC)の量が不変でなければならないが、V2ではそうではなくて借りた量＋feeさえ返せばいいことを知りました**

では、V2では何が嬉しいのか

**V1の実装では、フラッシュローンのコールバック関数内で`aToken`を償還することができません。しかし、V2では借りた量さえ返せばいいので、[ここの章](#aaveって？)で述べたようなAave内でCollateral Swapや[Cono finance](https://www.cono.finance/assets)*のような仕組みのローン・プロテクションが可能になります！**

*Cono Financeのローンプロテクションは、借り入れしているポジションが指定のヘルスファクターを下回った際に、Gelatoにより自動で担保を崩すことで一部ローンを返済し、清算を回避するDeFiです。Gelatoはインセンティブが与えられており、条件を満たした際、代わりにスマートコントラクトで定めたタスクを実行してくれるボットみたいなものです。

##### Cono Financeのローン・プロテクション
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/902476/7a6801c1-49a2-20ff-a90f-c7fec63bb284.png?width=50%&height=auto)
[画像は The defiantより](https://thedefiant.io/gelato-finance-liquidation-tool/)


##　おわりに
今回は、AaveのフラッシュローンについてV1とV2の実装の違いを理解し、V2ではフラッシュローンのコールバック関数内で、`aToken`を償還できることを理解しました。それによって、ローン・プロテクションのようなことができるようになり、フラッシュローンの利用の幅が広がったと言えます。

おそらくニッチな内容でしたが、この発見に興味を持っていただけると嬉しいです。