LoginSignup
15
7

More than 1 year has passed since last update.

Dex(分散型取引所)の一つであるPancakeSwapを開発面から理解する

Posted at

この記事は、Dex(分散型取引所)の一つであるPancakeSwapを開発面から理解するための記事です。

おそらく、用語をググったり、BscScanやGithubを眺めたりすることになりますので珈琲片手に暇なときにご覧いただければ幸いです。

PacakeSwapのソースコードは、Githubで公開されています。
https://github.com/pancakeswap

対象読者

  • DeFi、DEXなどの用語がわかる
  • BinanceSmartChainを知っている
  • PancakeSwapを使ったことがある
  • 開発に興味がある
  • BlockChainとWEBアプリってどうやって接続されてるのかしりたい

はじめに

この記事では、PancakeSwapのDEX機能を確認していきます。

PancakeSwapの概要

概要

PancakeSwapとは、DEXの一つです。
公式ドキュメント

PancakeSwapは、イーサリアムでの代表的なDEXであるUniswapのフォークとしてBinanceSmartChainに作成されました。
UniswapもPancakeSwapと同様にGithubにソースコードが公開されており、誰でも見れるようになっています。
そのため、PancakeSwapだけでなく同じ形式のDEXが大量に作成されました。

PancakeSwapは、様々な機能を有しています。
詳しくはドキュメントを見ていただければよいのですが、今回確認する機能は下記の2つです。

  • Exchange
    • 俗に言う、スワップ。トークンとトークンの交換機能です。
  • Liquidity
    • 流動性の提供機能です。

構成

PancakeSwapでは、主にブロックチェーン側の処理とフロントエンドの処理に別れています。

フロントエンドは今までのWEBアプリと同じで、AWSやGCP上でうごいているようなWEBシステムを考えてください。

ブロックチェーン側の処理については、コントラクトという単位でBinanceSmartChain上に存在しています。

スワップ機能
https://pancakeswap.finance/swap

流動性提供機能
https://pancakeswap.finance/liquidity

PancakeSwap(Dex).png

スワップ(トークンの交換)について

一般的なスワップ

トークンとトークンを交換する行為を「スワップする」と言います。
しかし、これにはいくつかのステップがあります。

  1. Approve

    • 交換したいトークンを使用する前に、Approveを行う必要があります。
    • Approveとは、DEX(今回であればPancakeSwap)に対して、自分のウォレットアドレスの残高の更新権限を与える行為です。
    • 無制限の値を許可することもできますし、今回使用する量のみを許可することもできます。
    • 許可するたびに、ガス代がかかります。
    • 「許可を取り消す」という行為は、許可する数量を0に更新するということになります。
  2. Liquidity

    • 流動性が存在しない場合、トークンを交換することができません。 (流動性がわからない場合は、先にDEXについて一度勉強することをおすすめします。)
    • 流動性は自身が提供者となることもできますが、他の人が提供してくれていれば、それを利用できます。
    • 流動性の提供者には、トークンを交換する者が手数料を支払います。DEXの仕組みの中に組み込まれています。
  3. Transfer

    • トークンを交換します。
    • トークンには、基軸通貨と呼ばれるそのチェーン上のメインのトークンが存在します。(BinanceSmartChainであればWBNB)

PancakeSwapでのスワップの流れ

それでは実際にPancakeSwap上で、あるトークンを交換した際の流れを確認していきましょう。
BlockChain上のトランザクションは誰でも確認することができるようになっています。

PancakeSwapのルーターのアドレス

Router:https://bscscan.com/address/0x10ed43c718714eb63d5aa57b78b54704e256024e

これは、PancakeSwapのルーターのアドレスです。
ルーターとは、PancakeSwapの中でトークンのスワップ機能についてを司るコントラクトです。
この、ルーターのトランザクションを確認していくことで、どのようにトークンの交換が行われているのかを誰でも確認することができます。

トランザクションの確認

それでは、ルーターのトランザクションの中からランダムに抽出した一つのトランザクションを確認していきます。
https://bscscan.com/tx/0xd1e48363d2accf13f20f177b192c89e15b5fba97a9a1c767eae1c5d29e310341

添付:トランザクションハッシュ
txhash_log_overview.png

BSCSCANにてトランザクションをチェックしていきます。
添付のようにOverviewを確認することでWBNBとTHCトークンのスワップであることがわかります。

