체인의정석

DeFI 소스코드 분석 - DyDX flashloan 본문

블록체인/디파이

DeFI 소스코드 분석 - DyDX flashloan

체인의정석 2021. 2. 4. 15:00
728x90
반응형

github.com/Devilla/eth-arbitrage/blob/master/contracts/Arbitrage.sol

 

Devilla/eth-arbitrage

A DeFi Arbitrage Bot with DyDx Flashloans. Contribute to Devilla/eth-arbitrage development by creating an account on GitHub.

github.com

위 소스코드를 분석하였다.

일단 주소값을 보면 kovan test network와 연결된 소스임을 확인 할 수 있었다.

pool에는 DyDx주소를 두어서 인식을 시켜주고, 각각 등록된 화폐를 넣어준다.

 

여기서 tokenToMarketId는 등록된 토큰에 해당된다. 만약 새로운 종류의 토큰을 등록하고 싶다면 위에서 정의 한 후 생성자에서 등록하면 될 것 같다. 하지만, 실제 환경과 같게 하기 위해서 WETH를 사용할 예정이다.

 

그 다음 라인까지 가면 드디어 flashloan이 나오게 된다. 받는 값은 token주소, amount양,  data가 있다. data의 경우에 처음에는 withdraw가 들어간 구조체를 넣어주고, 나중에 다시 돌려줄 땐 deposit을 넣어서 실행해주면 된다. 차익거래의 핵심은 한 명령어 안에 실행하는 것이므로, 이전 포스팅에서 설명한 총 3단계에 걸친 차익거래 로직이 실행되게 된다.

 

contract DyDxFlashLoan is Structs {
    DyDxPool pool = DyDxPool(0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e);

    address public WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
    address public SAI = 0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359;
    address public USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
    address public DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
    mapping(address => uint256) public currencies;

    constructor() public {
        currencies[WETH] = 1;
        currencies[SAI] = 2;
        currencies[USDC] = 3;
        currencies[DAI] = 4;
    }

    modifier onlyPool() {
        require(
            msg.sender == address(pool),
            "FlashLoan: could be called by DyDx pool only"
        );
        _;
    }

    function tokenToMarketId(address token) public view returns (uint256) {
        uint256 marketId = currencies[token];
        require(marketId != 0, "FlashLoan: Unsupported token");
        return marketId - 1;
    }

    // the DyDx will call `callFunction(address sender, Info memory accountInfo, bytes memory data) public` after during `operate` call
    function flashloan(address token, uint256 amount, bytes memory data)
        internal
    {
        IERC20(token).approve(address(pool), amount + 1);
        Info[] memory infos = new Info[](1);
        ActionArgs[] memory args = new ActionArgs[](3);

        infos[0] = Info(address(this), 0);

        AssetAmount memory wamt = AssetAmount(
            false,
            AssetDenomination.Wei,
            AssetReference.Delta,
            amount
        );
        ActionArgs memory withdraw;
        withdraw.actionType = ActionType.Withdraw;
        withdraw.accountId = 0;
        withdraw.amount = wamt;
        withdraw.primaryMarketId = tokenToMarketId(token);
        withdraw.otherAddress = address(this);

        args[0] = withdraw;

        ActionArgs memory call;
        call.actionType = ActionType.Call;
        call.accountId = 0;
        call.otherAddress = address(this);
        call.data = data;

        args[1] = call;

        ActionArgs memory deposit;
        AssetAmount memory damt = AssetAmount(
            true,
            AssetDenomination.Wei,
            AssetReference.Delta,
            amount + 1
        );
        deposit.actionType = ActionType.Deposit;
        deposit.accountId = 0;
        deposit.amount = damt;
        deposit.primaryMarketId = tokenToMarketId(token);
        deposit.otherAddress = address(this);

        args[2] = deposit;

        pool.operate(infos, args);
    }
}

여기서 call.data가 무엇을 의미하는지 모르겠어서 소스코드를 찾아 보았다.

     * Arguments that are passed to Solo in an ordered list as part of a single operation.
     * Each ActionArgs has an actionType which specifies which action struct that this data will be
     * parsed into before being processed.
     */
    struct ActionArgs {
        ActionType actionType;
        uint256 accountId;
        Types.AssetAmount amount;
        uint256 primaryMarketId;
        uint256 secondaryMarketId;
        address otherAddress;
        uint256 otherAccountId;
        bytes data;
    }

