1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

EthereumAdvent Calendar 2021

Day 12

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

Last updated at Posted at 2021-12-11

自己紹介

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

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

フラッシュローンって?

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

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

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

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

AAVEって?

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

本題

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

V1のフラッシュローン

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

以下はV1のLendlingPool.solのflashLoan関数を簡略化したコードです。
_receiver_amount量の借りるトークン_reserveの受け取りアドレスで、コールバックexecuteOperation()を受け取るためにIFlashLoanReceiverインターフェースを実装している必要があります。

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

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

    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のflashLoan関数を簡略化したコードです。
実際、違いはかなり多いのですが今回では一点に絞ってお伝えします。
基本的にはV1と同じフラッシュローンのプロセスを踏みますが、V1ではrequire(...)で借りたトークン+feeがきちんと返済されたかをチェックするのに対して、V2ではそれに相当するrequire(...)はありません。代わりに、safeTransferFrom()でトークンを借りたアドレスから、返済すべき量(借りた量+fee)のトークンを回収します。

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

  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内でCollateral SwapやCono financeのような仕組みのローン・プロテクションが可能になります!

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

Cono Financeのローン・プロテクション

image.png
画像は The defiantより

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

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

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?