このOverviewから、図にも記載していますが大まかに下記のことがわかります。

  • トークンの持ち主(ウォレットアドレス)が「0x5d37fdb31d06d76603a908c69b7c3d05a34d8024」であること
  • WBNBをPancakeSwapを使用してTHCトークンへ変換していること
  • PancakeSwapのルーターは、流動性トークンを介して持ち主のアドレスへTHCトークンを渡していること
  • swapETHForExactTokensが利用されていることおよび、その引数の内容

それでは、PancakeSwapルーターのコントラクトコードから、swapETHForExactTokensの内容を見ていきましょう。
※内部で呼び出されている_swap()処理も一緒に記載しています。

    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
        external
        virtual
        override
        payable
        ensure(deadline)
        returns (uint[] memory amounts)
    {
        // InputDataのデコードにより下記がわかります。
        // amountOut : 200000000000000000000(取得したい200THC<Decimal18>)
        // path:    [0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c, 0x24802247bD157d771b7EFFA205237D8e9269BA8A]
        //  [WBNB, THC]
        // to: 0x5D37fdb31d06D76603a908C69b7c3d05A34D8024(持ち主のアドレス)
        // deadline: 1643445415 (日時(Tokyo)=2022/01/29 17:36:55)

        // 交換元が基軸通貨であることをチェック
        // WETHとなっているが、アドレスの実態はWBNB。イーサチェーンのUniswapをフォークして作成しているため、WETHを使いまわしていると思われる。
        require(path[0] == WETH, 'PancakeRouter: INVALID_PATH');

        // 200THCを得るために必要なWBNBの数量を取得する。
        amounts = PancakeLibrary.getAmountsIn(factory, amountOut, path);

        // amountsはWBNBとTHCの数量が返ってくるため、WBNBをチェック。
        // msg.valueには交換したいWBNB(WebUIより入力)が入っている。
        // WebUIより入力された値と、実際の交換する値の正当性をチェックしている
        require(amounts[0] <= msg.value, 'PancakeRouter: EXCESSIVE_INPUT_AMOUNT');

        // WBNBコントラクトにてデポジット処理を行う
        IWETH(WETH).deposit{value: amounts[0]}();

        // WBNBコントラクトにてtransfer処理を行う
        assert(IWETH(WETH).transfer(PancakeLibrary.pairFor(factory, path[0], path[1]), amounts[0]));

        // _swap関数を呼び出す
        _swap(amounts, path, to);

        // safeTransferETH処理を呼び出す。
        // refund dust eth, if any
        if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
    }



    // **** SWAP ****
    // requires the initial amount to have already been sent to the first pair
    function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {
        // InputData、swapETHForExactTokens処理より
        // amounts: [23943652015054953, 200000000000000000000] ([WBNB, THC])
        // path:    [0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c, 0x24802247bD157d771b7EFFA205237D8e9269BA8A]
        //  [WBNB, THC]
        // to: 0x5D37fdb31d06D76603a908C69b7c3d05A34D8024(持ち主のアドレス)

        // pathの長さ(今回は2)-1回だけループ処理を行う。
        // 今回の場合は1度だけ処理が行われる。
        for (uint i; i < path.length - 1; i++) {
            // input = path[0], output = path[1]を設定する
            (address input, address output) = (path[i], path[i + 1]);

            // ペアの順番が常におなじになるようにソートする。
            (address token0,) = PancakeLibrary.sortTokens(input, output);

            // amountOutへTHCの値を設定する(200000000000000000000)
            uint amountOut = amounts[i + 1];

            // ソートした結果、により、amount0,1の値を調整する(三項演算子)
            (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));

            // 中継するトークンが存在する場合は、toの値を対象のトークンとする。
            // 最後もしくは中継が必要ない場合は、持ち主のアドレス(_to)が使われる。
            address to = i < path.length - 2 ? PancakeLibrary.pairFor(factory, output, path[i + 2]) : _to;

            // LPトークンのスワップ処理を呼び出す
            IPancakePair(PancakeLibrary.pairFor(factory, input, output)).swap(
                amount0Out, amount1Out, to, new bytes(0)
            );
        }
    }