아무래도 딱히 역할은 무엇인지 모르겠지만 call을 할때 사용한는것을 볼 수 있었다.

 

다른 사람은 어떻게 할지 한참을 찾아보다가 깃허브에서 한 소스를 발견하였다.

github.com/fifikobayashi/Aggregated-Flashloan/blob/master/contracts/AggregatedFlashloans.sol

 

fifikobayashi/Aggregated-Flashloan

Truffle project which uses the Kollateral protocol to aggregate flash liquidity from both DyDx and Aave within the same transaction. - fifikobayashi/Aggregated-Flashloan

github.com

pragma solidity >= 0.5.0 < 0.7.0;
import "@kollateral/contracts/invoke/IInvoker.sol";
import "@kollateral/contracts/invoke/KollateralInvokable.sol";
import "@openzeppelin/contracts/ownership/Ownable.sol";

contract AggregatedFlashloans is KollateralInvokable {

  address owner; // contract owner
  address internal INVOKER_ROPSTEN;

  // initialize deployment parameters
  constructor() public{
      owner = msg.sender;
       INVOKER_ROPSTEN = address(0x234A76352e816c48098F20F830A21c820085b902); //Kollateral inovker on Ropsten
  }

  // only the contract owner can call fund
  modifier onlyOwner() {
      require(msg.sender == owner);
      _;
  }

  // initializes the flash loan aggregation parameters
  function invoke() external payable {
    address to = address(this);
    bytes memory data = "";
    address tokenAddress = address(0x0000000000000000000000000000000000000001); //Ropsten ETH
    uint256 tokenAmount = 1e18; // i.e. 1 ETH worth
    IInvoker(INVOKER_ROPSTEN).invoke(to, data, tokenAddress, tokenAmount);
  }

  /**
    Mid-flash loan aggregation logic i.e. what you do with
    the temporarily acquired flash liquidity from DyDx and Aave
  */
  function execute(bytes calldata data) external payable {

    // ***Step 1: Arbitrage or liquidation or whatever logic goes here

    repay(); // ***Step 2: Repay the aggregated flash loan + above fees
    /* Remember to factor in:
        + 6bps in Kollateral aggregation fee
        + 9bps in native Aave flash loan fee
        + 2 Wei in native DyDx floan loan fee
    */

  }

  /**
    ***Step 3: sweep entire balance on the contract back to contract owner
  */
  function WithdrawBalance() public payable onlyOwner {
      msg.sender.transfer(address(this).balance); // withdraw all ETH
  }

}

 

여기서 보면 data는 공백으로 두었다. 공백으로 두어도 작동한다면, 공백으로 두고 돌려보기로 하였다. 나머지 입력값은 콜레터럴을 쓰던, AAVE를 쓰던 DyDX를 쓰던 똑같은것 같다. 

 

    function getFlashloan(address flashToken, uint256 flashAmount, address arbToken, bytes calldata zrxData, uint256 oneSplitMinReturn, uint256[] calldata oneSplitDistribution) external payable onlyOwner {
        uint256 balanceBefore = IERC20(flashToken).balanceOf(address(this));
        bytes memory data = abi.encode(flashToken, flashAmount, balanceBefore, arbToken, zrxData, oneSplitMinReturn, oneSplitDistribution);
        flashloan(flashToken, flashAmount, data); // execution goes to `callFunction`

        // and this point we have succefully paid the dept
    }

 

 

github.com/cryptopixelfrog/DyDxFlashloanArb/blob/master/contracts/dydx/DyDxFlashLoan.sol

 

cryptopixelfrog/DyDxFlashloanArb

Get Flashloan from DyDx, then do arbitrage trading between Kyber and Uniswap - cryptopixelfrog/DyDxFlashloanArb

github.com

다음 소스코드를 본 결과

data안에 들어간 변수는 차익거래를 실행할 때 사용되는 변수임을 알 수 있었다. 차익거래에 필요한 입력값을 한번에 받아와서 넘기면 해당 입력값을 통해서 차익거래를 실행하게 된다. 따라서 넘기는 변수로는 차익거래 시에 필요한 변수를 상황에 맞게 넣어주면 된다.

 

 

 

이에 관련하여 더 찾아보니 eip 3156이 있었다. 

eips.ethereum.org/EIPS/eip-3156

 

EIP-3156: Flash Loans

Details on Ethereum Improvement Proposal 3156 (EIP-3156): Flash Loans

eips.ethereum.org

 

728x90
반응형
Comments