<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>narvik</title>
        <link>https://paragraph.com/@narvik</link>
        <description>undefined</description>
        <lastBuildDate>Fri, 19 Jun 2026 11:29:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>narvik</title>
            <url>https://storage.googleapis.com/papyrus_images/51b1d3c4d0aa4c09f9ea9eea14e820e55177423f0071de7ec9bac7ac73892089.jpg</url>
            <link>https://paragraph.com/@narvik</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[PancakeSwapの中身を読む]]></title>
            <link>https://paragraph.com/@narvik/pancakeswap</link>
            <guid>pgPWX7YtCMoh2bytFFkF</guid>
            <pubDate>Fri, 11 Feb 2022 04:33:34 GMT</pubDate>
            <description><![CDATA[※ 2021年7月19日にnoteで投稿した記事の移植です。はじめにこの記事ではPancakeSwapのコントラクトがswap時にどのような挙動をするかを記載しています。具体的には基本的なswapの種類の説明と、swapExactTokensForTokensに対して内部実装の解説です。誤っている点があったらコメントを頂けると嬉しいです。 またPancakeSwapはUniswap v2のフォークなので事実上Uniswap v2の説明となります。PancakeSwapのドキュメントにもUniswap v2のドキュメントを読めと記載されているので記事中ではUniswap v2のドキュメントを参照します。前提知識swapはざっくりと以下のようなシーケンスで行われます。swap前にトークンにapproveが必要な理由はswap時にPancakeRouterが対象トークンのコントラクトに対してtransferFromというメソッドを利用してトークンを移動させますが、この際にapproveで指定された量までしか移動できないためです。 approveは小切手を発行するメソッドです。指定したア...]]></description>
            <content:encoded><![CDATA[<p>※ 2021年7月19日にnoteで投稿した記事の移植です。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">はじめに</h2><p>この記事ではPancakeSwapのコントラクトがswap時にどのような挙動をするかを記載しています。具体的には基本的なswapの種類の説明と、swapExactTokensForTokensに対して内部実装の解説です。誤っている点があったらコメントを頂けると嬉しいです。</p><p>またPancakeSwapはUniswap v2のフォークなので事実上Uniswap v2の説明となります。PancakeSwapのドキュメントにもUniswap v2のドキュメントを読めと記載されているので記事中ではUniswap v2のドキュメントを参照します。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">前提知識</h2><p>swapはざっくりと以下のようなシーケンスで行われます。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/2006b0d15b145d6880f4af74ac026322b977a6ff3bdbb9bc55a84805817b2990.png" alt="" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>swap前にトークンにapproveが必要な理由はswap時にPancakeRouterが対象トークンのコントラクトに対してtransferFromというメソッドを利用してトークンを移動させますが、この際にapproveで指定された量までしか移動できないためです。</p><p>approveは小切手を発行するメソッドです。指定したアドレスに対し、approveで指定した量までtransferFrom経由で移動させることを許可します。例えばapproveで数量: 100を指定した場合、対象者が100の数量をtransferFromで移動させると対象者はそれ以上transferFromで移動させることができません。利便性の観点からrouterにapproveするときはかなり大きな数値を指定するようになっているため、小切手が尽きないようになっています。</p><h2 id="h-swap" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Swapの種類</h2><p>Uniswapのswap系メソッドは以下にまとまっています。</p><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://docs.uniswap.org/protocol/V2/reference/smart-contracts/router-02">https://docs.uniswap.org/protocol/V2/reference/smart-contracts/router-02</a></p><p>メソッドはswap{A}For{B}の命名規則になっています。Aを入力トークン、Bを出力トークンを呼びます。またETHは基軸通貨と呼びます。</p><p>まずExactの説明です。ExactがAの前にある場合は出力トークンの最小量を指定します。Bの前にある場合は入力トークンの最大量を指定します。UIから指定する際に入力トークンの量を指定して購入する場合はswapExact{A}For{B}となり、出力トークンの量を指定して購入する場合はswap{A}ForExact{B}となります。UIでは以下のように変化します。</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/8c7dc1c3d226996d4f3510b5a8dd4d0a3196be185a28e16afa5bede70e7fa7d1.png" alt="Fromに入力した場合はswapExact{A}For{B}となり最小出力量が表示される" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Fromに入力した場合はswapExact{A}For{B}となり最小出力量が表示される</figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://storage.googleapis.com/papyrus_images/a4a9978d5fbd9223c7207b60fa97ae212f7a77351def311faa17818d186fef4f.png" alt="Toに入力した場合はswap{A}ForExact{B}となり最大入力量が表示される" blurdataurl="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=" nextheight="600" nextwidth="800" class="image-node embed"><figcaption HTMLAttributes="[object Object]" class="">Toに入力した場合はswap{A}ForExact{B}となり最大入力量が表示される</figcaption></figure><p>Toに入力した場合はswap{A}ForExact{B}となり最大入力量が表示される</p><p>slippageを変化させるとMinimum received, Maximum soldの値が変化します。また、transfer taxがかかるトークンとswap{A}ForExact{B}は相性が悪いです。To側の数量を指定して売買をするためには自分でtransfer taxを加味した数値にする必要があるためです。</p><p>次にETHの説明です。swap{A}For{B}のAにETHが来るパターンとBにETHが来るパターンがあります。これは入力トークンに基軸通貨を使うか、出力に基軸通貨を使う場合に利用されます。基軸通貨は他のトークンを扱いが違うためメソッドも分かれています。端的に他のトークンとの違いを言うと基軸通貨にはアドレスが存在しません。</p><p>基軸通貨をWrapし、WETH、WBNBなどにすることで他のトークンと同様の扱いが行えるようになります。swapETHFor{B}の場合は最初のRouteがWrapped Tokenとなり、swap{A}ForETHの場合は最後のRouteがWrapped Tokenとなります。</p><p>最後にswap{A}For{B}SupportingFeeOnTransferTokensについてです。これは転送料がかかるトークンに対して利用します。前提知識で述べた通り、swapするためには一度PancakePairのLPアドレスにトークンをtransferする必要があります。transfer taxがかかるトークンの場合、PancakePairのLPアドレスにトークンが到着した時点でFromに指定した値より小さくなっているケースが考えられます。それを考慮したswapが行えるメソッドはSupportingFeeOnTransferTokensのsuffixがついています。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">内部挙動</h2><p>ここからが本題です。swapExactTokensForTokensのコードは以下のようになっています。</p><pre data-type="codeBlock" text="function swapExactTokensForTokens(
   uint amountIn,
   uint amountOutMin,
   address[] calldata path,
   address to,
   uint deadline
) external virtual override ensure(deadline) returns (uint[] memory amounts) {
   amounts = PancakeLibrary.getAmountsOut(factory, amountIn, path);
   require(amounts[amounts.length - 1] &gt;= amountOutMin, &apos;PancakeRouter: INSUFFICIENT_OUTPUT_AMOUNT&apos;);
   TransferHelper.safeTransferFrom(
       path[0], msg.sender, PancakeLibrary.pairFor(factory, path[0], path[1]), amounts[0]
   );
   _swap(amounts, path, to);
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">swapExactTokensForTokens</span>(<span class="hljs-params">
   <span class="hljs-keyword">uint</span> amountIn,
   <span class="hljs-keyword">uint</span> amountOutMin,
   <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">calldata</span> path,
   <span class="hljs-keyword">address</span> to,
   <span class="hljs-keyword">uint</span> deadline
</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> <span class="hljs-title"><span class="hljs-keyword">override</span></span> <span class="hljs-title">ensure</span>(<span class="hljs-params">deadline</span>) <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint</span>[] <span class="hljs-keyword">memory</span> amounts</span>) </span>{
   amounts <span class="hljs-operator">=</span> PancakeLibrary.getAmountsOut(factory, amountIn, path);
   <span class="hljs-built_in">require</span>(amounts[amounts.<span class="hljs-built_in">length</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>] <span class="hljs-operator">></span><span class="hljs-operator">=</span> amountOutMin, <span class="hljs-string">'PancakeRouter: INSUFFICIENT_OUTPUT_AMOUNT'</span>);
   TransferHelper.safeTransferFrom(
       path[<span class="hljs-number">0</span>], <span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, PancakeLibrary.pairFor(factory, path[<span class="hljs-number">0</span>], path[<span class="hljs-number">1</span>]), amounts[<span class="hljs-number">0</span>]
   );
   _swap(amounts, path, to);
}
</code></pre><p>メソッドの引数はamountIn, amountOutMin, path, to, deadlineの5つです。それぞれ以下のような意味です。</p><ul><li><p>amountIn: 入力トークンの数量</p></li><li><p>amountOutMin: 出力トークンの最低量(slippage考慮後の値)</p></li><li><p>path: トークンをswapするためのroute</p></li><li><p>to: 交換した後にtransferする対象、基本的に自分のアドレスが入る</p></li><li><p>deadline: 指定された時刻を過ぎた場合はswapを中止する</p></li></ul><p>pathについて説明すると、トークンをswapするためにどのLP(流動性)を使ってswapするかを指定します。例えばBUSDをCAKEにswapしたい場合、</p><ul><li><p>BUSD-CAKEのLPだけ存在する</p><ul><li><p>BUSDアドレス,CAKEアドレスがrouteになる</p></li></ul></li><li><p>USDT-BUSD、USDT-CAKEのLPだけ存在する</p><ul><li><p>BUSDアドレス,USDTアドレス,CAKEアドレスがrouteになる</p></li><li><p>BUSDアドレス,CAKEアドレスを指定してもLPのrouteがないのでNG</p></li></ul></li></ul><p>といった形で存在するLPを元にrouteを組み立てる必要があります。PancakeSwapはUI側でrouteを特定のトークンに制限して総当りで検証し、最も良いレートのrouteが選ばれるようになっています。反面、試行ルートが多いのでレートの反映が遅いケースもあります。</p><p>最初に PancakeLibrary.getAmountsOut(factory, amountIn, path) を行っています。これは一言で言うと指定されたroute, amountInで取得できるトークン量を計算しています。実際の処理を見てみます。</p><pre data-type="codeBlock" text="function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
   require(path.length &gt;= 2, &apos;PancakeLibrary: INVALID_PATH&apos;);
   amounts = new uintUnsupported embed;
   amounts[0] = amountIn;
   for (uint i; i &lt; path.length - 1; i++) {
       (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
       amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
   }
}
"><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAmountsOut</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> factory, <span class="hljs-keyword">uint</span> amountIn, <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">memory</span> path</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint</span>[] <span class="hljs-keyword">memory</span> amounts</span>) </span>{
   <span class="hljs-built_in">require</span>(path.<span class="hljs-built_in">length</span> <span class="hljs-operator">></span><span class="hljs-operator">=</span> <span class="hljs-number">2</span>, <span class="hljs-string">'PancakeLibrary: INVALID_PATH'</span>);
   amounts <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> uintUnsupported embed;
   amounts[<span class="hljs-number">0</span>] <span class="hljs-operator">=</span> amountIn;
   <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint</span> i; i <span class="hljs-operator">&#x3C;</span> path.<span class="hljs-built_in">length</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
       (<span class="hljs-keyword">uint</span> reserveIn, <span class="hljs-keyword">uint</span> reserveOut) <span class="hljs-operator">=</span> getReserves(factory, path[i], path[i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>]);
       amounts[i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>] <span class="hljs-operator">=</span> getAmountOut(amounts[i], reserveIn, reserveOut);
   }
}
</code></pre><p>for文を利用して繰り返しgetReservesとgetAmountOutを呼び出しています。例えばrouteにBUSDアドレス,USDTアドレス,CAKEアドレスを指定した場合、</p><ol><li><p>BUSDアドレス,USDTアドレスでgetReservesを呼ぶ</p></li><li><p>得られたreserveIn, reserveOutを使ってBUSD-&gt;USDTへのswapで得られる数量をgetAmountOutで取得</p></li><li><p>USDTアドレス,CAKEアドレスでgetReservesを呼ぶ</p></li><li><p>得られたreserveIn, reserveOutを使ってUSDT-&gt;CAKEへのswapで得られる数量をgetAmountOutで取得</p></li><li><p>それぞれのpathでswapした結果を返す</p></li></ol><p>という挙動になります。getReservesとgetAmountOutの処理を見てみます。</p><pre data-type="codeBlock" text="// Router側
function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
   (address token0,) = sortTokens(tokenA, tokenB);
   pairFor(factory, tokenA, tokenB);
   (uint reserve0, uint reserve1,) = IPancakePair(pairFor(factory, tokenA, tokenB)).getReserves();
   (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
   
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
   require(amountIn &gt; 0, &apos;PancakeLibrary: INSUFFICIENT_INPUT_AMOUNT&apos;);
   require(reserveIn &gt; 0 &amp;&amp; reserveOut &gt; 0, &apos;PancakeLibrary: INSUFFICIENT_LIQUIDITY&apos;);
   uint amountInWithFee = amountIn.mul(9975);
   uint numerator = amountInWithFee.mul(reserveOut);
   uint denominator = reserveIn.mul(10000).add(amountInWithFee);
   amountOut = numerator / denominator;
}