※必要な部分だけ抜粋
library PancakeLibrary {
    using SafeMath for uint;

    // returns sorted token addresses, used to handle return values from pairs sorted in this order
    function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
        require(tokenA != tokenB, 'PancakeLibrary: IDENTICAL_ADDRESSES');
        (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
        require(token0 != address(0), 'PancakeLibrary: ZERO_ADDRESS');
    }

    // calculates the CREATE2 address for a pair without making any external calls
    function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(uint(keccak256(abi.encodePacked(
                hex'ff',
                factory,
                keccak256(abi.encodePacked(token0, token1)),
                hex'00fb7f630766e6a796048ea87d01acd3068e8ff67d078148a3fa3f4a84f69bd5' // init code hash
            ))));
    }

}

swapETHForExactTokensの中身を見てきましたが、様々な処理が行われていることがわかります。
毎回処理の中身を追いかけるのは大変です。

コントラクトの処理は、イベントでログを発行できるようになっており、主要な処理はそこで確認できます。

トランザクションログの確認

BinanceSmartChainでは主にSolidityという言語でコントラクトの処理が書かれています。
コントラクトでは、下記のようにeventを定義し、各functionの中でeventをemitすることでログを発行しています。

// WBNBトークンのデポジット処理の例
     ...
    event  Deposit(address indexed dst, uint wad);
     ...
    function deposit() public payable {
        balanceOf[msg.sender] += msg.value;
        emit Deposit(msg.sender, msg.value);
    }
     ...

この、発行されたログを確認することができるのが、Logsのタブとなります。
Logsタブに移ることで、コントラクトが発行したログを確認することができます。

txhash_log_eventlogs.png

それでは、Logsを順番に見ていきましょう。

1. WBNBトークンのDeposit

Addressに記載されているトークンアドレスを確認することで、対象のコントラクトがわかります。
https://bscscan.com/address/0xbb4cdb9cbd36b01bd1cbaebf2de08d9173bc095c
bscscanで確認すると、WBNBトークンコントラクトであることがわかります。

Nameに記載されている関数名から、対象の処理がわかります。(View Sourceをクリックするとコードが確認できます)
Name -> Deposit (index_topic_1 address dst, uint256 wad)
引数として下記が渡されていることがわかります。
address : 0x10ed43c718714eb63d5aa57b78b54704e256024e(PancakeSwapのルーター)
wad : 23943652015054953(Decimal:18のため、0.0239...WBNB)

View Sourceにてソースコードを確認してみます。
コメントに処理内容や補足事項を記載。

    function deposit() public payable {
        // EventLogから下記がわかる
        // msg.sender = 0x10ed43c718714eb63d5aa57b78b54704e256024e(PancakeSwapのルーター)
        // msg.value = 23943652015054953(WBNBの数量)

    // WBNBトークンにてPancakeSwapルーターアドレスのバランスを0.0239...WBNB増加する
        balanceOf[msg.sender] += msg.value;
        // EventLogを発行する
        Deposit(msg.sender, msg.value);
    }

つまり、最初のログでは下記のことがわかります。

WBNBトークンコントラクトにて、PancakeSwapのルータのバランスを0.0239...増加している。

2. WBNBトークンのTransfer

    function transferFrom(address src, address dst, uint wad)
    public
    returns (bool)
    {
        // EventLogから下記がわかる
        // src = 0x10ed43c718714eb63d5aa57b78b54704e256024e(PancakeSwapのルーター)
        // dst = 0x2fa22acdb65ce9763d927656909f14e4e66b5f08(LPトークン)
        // wad = 23943652015054953(WBNBの数量)

        // PancakeSwapのルーターに交換したいWBNBの数量以上のバランスが存在しているかをチェック
        require(balanceOf[src] >= wad);

        // 持ち主のアドレス(トランザクション発行元)がPancakeSwapのルーターではないこと
        // PancakeSwapのルーターが持ち主のアドレスの更新許可を持っていることなどをチェックする
        if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
            require(allowance[src][msg.sender] >= wad);
            allowance[src][msg.sender] -= wad;
        }

        // PancakeSwapのルーターからLPトークンへバランスを移行する
        balanceOf[src] -= wad;
        balanceOf[dst] += wad;

        Transfer(src, dst, wad);

        return true;
    }

このログでわかること
WBNBトークンコントラクトにて、ルーターからLPトークンへバランスを移行している

