🌪️

FlashLoneの始め方【AAVE】

2022/08/19に公開約5,900字

はじめに

1つのトランザクションに「借りる」と「返す」を含めると、無担保でいくらでも借りれるという意味の分からない仕組みに興味が湧いて、AAVEを触ってみたものの動くサンプルがなさすぎて泣いたので、後の人が困らないように解説と動くコードを残していきます。(動かないコード多すぎ!)

FlashLoanについて

1つのトランザクションの中であれば、無担保でトークンを借りれるという仕組みです。条件は最初に引き出したトークンの量と、後から返却されたトークンの量が同じであれば、成立し、違っていれば不成立になるという仕組みです。

Aaveについて

とはいえ存在しないものを借りることはできません。なのでステーキングしてくれるユーザーを集め、そこからFlashloanができるようにするサービスがいくつかあり、今回はその中でも簡単なAaveを使っていきます。

Aave ポータル

https://staging.aave.com/

Aave ドキュメント

https://docs.aave.com/developers/

全体のイメージ

基本的にはILendingPoolAddressProviderを経由して、Poolのアドレスを拾い、IPoolを経由して、flashloan関数を叩きにいくイメージです。

V2とV3の違い

基本的にV2でもV3でもFlashloanできるのですが、V3の方が直感的だがテストネットのみ、V2の方が分かりづらいがメインネットもできるって感じなので、とりあえずV3からコードを書いていく。

コード(V3)

全体の解説

V2とV3のどっちもで厄介なのが実行する下のスマコンに手数料が入ってないと動かないこと。(自分のウォレットじゃない)なのでV3の方はFaucetを準備して予めある程度のトークンを入れておく処理が入っている。ここはテストネット仕様なのでメインネットの場合は消して、スマコンに送金してあげる必要がある。

// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.10;
pragma experimental ABIEncoderV2;

import {
  IPoolAddressesProvider
} from "https://github.com/aave/aave-v3-core/contracts/interfaces/IPoolAddressesProvider.sol";
import { IPool } from "https://github.com/aave/aave-v3-core/contracts/interfaces/IPool.sol";
import { IFlashLoanSimpleReceiver } from "https://github.com/aave/aave-v3-core/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol";
import { IERC20 } from "https://github.com/aave/aave-v3-core/contracts/dependencies/openzeppelin/contracts/IERC20.sol";
import { SafeMath } from "https://github.com/aave/aave-v3-core/contracts/dependencies/openzeppelin/contracts/SafeMath.sol";

interface IFaucet {
    function mint(
        address _token,
        uint256 _amount
    ) external;
}

abstract contract FlashLoanSimpleReceiverBase is IFlashLoanSimpleReceiver {
  using SafeMath for uint256;

  IPoolAddressesProvider public immutable override ADDRESSES_PROVIDER;
  IPool public immutable override POOL;
  IFaucet public immutable FAUCET;

  constructor(IPoolAddressesProvider provider, IFaucet faucet) {
    ADDRESSES_PROVIDER = provider;
    POOL = IPool(provider.getPool());
    FAUCET = faucet;
  }
}


contract MySimpleFlashLoanV3 is FlashLoanSimpleReceiverBase {
    using SafeMath for uint256;

    constructor(IPoolAddressesProvider _addressProvider, IFaucet _faucet) FlashLoanSimpleReceiverBase(_addressProvider, _faucet) {}

    function executeOperation(
        address asset,
        uint256 amount,
        uint256 premium,
        address initiator,
        bytes calldata params
    )
        external
        override
        returns (bool)
    {
        uint amountOwed = amount.add(premium);
        FAUCET.mint(asset,premium);
        IERC20(asset).approve(address(POOL), amountOwed);

        return true;
    }

    function executeFlashLoan(
        address asset,
        uint256 amount
    ) public {
        address receiverAddress = address(this);

        bytes memory params = "";
        uint16 referralCode = 0;

        POOL.flashLoanSimple(
            receiverAddress,
            asset,
            amount,
            params,
            referralCode
        );
    }
}

実行の際に必要になったもの

デプロイチェーン:Goerli
LendingProviderAddress:0xc4dCB5126a3AfEd129BC3668Ea19285A9f56D15D
Faucet:0x1ca525Cd5Cb77DB5Fa9cBbA02A0824e283469DBe
DAI:0xDF1742fE5b0bFc12331D8EAec6b478DfDbD31464

参考文献(引用元)

https://www.youtube.com/watch?v=LzaS8IiqnPY
https://github.com/defispartan/hackmoney-demo

コード(V2)

// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.6.12;

import { ILendingPoolAddressesProvider } from "https://github.com/aave/protocol-v2/blob/master/contracts/interfaces/ILendingPoolAddressesProvider.sol";
import { ILendingPool } from "https://github.com/aave/protocol-v2/blob/master/contracts/interfaces/ILendingPool.sol";
import { IERC20 } from "https://github.com/aave/protocol-v2/blob/master/contracts/dependencies/openzeppelin/contracts/IERC20.sol";
import { SafeMath } from "https://github.com/aave/protocol-v2/blob/master/contracts/dependencies/openzeppelin/contracts/SafeMath.sol";

contract helloFlashLoan {
    using SafeMath for uint256;
    ILendingPoolAddressesProvider provider;
    address dai;

    constructor(address _provider, address _dai) public {
        provider = ILendingPoolAddressesProvider(_provider);
        dai = _dai;
    }

    function executeOperation(
        address[] calldata assets,
        uint256[] calldata amounts,
        uint256[] calldata premiums,
        address initiator,
        bytes calldata params
    )
        external
        returns (bool)
    {
        for (uint i = 0; i < assets.length; i++) {
            uint amountOwing = amounts[i].add(premiums[i]);
            IERC20(assets[i]).approve(address(provider.getLendingPool()), amountOwing);
        }
        return true;
    } 

    function startFlashLoan(uint amount) external {
        address receiverAddress = address(this);

        address[] memory assets = new address[](1);
        assets[0] = dai;

        uint256[] memory amounts = new uint256[](1);
        amounts[0] = amount;

        uint256[] memory modes = new uint256[](1);
        modes[0] = 0;

        address onBehalfOf = address(this);
        bytes memory params = "";
        uint16 referralCode = 0;

        ILendingPool lendingPool = ILendingPool(provider.getLendingPool());
        lendingPool.flashLoan(
            receiverAddress,
            assets,
            amounts,
            modes,
            onBehalfOf,
            params,
            referralCode
        );
    }
}

実行の際に必要になったもの

デプロイチェーン:Kovan
LendingAddressProvider:0x67FB118A780fD740C8936511947cC4bE7bb7730c
DAI:0xFf795577d9AC8bD7D90Ee22b6C1703490b6512FD

参考文献(引用元)

https://youtu.be/03jO9vbrXvY

おわりに

これらのコードはそのまま貼り付けても動くので最初に体験するにはとても良いサンプルであると思う。しかし、実際に色々と使う場合はスマコンからトークンを引き出すためのwithdraw関数など色々と作る必要があると思う。

Discussion

ログインするとコメントできます