// Factory側(PancakePair)
function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
   _reserve0 = reserve0;
   _reserve1 = reserve1;
   _blockTimestampLast = blockTimestampLast;
}
"><code><span class="hljs-comment">// Router側</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getReserves</span>(<span class="hljs-params"><span class="hljs-keyword">address</span> factory, <span class="hljs-keyword">address</span> tokenA, <span class="hljs-keyword">address</span> tokenB</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint</span> reserveA, <span class="hljs-keyword">uint</span> reserveB</span>) </span>{
   (<span class="hljs-keyword">address</span> token0,) <span class="hljs-operator">=</span> sortTokens(tokenA, tokenB);
   pairFor(factory, tokenA, tokenB);
   (<span class="hljs-keyword">uint</span> reserve0, <span class="hljs-keyword">uint</span> reserve1,) <span class="hljs-operator">=</span> IPancakePair(pairFor(factory, tokenA, tokenB)).getReserves();
   (reserveA, reserveB) <span class="hljs-operator">=</span> tokenA <span class="hljs-operator">=</span><span class="hljs-operator">=</span> token0 ? (reserve0, reserve1) : (reserve1, reserve0);
}
   
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getAmountOut</span>(<span class="hljs-params"><span class="hljs-keyword">uint</span> amountIn, <span class="hljs-keyword">uint</span> reserveIn, <span class="hljs-keyword">uint</span> reserveOut</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">pure</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint</span> amountOut</span>) </span>{
   <span class="hljs-built_in">require</span>(amountIn <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">'PancakeLibrary: INSUFFICIENT_INPUT_AMOUNT'</span>);
   <span class="hljs-built_in">require</span>(reserveIn <span class="hljs-operator">></span> <span class="hljs-number">0</span> <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> reserveOut <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">'PancakeLibrary: INSUFFICIENT_LIQUIDITY'</span>);
   <span class="hljs-keyword">uint</span> amountInWithFee <span class="hljs-operator">=</span> amountIn.mul(<span class="hljs-number">9975</span>);
   <span class="hljs-keyword">uint</span> numerator <span class="hljs-operator">=</span> amountInWithFee.mul(reserveOut);
   <span class="hljs-keyword">uint</span> denominator <span class="hljs-operator">=</span> reserveIn.mul(<span class="hljs-number">10000</span>).add(amountInWithFee);
   amountOut <span class="hljs-operator">=</span> numerator <span class="hljs-operator">/</span> denominator;
}

<span class="hljs-comment">// Factory側(PancakePair)</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getReserves</span>(<span class="hljs-params"></span>) <span class="hljs-title"><span class="hljs-keyword">public</span></span> <span class="hljs-title"><span class="hljs-keyword">view</span></span> <span class="hljs-title"><span class="hljs-keyword">returns</span></span> (<span class="hljs-params"><span class="hljs-keyword">uint112</span> _reserve0, <span class="hljs-keyword">uint112</span> _reserve1, <span class="hljs-keyword">uint32</span> _blockTimestampLast</span>) </span>{
   _reserve0 <span class="hljs-operator">=</span> reserve0;
   _reserve1 <span class="hljs-operator">=</span> reserve1;
   _blockTimestampLast <span class="hljs-operator">=</span> blockTimestampLast;
}
</code></pre><p>まずRouter側ではgetReserves内でトークンA,トークンBのLPアドレスを計算し、LPアドレス内のトークン量を取得します。その後getAmountOutでは流動性の状態とamountInの値を使ってamountOutを算出します。uint amountInWithFee = amountIn.mul(9975) と記述されている部分はPancakeSwapの取引手数料です。ドキュメントに記載されている通り、0.25%がtrading feeとして徴収されます。</p><p>上記の計算をした結果、得られるトークン量が指定されたamountOutMinを下回る場合、その時点でswapが失敗します。上回った場合はTransferHelper.safeTransferFromを利用し、Fromに指定されたトークン量をLPアドレスに移動し、_swapを行います。</p><pre data-type="codeBlock" text="// Router側
function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {
   for (uint i; i &lt; path.length - 1; i++) {
       (address input, address output) = (path[i], path[i + 1]);
       (address token0,) = PancakeLibrary.sortTokens(input, output);
       uint amountOut = amounts[i + 1];
       (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
       address to = i &lt; path.length - 2 ? PancakeLibrary.pairFor(factory, output, path[i + 2]) : _to;
       IPancakePair(PancakeLibrary.pairFor(factory, input, output)).swap(
           amount0Out, amount1Out, to, new bytes(0)
       );
   }
}

// Factory側(PancakePair)
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
   require(amount0Out &gt; 0 || amount1Out &gt; 0, &apos;Pancake: INSUFFICIENT_OUTPUT_AMOUNT&apos;);
   (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
   require(amount0Out &lt; _reserve0 &amp;&amp; amount1Out &lt; _reserve1, &apos;Pancake: INSUFFICIENT_LIQUIDITY&apos;);

   uint balance0;
   uint balance1;
   { // scope for _token{0,1}, avoids stack too deep errors
   address _token0 = token0;
   address _token1 = token1;
   require(to != _token0 &amp;&amp; to != _token1, &apos;Pancake: INVALID_TO&apos;);
   if (amount0Out &gt; 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
   if (amount1Out &gt; 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
   if (data.length &gt; 0) IPancakeCallee(to).pancakeCall(msg.sender, amount0Out, amount1Out, data);
   balance0 = IERC20(_token0).balanceOf(address(this));
   balance1 = IERC20(_token1).balanceOf(address(this));
   }
   uint amount0In = balance0 &gt; _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
   uint amount1In = balance1 &gt; _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
   require(amount0In &gt; 0 || amount1In &gt; 0, &apos;Pancake: INSUFFICIENT_INPUT_AMOUNT&apos;);
   { // 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) &gt;= uint(_reserve0).mul(_reserve1).mul(10000**2), &apos;Pancake: K&apos;);
   }

   _update(balance0, balance1, _reserve0, _reserve1);
   emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}
"><code><span class="hljs-comment">// Router側</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_swap</span>(<span class="hljs-params"><span class="hljs-keyword">uint</span>[] <span class="hljs-keyword">memory</span> amounts, <span class="hljs-keyword">address</span>[] <span class="hljs-keyword">memory</span> path, <span class="hljs-keyword">address</span> _to</span>) <span class="hljs-title"><span class="hljs-keyword">internal</span></span> <span class="hljs-title"><span class="hljs-keyword">virtual</span></span> </span>{
   <span class="hljs-keyword">for</span> (<span class="hljs-keyword">uint</span> i; i <span class="hljs-operator">&#x3C;</span> path.<span class="hljs-built_in">length</span> <span class="hljs-operator">-</span> <span class="hljs-number">1</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
       (<span class="hljs-keyword">address</span> input, <span class="hljs-keyword">address</span> output) <span class="hljs-operator">=</span> (path[i], path[i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>]);
       (<span class="hljs-keyword">address</span> token0,) <span class="hljs-operator">=</span> PancakeLibrary.sortTokens(input, output);
       <span class="hljs-keyword">uint</span> amountOut <span class="hljs-operator">=</span> amounts[i <span class="hljs-operator">+</span> <span class="hljs-number">1</span>];
       (<span class="hljs-keyword">uint</span> amount0Out, <span class="hljs-keyword">uint</span> amount1Out) <span class="hljs-operator">=</span> input <span class="hljs-operator">=</span><span class="hljs-operator">=</span> token0 ? (<span class="hljs-keyword">uint</span>(<span class="hljs-number">0</span>), amountOut) : (amountOut, <span class="hljs-keyword">uint</span>(<span class="hljs-number">0</span>));
       <span class="hljs-keyword">address</span> to <span class="hljs-operator">=</span> i <span class="hljs-operator">&#x3C;</span> path.<span class="hljs-built_in">length</span> <span class="hljs-operator">-</span> <span class="hljs-number">2</span> ? PancakeLibrary.pairFor(factory, output, path[i <span class="hljs-operator">+</span> <span class="hljs-number">2</span>]) : _to;
       IPancakePair(PancakeLibrary.pairFor(factory, input, output)).swap(
           amount0Out, amount1Out, to, <span class="hljs-keyword">new</span> <span class="hljs-keyword">bytes</span>(<span class="hljs-number">0</span>)
       );
   }
}

<span class="hljs-comment">// Factory側(PancakePair)</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">swap</span>(<span class="hljs-params"><span class="hljs-keyword">uint</span> amount0Out, <span class="hljs-keyword">uint</span> amount1Out, <span class="hljs-keyword">address</span> to, <span class="hljs-keyword">bytes</span> <span class="hljs-keyword">calldata</span> data</span>) <span class="hljs-title"><span class="hljs-keyword">external</span></span> <span class="hljs-title">lock</span> </span>{
   <span class="hljs-built_in">require</span>(amount0Out <span class="hljs-operator">></span> <span class="hljs-number">0</span> <span class="hljs-operator">|</span><span class="hljs-operator">|</span> amount1Out <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">'Pancake: INSUFFICIENT_OUTPUT_AMOUNT'</span>);
   (<span class="hljs-keyword">uint112</span> _reserve0, <span class="hljs-keyword">uint112</span> _reserve1,) <span class="hljs-operator">=</span> getReserves(); <span class="hljs-comment">// gas savings</span>
   <span class="hljs-built_in">require</span>(amount0Out <span class="hljs-operator">&#x3C;</span> _reserve0 <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> amount1Out <span class="hljs-operator">&#x3C;</span> _reserve1, <span class="hljs-string">'Pancake: INSUFFICIENT_LIQUIDITY'</span>);

   <span class="hljs-keyword">uint</span> balance0;
   <span class="hljs-keyword">uint</span> balance1;
   { <span class="hljs-comment">// scope for _token{0,1}, avoids stack too deep errors</span>
   <span class="hljs-keyword">address</span> _token0 <span class="hljs-operator">=</span> token0;
   <span class="hljs-keyword">address</span> _token1 <span class="hljs-operator">=</span> token1;
   <span class="hljs-built_in">require</span>(to <span class="hljs-operator">!</span><span class="hljs-operator">=</span> _token0 <span class="hljs-operator">&#x26;</span><span class="hljs-operator">&#x26;</span> to <span class="hljs-operator">!</span><span class="hljs-operator">=</span> _token1, <span class="hljs-string">'Pancake: INVALID_TO'</span>);
   <span class="hljs-keyword">if</span> (amount0Out <span class="hljs-operator">></span> <span class="hljs-number">0</span>) _safeTransfer(_token0, to, amount0Out); <span class="hljs-comment">// optimistically transfer tokens</span>
   <span class="hljs-keyword">if</span> (amount1Out <span class="hljs-operator">></span> <span class="hljs-number">0</span>) _safeTransfer(_token1, to, amount1Out); <span class="hljs-comment">// optimistically transfer tokens</span>
   <span class="hljs-keyword">if</span> (data.<span class="hljs-built_in">length</span> <span class="hljs-operator">></span> <span class="hljs-number">0</span>) IPancakeCallee(to).pancakeCall(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount0Out, amount1Out, data);
   balance0 <span class="hljs-operator">=</span> IERC20(_token0).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
   balance1 <span class="hljs-operator">=</span> IERC20(_token1).balanceOf(<span class="hljs-keyword">address</span>(<span class="hljs-built_in">this</span>));
   }
   <span class="hljs-keyword">uint</span> amount0In <span class="hljs-operator">=</span> balance0 <span class="hljs-operator">></span> _reserve0 <span class="hljs-operator">-</span> amount0Out ? balance0 <span class="hljs-operator">-</span> (_reserve0 <span class="hljs-operator">-</span> amount0Out) : <span class="hljs-number">0</span>;
   <span class="hljs-keyword">uint</span> amount1In <span class="hljs-operator">=</span> balance1 <span class="hljs-operator">></span> _reserve1 <span class="hljs-operator">-</span> amount1Out ? balance1 <span class="hljs-operator">-</span> (_reserve1 <span class="hljs-operator">-</span> amount1Out) : <span class="hljs-number">0</span>;
   <span class="hljs-built_in">require</span>(amount0In <span class="hljs-operator">></span> <span class="hljs-number">0</span> <span class="hljs-operator">|</span><span class="hljs-operator">|</span> amount1In <span class="hljs-operator">></span> <span class="hljs-number">0</span>, <span class="hljs-string">'Pancake: INSUFFICIENT_INPUT_AMOUNT'</span>);
   { <span class="hljs-comment">// scope for reserve{0,1}Adjusted, avoids stack too deep errors</span>
   <span class="hljs-keyword">uint</span> balance0Adjusted <span class="hljs-operator">=</span> (balance0.mul(<span class="hljs-number">10000</span>).sub(amount0In.mul(<span class="hljs-number">25</span>)));
   <span class="hljs-keyword">uint</span> balance1Adjusted <span class="hljs-operator">=</span> (balance1.mul(<span class="hljs-number">10000</span>).sub(amount1In.mul(<span class="hljs-number">25</span>)));
   <span class="hljs-built_in">require</span>(balance0Adjusted.mul(balance1Adjusted) <span class="hljs-operator">></span><span class="hljs-operator">=</span> <span class="hljs-keyword">uint</span>(_reserve0).mul(_reserve1).mul(<span class="hljs-number">10000</span><span class="hljs-operator">*</span><span class="hljs-operator">*</span><span class="hljs-number">2</span>), <span class="hljs-string">'Pancake: K'</span>);
   }

   _update(balance0, balance1, _reserve0, _reserve1);
   <span class="hljs-keyword">emit</span> Swap(<span class="hljs-built_in">msg</span>.<span class="hljs-built_in">sender</span>, amount0In, amount1In, amount0Out, amount1Out, to);
}
</code></pre><p>swapではLPのトークン保有量に不整合が発生しないようにしたり、FlashSwapの補助を行ったりしています。Router側で_swap(..., new bytes(0))という記述がありますが、何らかの値を入力するとFlashSwap扱いになります。これらの処理をpathごとに行うことでswapが完了します。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">まとめ</h2><ul><li><p>swapには入力トークン、出力トークンに応じていろいろなメソッドが使い分けられている</p></li><li><p>swap時にはrouteを辿り、複数回のswapが行われている(route毎にtrading feeがかかる)</p></li></ul><p>シンプルなUIの裏でいろいろなことをやっていますね。 ユーザーに複雑さを感じさせない凄い仕組みだなぁと感じました。</p><h2 id="h-" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">コントラクトリンク</h2><ul><li><p>PancakeRouter</p></li><li><p>PancakeFactory</p></li></ul>]]></content:encoded>
            <author>narvik@newsletter.paragraph.com (narvik)</author>
        </item>
    </channel>
</rss>