3. THCトークンのTransfer

    function _transfer(
        address sender,
        address recipient,
        uint256 amount
    ) internal virtual {
        // EventLogから下記がわかる
        // sender= 0x2fa22acdb65ce9763d927656909f14e4e66b5f08(LPトークン)
        // recipient= 0x5D37fdb31d06D76603a908C69b7c3d05A34D8024(持ち主のアドレス)
        // amount= 200000000000000000000(THCの数量)

        // 0アドレスからの送付、0アドレスへの送付でないことをチェックする
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

       // 前処理を行う
        _beforeTokenTransfer(sender, recipient, amount);

        // LPトークンのバランスをチェックし、スワップする数量をへらす
        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[sender] = senderBalance - amount;
        }

        // 持ち主のアドレスのバランスを増加する
        _balances[recipient] += amount;

        // イベント発行
        emit Transfer(sender, recipient, amount);

    // 後処理
        _afterTokenTransfer(sender, recipient, amount);
    }

このログでわかること
THCトークンコントラクトにて、LPトークンから持ち主のアドレスへバランスを移行している

4. LP(WBNB-THC)トークンのSync

    // update reserves and, on the first call per block, price accumulators
    function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
        // EventLogから下記がわかる
        // reserve0 = 10824887263761187677681967
        // reserve1 = 1292720770708685069662

        // オーバーフロー指定内科を確認
        require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'Pancake: OVERFLOW');
        uint32 blockTimestamp = uint32(block.timestamp % 2**32);
        uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
        if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
            // * never overflows, and + overflow is desired
            price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
            price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
        }

        // 引数で与えられたbalanceをreserveに代入(流動性残量確認の記憶領域にセット)
        reserve0 = uint112(balance0);
        reserve1 = uint112(balance1);
        blockTimestampLast = blockTimestamp;
        emit Sync(reserve0, reserve1);
    }

このログでわかること
LP(WBNB-THC)トークンコントラクトにて、流動性残高の更新を行っている

5. LP(WBNB-THC)トークンのSwap

    // this low-level function should be called from a contract which performs important safety checks
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
        // EventLogから下記がわかる
        // msg.sender : 0x10ED43C718714eb63d5aA57B78B54704E256024E(ルーター)
        // amount0In :0
        // amount1In :23943652015054953
        // amount0Out :200000000000000000000
        // amount1Out :0
        // to : 0x5D37fdb31d06D76603a908C69b7c3d05A34D8024(持ち主のアドレス)

        // OUTPUTの数量が0以上かどうかの確認
        require(amount0Out > 0 || amount1Out > 0, 'Pancake: INSUFFICIENT_OUTPUT_AMOUNT');
        // 流動性のバランスを取得
        (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
        // 数量がバランス以下かどうかを確認
        require(amount0Out < _reserve0 && amount1Out < _reserve1, 'Pancake: INSUFFICIENT_LIQUIDITY');

    // トークンのトランスファー処理
        uint balance0;
        uint balance1;
        { // scope for _token{0,1}, avoids stack too deep errors
        address _token0 = token0;
        address _token1 = token1;
        require(to != _token0 && to != _token1, 'Pancake: INVALID_TO');
        if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
        if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
        if (data.length > 0) IPancakeCallee(to).pancakeCall(msg.sender, amount0Out, amount1Out, data);

        // 交換後の各トークンに対してLPトークンバランスを取得
        balance0 = IERC20(_token0).balanceOf(address(this));
        balance1 = IERC20(_token1).balanceOf(address(this));
        }
        uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
        uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
        require(amount0In > 0 || amount1In > 0, 'Pancake: INSUFFICIENT_INPUT_AMOUNT');
        { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
        uint balance0Adjusted = (balance0.mul(10000).sub(amount0In.mul(25)));
        uint balance1Adjusted = (balance1.mul(10000).sub(amount1In.mul(25)));
        require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(10000**2), 'Pancake: K');
        }

        // 4.のSyncイベントの処理を呼び出し
        _update(balance0, balance1, _reserve0, _reserve1);

        emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
    }

このログでわかること
LP(WBNB-THC)トークンにて、持ち主のアドレスへトークンを交換し、交換後のLPトークンのバランスを取得している

トランザクションログのまとめ

ここまでのログをまとめると、WBNB→THCのスワップでは下記の動きをしていることがわかります。

1. WBNBトークンコントラクトにて、PancakeSwapのルータのバランスを0.0239...増加している。
2. WBNBトークンコントラクトにて、ルーターからLPトークンへバランスを移行している
3. THCトークンコントラクトにて、LPトークンから持ち主のアドレスへバランスを移行している
4. LP(WBNB-THC)トークンコントラクトにて、流動性残高の更新を行っている
5. LP(WBNB-THC)トークンにて、持ち主のアドレスへトークンを交換し、交換後のLPトークンのバランスを取得している

Eventを発行したタイミングでログが出力されるため、処理の順番とは少しちぐはぐになっていることもあります。
ログを見ることで大まかな処理の流れや、どのような値が流れていったのかを確認することができます。

今回は「WBNB→THCのスワップ」を確認しました。きになる処理の流れを実際に見てみるといろんなことがわかると思います。

例えば、THC→WBNBのスワップではどうでしょうか。
また、基軸通貨ではないトークン同士、中継が発生するトークンでのスワップではどうでしょうか?

ついでに、流動性提供(AddLiquidity)のトランザクションも確認してみましょう。

さて、ここまで主なPancakeSwapのDEX機能の構成と、スワップの流れをブロックチェーン側の視点で見てきました。

WEB側からも合わせて確認してみましょう。

PancakeSwapを動かしてみる

構成に書いたとおり、PancakeSwapとはBlockChain上のコントラクトとクラウド上のWEBアプリで作られています。
PancakeSwapを動かすには、その両方の知識が必要になってきます。

PancakeSwapの技術的な構成要素

簡単に、WEB側とBlockChain側の技術要素を記載してみます。

WEB側

  • React/Next.js

BlockChain側

  • Solidity

書いてみるとめちゃくちゃシンプルでしたね。
(それぞれの開発手法を知らないとなかなか難しいところはあるかもしれませんが)

それでは、PancakeSwapのDEX機能についてローカル環境(WEBのみ)を構築してみましょう。
汎用性を持たせるために、Docker環境での構築を行います。

Dockerとは、開発環境を手早く柔軟に作ることができる、開発者御用達のものです。
初めて使う方も、この機会に是非取り入れてみてはいかがでしょうか。
Docker公式

今回構築する構成について

今回構築する構成ですが、以下とします。
- PancakeSwapのWEBアプリをローカルで構築
- PancakeSwapのコントラクトはBSCテストネット上のものを利用
(Nhan Caoさんのデプロイ情報を公開してくれているものを利用することにします)

本当は、コントラクトもクローンをデプロイして使えると良かったのですが、トークン情報や流動性などすべて1から用意しないといけなくなるため、情報が散乱してしまうため今回はすでにデプロイしてあるものを利用することとします。

今回の構成.png

WEB環境構築

PancakeSwapV2では、TestNetの対応をカバーしていないようで、自分で設定を変える必要があります。
(優先度低いとの発言あり)

1. フロントエンドの構築

1. Node.js環境の用意

今回は、DockerにてReact/Next.js用の環境をgithubへ用意しています。

好きな場所
PancakeSwapのフロントエンドを動かす用のDocker環境を用意しています。
必要であれば使ってください。
https://github.com/morimri-pet/pancake-study-dex.git

git clone https://github.com/morimri-pet/pancake-study-dex.git

2. 必要なソースの入手

projectroot/mnt/node

git clone https://github.com/pancakeswap/pancake-frontend.git

3. Docker環境の立ち上げ&環境構築

cd projectroot
docker-compose up -d
docker-compose exec node bash
cd pancake-frontend
yarn install

4. 接続先情報の修正

mnt\node\pancake-frontend.env.development
(今回は、yarn devでローカル開発環境として起動させるためenv.developmentの接続先情報を修正します)

NEXT_PUBLIC_CHAIN_ID = "97"
NEXT_PUBLIC_GTAG = "GTM-PXLD3XW"

# 10+ nodes balanced, US/EU
NEXT_PUBLIC_NODE_1 = "https://data-seed-prebsc-1-s1.binance.org:8545"

# 10+ nodes balanced, US/EU
NEXT_PUBLIC_NODE_2 = "https://data-seed-prebsc-1-s1.binance.org:8545"

# 10+ nodes balanced in each region, global
NEXT_PUBLIC_NODE_3 = "https://data-seed-prebsc-1-s1.binance.org:8545"

# NEXT_PUBLIC_NODE_PRODUCTION = "https://nodes.pancakeswap.com"

5. 必要な情報の書き換え

ROUTERとFACTORYのアドレスをテストネット用に書き換えます。

mnt\node\pancake-frontend\src\config\constants\index.ts

export const ROUTER_ADDRESS = '0x05fF2B0DB69458A0750badebc4f9e13aDd608C7F'

mnt\node\pancake-swap-sdk\dist\constants.d.ts

export declare const FACTORY_ADDRESS = "0xBCfCcbde45cE874adCB698cC183deBcF17952812";
export declare const INIT_CODE_HASH = "0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66";

6. ローカル環境立ち上げ

cd projectroot
docker-compose exec node bash
cd pancake-frontend
yarn dev

下記のログが出て正常に立ち上がれば成功です。

yarn run v1.22.17
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
info  - Loaded env from /src/pancake-frontend/.env.development
info  - Loaded env from /src/pancake-frontend/.env

今回はDEX機能に絞っているのでこれだけでTESTNETに接続されます。
すべての機能を使いたい場合は、更に色々と変更していく必要があります。

2. スワップしてみよう

デフォルトであれば、port3000でPancakeSwapが立ち上がっていると思います。

こちらにアクセスして、通常のPancakeSwapと同様にスワップしてみてください。

WEBの機能あれこれ

1. コントラクト情報の生成はどこでやっている?

PancakeSwapはethers.jsを利用しています。
https://github.com/ethers-io/ethers.js

ethers.jsはweb3.jsと並んで、ブロックチェーンとの通信でよく使われているライブラリです。

実際に呼び出しているところを見てみましょう。
utils/contractHelperでContractを生成しています。

mnt\node\pancake-frontend\src\utils\contractHelpers.ts

const getContract = (abi: any, address: string, signer?: Signer | Provider) => {
  const signerOrProvider = signer ?? simpleRpcProvider
  return new Contract(address, abi, signerOrProvider)
}

2. コントラクトを呼び出しているのはどこ?

実際にコントラクトとの通信をしている例を見てみましょう。
multicallを呼び出している箇所です。

このように、Helperで作成したコントラクトのインスタンス(ethers.jsを利用)に対して、awaitでコントラクトのメソッドを呼び出しています。

基本的にはこのような構成になっています。

ローカル環境を構築しているので
debugger
を差し込んで処理を止めてみたり、
consolo.log()でログを出力したりして色々確認してみてください。

mnt\node\pancake-frontend\src\utils\multicall.ts


const multicall = async <T = any>(abi: any[], calls: Call[]): Promise<T> => {
  const multi = getMulticallContract()
  const itf = new Interface(abi)

  const calldata = calls.map((call) => ({
    target: call.address.toLowerCase(),
    callData: itf.encodeFunctionData(call.name, call.params),
  }))
  const { returnData } = await multi.aggregate(calldata)

  const res = returnData.map((call, i) => itf.decodeFunctionResult(calls[i].name, call))

  return res as any
}

最後に

お疲れ様でした。
ここまでで、PancakeSwapのDEX機能がかなり理解出来てきたのではないでしょうか。

自分自身、この記事を通してDEXへの理解が深まりました。

DEX周りの情報は、なかなか技術的な解説まで踏み込んでいるものが無い(儲け方や使い方のほうが需要がある)のですが、せっかくDEXに触れているなら少しでも開発面にも興味をもっていただけたらいいなぁと思い記事にしてみました。

これを機に、プログラミングちょっとやってみようかなと言う方が増えたら幸いです!

PancakeSwapその他あれこれ

書きたかったんですが紙面の都合上()かけなかったやつです。
スワップ一つとっても色々とあるんですよねー。

だれか解説を書いてくれたらぜひ教えて下さい!

PancakeRouteでのSwapFunctions各種

  • swapExactTokensForTokens
  • swapTokensForExactTokens
  • swapExactETHForTokens
  • swapTokensForExactETH
  • swapExactTokensForETH
  • swapETHForExactTokens
  • swapExactTokensForTokensSupportingFeeOnTransferTokens
  • swapExactETHForTokensSupportingFeeOnTransferTokens
  • swapExactTokensForETHSupportingFeeOnTransferTokens
15
7
2

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
15
7