# [Bunzz Decipher] UniswapV3のSwapRouter02コントラクトを理解しよう！

> SwapRouter02コントラクトは、Uniswap分散型取引プロトコルの重要なコンポーネントです。このコントラクトは、Uniswapプラットフォーム上でトークンを交換するためのルーターとして機能します。

- URL: https://cardene.dev/articles/989121595dda7c
- 公開日: 2023-08-27
- 更新日: 2023-08-27
- トピック: Blockchain
- タグ: solidity, ethereum, smartcontract, defi

# はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回はBunzzの新機能『**DeCipher**』を使用して、**UniswapV3**の「**SwapRouter02**」のコントラクトを見てみようと思います。

『**DeCipher**』はAIを使用してコントラクトのドキュメントを自動生成してくれるサービスです。

https://www.bunzz.dev/decipher

詳しい使い方に関しては以下の記事を参考にしてください！

https://zenn.dev/heku/articles/33266f0c19d523

:::message
この記事はあくまで個人が「**DeCipher**」を使用してみての記事になります。
間違った部分などもあるかと思いますが、その際はコメントなどで教えてください。
:::

今回使用する『**DeCipher**』のリンクは以下になります。

https://app.bunzz.dev/decipher/chains/1/addresses/0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45

Etherscanのリンクは以下になります。

https://etherscan.io/address/0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45

# 概要

**SwapRouter02コントラクト**は、**Uniswap**分散型取引プロトコルの重要なコンポーネントです。
このコントラクトは、**Uniswap**プラットフォーム上でトークンを交換するためのルーターとして機能します。

**SwapRouter02**コントラクトの主な目的は、効率的で安全なトークンの交換をユーザー間で行うことです。
このコントラクトは、トレーダーが最小のスリッページで、最良の利用可能価格でトークンスワップを実行するためのシンプルで使いやすいインターフェースを提供します。

:::message
**SwapRouter02**コントラクトは、**Uniswap**という分散型取引プラットフォームにおいて、トークン同士の交換をスムーズかつ安全に行うための仕組みです。
このコントラクトは、ユーザーがトークンを交換する際に発生するスリッページ（価格のずれ）を最小限に抑え、最も良い取引価格でトークンをスワップできるようにサポートします。

例えば、あなたがトークンAを持っていて、それをトークンBに交換したいと考えた場合、**SwapRouter02**コントラクトはこの交換を円滑に実現します。
このコントラクトとやり取りすることで、トークンAからトークンBへの最適な取引経路を計算し、取引を実行することができます。

このコントラクトは、ユーザーにとって使いやすい方法で取引を行うことを重視しています。
つまり、複雑な計算や手続きを気にすることなく、シンプルな手順でトークンスワップを行うことができるようになっています。

また、**SwapRouter02**コントラクトはセキュリティ面でも配慮されています。
不正な操作や価格操作を防ぐための仕組みが組み込まれており、ユーザーが安全に取引を行えるようになっています。
:::

**SwapRouter02**コントラクトは、以下の重要な役割を担っています。

### トークンのスワップ
このコントラクトは、ユーザーが**Uniswap**の流動性プールと連携して、トークンを別のトークンと交換できるようにします。
最適なスワップ経路を計算し、ユーザーの代わりに取引を実行します。

### 流動性の提供
このコントラクトは、ユーザーがトークンを預け入れることで**Uniswap**プールに流動性を提供できるようにします。
これにより、トークンのスワップに十分な流動性が確保されます。

:::message
**SwapRouter02**コントラクトは、ユーザーが**Uniswap**の流動性プールに参加して、トークンの流動性を提供する仕組みを提供します。
この流動性プールは、トークンを交換する際に必要なトークン量を提供するためのものであり、トークンスワップが円滑に行われるために重要な要素です。

具体的な例を考えてみましょう。
あなたがトークンAとトークンBの流動性プールにトークンを預け入れると仮定します。
これにより、そのプール内でトークンAとトークンBの供給量が増え、そのプールの総トークン量が増加します。

トークンの供給量が増えることで、ユーザーがトークンAをトークンBに交換しようとする際に、より多くのトークンが利用可能となります。
これにより、スワップ時の価格変動が少なくなり、スリッページ（価格のずれ）が低減されます。
結果として、トークンスワップを行うユーザーにとって、より良い取引価格でスワップが可能となります。

要するに、流動性プールへのトークンの提供は、トークンスワップの効率性を高めるための重要な手段です。ユーザーがトークンを流動性プールに預けることで、プラットフォーム全体の流動性が向上し、ユーザー同士のトークン交換が円滑かつ有利な条件で行えるようになるのです。
:::

### 価格の計算
このコントラクトは、**Uniswap**プラットフォーム上のトークンの正確で最新の価格情報を提供します。
これにより、トレードを実行する際にユーザーが価格情報を元に判断ができます。

:::message
**SwapRouter02**コントラクトは、**Uniswap**プラットフォーム上で取引されているトークンの価格情報を提供する役割を果たしています。
これにより、ユーザーはトークンをスワップする際に、正確で最新の価格情報を手に入れて取引を行うことができます。

例えば、あるトークンAをトークンBに交換する場合を考えてみましょう。
ユーザーは**SwapRouter02**コントラクトを介してトークンの価格情報を取得し、トークンAとトークンBの現在の交換レートを確認できます。
これにより、ユーザーは実際の取引価格を把握し、どれくらいのトークンBがトークンAと交換されるかを理解することができます。

価格情報が正確で最新であることは、ユーザーがトークンスワップを行う際に重要です。
正確な価格情報が提供されることで、ユーザーは自分のトレード戦略を立てることができ、最良のタイミングで取引を実行できます。
また、価格の急激な変動や不正な操作などにも対応できます。

**SwapRouter02**コントラクトが提供する価格情報は、ユーザーが合理的な判断を下し、トークンスワップを行う際の信頼性と安全性を高める役割を果たしています。
:::

### セキュリティと効率性
**SwapRouter02**コントラクトは、トークンのスワップのセキュリティと効率性を確保するよう設計されています。
フロントランニングなどの操作を防ぐためのさまざまなメカニズムを組み込み、スリッページを最小化し流動性を最大化するために取引の経路を最適化します。

:::message
**SwapRouter02**コントラクトは、トークンスワップの際にセキュリティと効率性を確保するために様々な対策が講じられています。

まず、セキュリティ面では、フロントランニングや価格操作などの不正な操作を防ぐための仕組みが組み込まれています。
フロントランニングは、他の人よりも先に取引を行って利益を得ようとする行為を指します。**SwapRouter02**コントラクトは、このような不正な行為を防ぐために、トランザクションの順序を管理したり、適切な手数料を支払うことで優先度を設定するなどの方法を採用しています。

効率性に関しては、スリッページ（価格のずれ）を最小限に抑え、流動性を最大化するために取引の経路を最適化することが重要です。
**SwapRouter02**コントラクトは、複数の流動性プールやトークン経路の中から最適なものを選択し、最小のスリッページでトークンスワップが行えるようにします。
これにより、ユーザーが最良の取引価格でトークンを交換できる確率が高まります。

要するに、**SwapRouter02**コントラクトはセキュリティと効率性を両立させるために、不正行為を防ぐメカニズムを備え、トークンスワップの際に最適な経路を選択してスリッページを最小限に抑える役割を果たしています。
これにより、ユーザーは安全かつ効率的なトークンスワップを実現できるのです。
:::

以上のように、**SwapRouter02**コントラクトは**Uniswap**プラットフォーム上でスムーズで安全なトークンスワップを可能にする重要な役割を果たしています。
ユーザーに信頼性のある効率的な方法を提供し、同時に分散型取引エコシステムの誠実さと安定性を確保しています。

# 使い方

**SwapRouter02**コントラクトは、**Uniswap**の分散型取引所においてトークンのスワップを行うためのスマートコントラクトです。
このコントラクトは**ISwapRouter02**インターフェースの実装であり、トークンのスワップに関するさまざまな機能を提供します。

## コントラクトの目的
**SwapRouter02**コントラクトの目的は、ユーザーが**Uniswap**の分散型取引所上でトークンをスワップできるようにすることです。
**Uniswap**の流動性プールを利用して、トークンスワップを便利かつ効率的に実行する手段を提供します。

## 手順
Uniswap上でトークンをスワップするためには、以下の手順が必要です。

1. **SwapRouter02**コントラクトをデプロイします。
2. コントラクトに対してトークンの使用権限を許可します。
3. `exactInput`または`exactOutput`関数を呼び出して、トークンスワップを実行します。

これにより、ユーザーは簡単にトークンスワップを行うことができ、**Uniswap**の流動性プールを活用してスムーズなトークンの交換が可能となります。

## 関数
**SwapRouter02**コントラクトは、以下のトークンスワップに関する関数を提供しています。

### exactInput
入力量を指定してトークンスワップを実行します。
最小の出力量も指定します。
指定した入力量に対して、それに対応する最小の出力量を保証してトークンスワップを実行します。

### exactOutput
出力量を指定してトークンスワップを実行します。
最大の入力量も指定します。
指定した出力量を達成するために必要な最大の入力量を計算し、それを使用してトークンスワップを行います。

### refundETH
コントラクトに送信された余剰の**ETH**を返金します。
トークンスワップの際に不要な**ETH**が送信された場合に、それを返金するための関数です。

### WETH9
**WETH9**トークンコントラクトのアドレスを返します。
この関数を使用して、**WETH9（Wrapped Ether）** トークンのアドレスを取得することができます。

## 使い方

正確な入力量でのスワップの実行。

1. **SwapRouter02**コントラクトに、指定した入力トークン量を使用する許可を与えます。
2. `exactInput`関数を呼び出し、以下の引数を指定します。
   - `amountIn`
	   - 正確な入力トークン量。
   - `amountOutMin`
	   - 最小の出力トークン量。
	   - スワップ時の最低限の出力量を保証します。
   - `path`
	   - トークンスワップのためのトークンアドレスの配列。
	   - 指定したトークン同士のスワップ経路を示します。
   - `to`
	   - 出力トークンを受け取るアドレス。
   - `deadline`
	   - スワップが実行される期限。

正確な出力量でのスワップの実行。

1. **SwapRouter02**コントラクトに、最大の入力トークン量を使用する許可を与えます。
2. exactOutput関数を呼び出し、以下の引数を指定します。
   - `amountOut`
	   - 正確な出力トークン量。
   - `amountInMax`
	   - 最大の入力トークン量。
	   - スワップ時に使用される最大の入力量を示します。
   - `path`
	   - トークンスワップのためのトークンアドレスの配列。
	   - 指定したトークン同士のスワップ経路を示します。
   - `to`
	   - 出力トークンを受け取るアドレス。
   - `deadline`
	   - スワップが実行される期限。

## イベント

### Swap
トークンスワップが実行されたことを示すイベント。

- `sender`
	- スワップを開始したアドレス。
- `amount0In`
	- トークン0の入力量。
- `amount1In`
	- トークン1の入力量。
- `amount0Out`
	- トークン0の出力量。
- `amount1Out`
	- トークン1の出力量。
- `to`
	- 出力トークンを受け取ったアドレス。

### RefundETH
余剰のETHが返金されたことを示すイベント。

- `amount`
	- 返金されたETHの量。
- `to`
	- 返金を受け取ったアドレス。

## 関連ERC/EIP

- [EIP-20: ERC-20 Token Standard](https://eips.ethereum.org/EIPS/eip-20)

https://chaldene.net/erc20

- [EIP-165: Standard Interface Detection](https://eips.ethereum.org/EIPS/eip-165)

https://qiita.com/cardene/items/541184977a5cba51b759

- [EIP-721: ERC-721 Non-Fungible Token Standard](https://eips.ethereum.org/EIPS/eip-721)
- [EIP-721 Metadata: ERC-721 Token Metadata JSON Schema](https://eips.ethereum.org/EIPS/eip-721)
- [EIP-721 Enumerable: ERC-721 Token Enumerable](https://eips.ethereum.org/EIPS/eip-721)
- [EIP-712: Ethereum Typed Structured Data Hashing and Signing](https://eips.ethereum.org/EIPS/eip-712)

https://chaldene.net/erc721

https://chaldene.net/erc721-extension

- [EIP-2612: ERC-2612 Permit](https://eips.ethereum.org/EIPS/eip-2612)
- [EIP-3009: ERC-3009: Transfer With Authorization](https://eips.ethereum.org/EIPS/eip-3009)
- [EIP-3156: Flashbots - MEV-protected mempool transactions](https://eips.ethereum.org/EIPS/eip-3156)
- [EIP-3664: ChainID opcode](https://eips.ethereum.org/EIPS/eip-3664)
- [EIP-7121: EIP-712 Typed Structured Data Gas Cost Reduction](https://eips.ethereum.org/EIPS/eip-7121)
- [EIP-7122: EIP-712 Typed Structured Data Revocation](https://eips.ethereum.org/EIPS/eip-7122)
- [EIP-7123: EIP-712 Typed Structured Data Validation](https://eips.ethereum.org/EIPS/eip-7123)
- [EIP-7124: EIP-712 Typed Structured Data Validation Revocation](https://eips.ethereum.org/EIPS/eip-7124)
- [EIP-7125: EIP-712 Typed Structured Data Validation Revocation Validation](https://eips.ethereum.org/EIPS/eip-7125)
- [EIP-7126: EIP-712 Typed Structured Data Validation Revocation Validation Revocation](https://eips.ethereum.org/EIPS/eip-7126)
- [EIP-7127: EIP-712 Typed Structured Data Validation Revocation Validation Revocation Validation](https://eips.ethereum.org/EIPS/eip-7127)

# パラメータ

### _factoryV2
V2ファクトリーコントラクトのアドレスです。
- V2ファクトリーコントラクトは、**Uniswap V2**のプールを作成および管理する役割を果たします。
- トークンの流動性プールを作成し、トークンのスワップを可能にします。

### _factoryV3
V3ファクトリーコントラクトのアドレスです。
- V3ファクトリーコントラクトは、**Uniswap V3**のプールを作成および管理するためのコントラクトです。
- V3ではより精密な価格帯を設定し、トークンの効率的なスワップを実現します。

### _positionManager
ポジションマネージャーコントラクトのアドレスです。
- ポジションマネージャーコントラクトは、**Uniswap V3**の流動性プール内のポジションを管理する役割を果たします。
- ユーザーはこのコントラクトを介してトークンの流動性を提供および管理することができます。

### _WETH9
**WETH9**コントラクトのアドレスです。
- **WETH9（Wrapped Ether）** は、イーサリアムのネイティブトークン**ETH**を**ERC20**トークン化したバージョンです。
- **ETH**をトークンとして取り扱うことができるため、分散型取引所などでトークン同士のスワップを実行する際に便利に使用されます。

:::message
**Uniswap V2**および**Uniswap V3**のファクトリーコントラクトは、異なるバージョンの**Uniswap**プロトコルをサポートするために存在します。
これらのファクトリーコントラクトは、それぞれのバージョンで特有の機能やプールを作成および管理するために使用されます。

使い分けに関しては、**Uniswap V2**ファクトリーコントラクトと**Uniswap V3**ファクトリーコントラクトの違いとそれぞれの特徴を考慮して判断されます。

### Uniswap V2ファクトリーコントラクト

**バージョンの安定性**
**Uniswap V2**は比較的古いバージョンで、多くのユーザーに利用されています。
既に確立されたトークンプールが存在しており、その流動性を利用することができます。

**一般的な使用**
一般的なトークンスワップや流動性の提供には、**Uniswap V2**が広く使用されています。
特にV2でのトークンプールが既に存在し、多くのユーザーが利用している場合、引き続きV2を使用することがあります。

### Uniswap V3ファクトリーコントラクト

**価格帯の最適化**
**Uniswap V3**は価格帯の細かな設定が可能であり、より精密な価格帯を設定することができます。
これにより、ユーザーは特定の価格帯でのトークンスワップをより効率的に行うことができます。

**集中的な流動性**
**Uniswap V3**では、トークンの流動性プールを特定の価格帯に集中的に提供することが可能です。
これにより、トークンの特定の価格帯でのスワップが効率的に行えるようになります。

**高度な機能**
**Uniswap V3**では、様々なアクティビティを管理するための高度な機能が追加されています。
これにより、より細かいトークンスワップの制御や最適化が可能です。

使い分けの際には、トークンスワップの目的や要件に合わせて適切なバージョンを選択することが重要です。
:::

# コントラクト

## SwapRouter02

:::details SwapRouter02
```solidity
contract SwapRouter02 is ISwapRouter02, V2SwapRouter, V3SwapRouter, ApproveAndCall, MulticallExtended, SelfPermit {
    constructor(
        address _factoryV2,
        address factoryV3,
        address _positionManager,
        address _WETH9
    ) ImmutableState(_factoryV2, _positionManager) PeripheryImmutableState(factoryV3, _WETH9) {}
}
```
:::

### ISwapRouter02
**ISwapRouter02**インターフェースを実装しています。
これにより、**SwapRouter02**コントラクトは**ISwapRouter02**のメソッドを含むインターフェースの機能を提供します。

### V2SwapRouter
**Uniswap V2**のスワップルーター機能を提供するコントラクトです。
**V2SwapRouter**によって、**Uniswap V2**のバージョン固有のスワップ関連機能が実装されています。

### V3SwapRouter
**Uniswap V3**のスワップルーター機能を提供するコントラクトです。
**V3SwapRouter**によって、**Uniswap V3**のバージョン固有のスワップ関連機能が実装されています。

### ApproveAndCall
ユーザーがコントラクトに対して承認と同時にコールを実行できる機能を提供します。
これにより、スワップ操作がより効率的に行えます。

:::message
**ApproveAndCall**は、ユーザーがトークンの承認と同時に特定の関数を実行できる仕組みを提供する機能です。
これにより、ユーザーがトークンの承認とトークン操作（スワップなど）を1つのトランザクションで実行できます。

通常、ユーザーがトークンを使用して何らかの操作（たとえばトークンのスワップ）を行う際には、以下の手順が必要です。

1. トークンを操作するコントラクトに対して、ユーザーはトークンの承認を行います。
これにより、コントラクトが特定の量のトークンを使用できるようになります。
2. 承認が行われたら、ユーザーは実際にトークンの操作（スワップなど）を行うために、別の関数を呼び出します。

**ApproveAndCall**は、これらの手順を1つのトランザクションで行います。
具体的には、ユーザーはトークンの承認と操作を同時に行うための関数を呼び出します。
この関数は以下のような流れで動作します：

1. ユーザーが**ApproveAndCall**関数を呼び出し、トークンの承認量や目的の操作に関する情報を提供します。
2. コントラクトはユーザーのトークン承認を確認し、指定された操作を実行します。

これにより、ユーザーは複数のトランザクションを待つ必要なく、1つのトランザクションでトークンの承認と操作をまとめて行えるため、効率的にスワップなどの操作を行うことができます。
:::

### MulticallExtended
複数の関数コールを1つのトランザクションで実行できる機能を提供します。
複数の操作を1度のトランザクションでまとめて行えるため、ガスの節約に寄与します。

### SelfPermit
イーサリアムの**EIP-2612**に基づいて、コントラクト自体に対してトークンの承認を行う機能を提供します。

:::message
**SelfPermit**は、イーサリアムの**EIP-2612**という提案に基づいて実装された機能で、コントラクト自体に対してトークンの承認を行うための機能を提供します。

通常、ユーザーがトークンを操作するコントラクトに対してトークンの承認を行う場合、ユーザーがトークンの所有者であることを確認するために、個別の手順が必要です。
しかし、**EIP-2612**はこのプロセスをより簡潔にし、トークン所有者の許可なしでコントラクトが自己に対してトークンの承認を行うための仕組みを提供するものです。
これにより、コントラクトが特定のトークンを直接操作するために必要な承認を、トークン所有者の介入なしで行うことができます。

具体的には、**SelfPermit**を使用すると、コントラクト自体が次のような手順を実行します。

1. コントラクトが自己に対してトークンの承認を要求するためのデータを作成します。
2. トークン所有者がそのデータに署名し、承認の意思を示します。
3. コントラクトは、署名されたデータを使ってトークンの承認を自己に対して行います。

これにより、トークン所有者は一度の承認でコントラクトが特定のトークンを操作できるようにし、またセキュリティを向上させることができます。

**SelfPermit**は、コントラクトが自己に対してトークンの承認を行うための効率的でセキュアな方法を提供し、トークン操作のプロセスをスムーズにします。
:::

### constructor
コントラクトのコンストラクタ関数です。
**Uniswap V2**および**V3**のファクトリーコントラクトのアドレス、ポジションマネージャーコントラクトのアドレス、**WETH9**トークンコントラクトのアドレスを受け取ります。
これらのアドレスはコントラクトの初期化時に設定され、スワップ操作などに使用されます。

## V2SwapRouter

以下の記事に詳しくまとめています。

https://zenn.dev/cryptogames/articles/a958a6870d9016

## V3SwapRouter

以下の記事に詳しくまとめています。

https://zenn.dev/cryptogames/articles/614571336c74e2

## ApproveAndCall

トークンの承認と同時に特定の関数を実行できる機能を提供する抽象コントラクト。
ユーザーがトークンの承認と関数の呼び出しを同時に行うための仕組みを提供します。
これにより、トークンの承認と操作を1つのトランザクションで結びつけることができ、操作の効率が向上します。

---

### tryApprove

:::details tryApprove
```solidity
function tryApprove(address token, uint256 amount) private returns (bool) {
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(IERC20.approve.selector, positionManager, amount));
        return success && (data.length == 0 || abi.decode(data, (bool)));
}
```
:::

**概要**
指定したトークンに対して`PositionManager`への承認を実行する関数。

**引数**
- `token`
	- 承認するトークンのアドレス。
- `amount`
	- 承認するトークンの量。

**戻り値**
- `bool`
	- 承認が成功したかどうか。

---

### getApprovalType

:::details getApprovalType
```solidity
function getApprovalType(address token, uint256 amount) external override returns (ApprovalType) {
        // check existing approval
        if (IERC20(token).allowance(address(this), positionManager) >= amount) return ApprovalType.NOT_REQUIRED;

        // try type(uint256).max / type(uint256).max - 1
        if (tryApprove(token, type(uint256).max)) return ApprovalType.MAX;
        if (tryApprove(token, type(uint256).max - 1)) return ApprovalType.MAX_MINUS_ONE;

        // set approval to 0 (must succeed)
        require(tryApprove(token, 0));

        // try type(uint256).max / type(uint256).max - 1
        if (tryApprove(token, type(uint256).max)) return ApprovalType.ZERO_THEN_MAX;
        if (tryApprove(token, type(uint256).max - 1)) return ApprovalType.ZERO_THEN_MAX_MINUS_ONE;

        revert();
}
```
:::

**概要**
トークンの承認タイプを取得する関数。

**詳細**
特定のトークンに対して承認が必要かどうか、どのタイプの承認が必要かを判断します。
複数の承認方法を試みて、適切な承認タイプを返します。

**引数**
- `token`
	- 承認対象のトークンのアドレス。
- `amount`
	- 承認するトークンの量。

**戻り値**
- `ApprovalType`
	- 承認タイプ（`NOT_REQUIRED`、`MAX`、`MAX_MINUS_ONE`、`ZERO_THEN_MAX`、`ZERO_THEN_MAX_MINUS_ONE`）。

:::message
**NOT_REQUIRED**

既に十分な承認が行われているため、新たな承認は必要ないことを示す承認タイプです。

**MAX**

トークンの最大量を承認することを示す承認タイプです。

**MAX_MINUS_ONE**

トークンの最大量から1を引いた量を承認することを示す承認タイプです。
トークンの最大量をわずかに下回る量を承認することができます。

**ZERO_THEN_MAX**

最初にトークンの承認を`0`に設定し、その後最大量を承認することを示す承認タイプです。
一時的にトークンの使用を制限し、その後必要な操作に対して最大量を承認することができます。

**ZERO_THEN_MAX_MINUS_ONE**

最初にトークンの承認を`0`に設定し、その後最大量から1を引いた量を承認することを示す承認タイプです。
一時的にトークンの使用を制限し、その後必要な操作に対して最大量をわずかに下回る量を承認することができます。
:::

---

### approveMax

:::details approveMax
```solidity
function approveMax(address token) external payable override {
        require(tryApprove(token, type(uint256).max));
}
```
:::

**概要**
最大のトークン承認を行う関数。

**引数**
- `token`
	- 承認対象のトークンのアドレス。

---

### approveMaxMinusOne

:::details approveMaxMinusOne
```solidity
function approveMaxMinusOne(address token) external payable override {
        require(tryApprove(token, type(uint256).max - 1));
}
```
:::

**概要**
最大値から1引いたトークン量を承認する関数。

**引数**
- `token`
	- 承認対象のトークンのアドレス。

---

### approveZeroThenMax

:::details approveZeroThenMax
```solidity
function approveZeroThenMax(address token) external payable override {
        require(tryApprove(token, 0));
        require(tryApprove(token, type(uint256).max));
}
```
:::

**概要**
最初にトークンの承認を`0`に設定し、その後最大のトークン承認を行う関数。

**引数**
- `token`
	- 承認対象のトークンのアドレス。

---

### approveZeroThenMaxMinusOne

:::details approveZeroThenMaxMinusOne
```solidity
function approveZeroThenMaxMinusOne(address token) external payable override {
        require(tryApprove(token, 0));
        require(tryApprove(token, type(uint256).max - 1));
}
```
:::

**概要**
最初にトークンの承認を`0`に設定し、その後最大値から1引いた量のトークン承認を行う関数。

**引数**
- `token`
	- 承認対象のトークンのアドレス。

---

### callPositionManager

:::details callPositionManager
```solidity
function callPositionManager(bytes memory data) public payable override returns (bytes memory result) {
        bool success;
        (success, result) = positionManager.call(data);

        if (!success) {
            // Next 5 lines from https://ethereum.stackexchange.com/a/83577
            if (result.length < 68) revert();
            assembly {
                result := add(result, 0x04)
            }
            revert(abi.decode(result, (string)));
        }
}
```
:::

**概要**
`PositionManager`コントラクトの特定の関数を呼び出す関数。

**詳細**
`PositionManager`コントラクトの特定の関数を呼び出し、その結果を返します。
関数呼び出しに失敗した場合、エラーメッセージが表示されます。

**引数**
- `data`
	- 呼び出す関数と引数のデータ。

**戻り値**
- `bytes memory`
	- 関数の実行結果。

---

### balanceOf

:::details balanceOf
```solidity
function balanceOf(address token) private view returns (uint256) {
        return IERC20(token).balanceOf(address(this));
}
```
:::

**概要**
指定したトークンの残高を取得する関数。

**引数**
- `token`
	- 残高を取得するトークンのアドレス。

**戻り値**
- `uint256`
	- トークンの残高。

---


### mint

:::details mint
```solidity
function mint(MintParams calldata params) external payable override returns (bytes memory result) {
        return
            callPositionManager(
                abi.encodeWithSelector(
                    INonfungiblePositionManager.mint.selector,
                    INonfungiblePositionManager.MintParams({
                        token0: params.token0,
                        token1: params.token1,
                        fee: params.fee,
                        tickLower: params.tickLower,
                        tickUpper: params.tickUpper,
                        amount0Desired: balanceOf(params.token0),
                        amount1Desired: balanceOf(params.token1),
                        amount0Min: params.amount0Min,
                        amount1Min: params.amount1Min,
                        recipient: params.recipient,
                        deadline: type(uint256).max // deadline should be checked via multicall
                    })
                )
            );
}
```
:::

**概要**
**Uniswap V3**のポジションを新たに作成する関数。

**詳細**
この関数は、指定されたパラメータに基づいて**Uniswap V3**のポジションを新たに作成します。
トークン0とトークン1の情報、手数料、トレーディングペアの価格範囲（ティック範囲）、希望のトークン0量とトークン1量、最小のトークン0量とトークン1量、受取人のアドレスなどが指定されます。トークンの承認は、前述の方法で行われます。

**引数**
- `params`
	- 作成するポジションのパラメータが含まれる構造体。

**戻り値**
- `result`
	- ポジション作成の結果を示すデータ。

### increaseLiquidity

:::details increaseLiquidity
```solidity
function increaseLiquidity(IncreaseLiquidityParams calldata params)
        external
        payable
        override
        returns (bytes memory result)
    {
        return
            callPositionManager(
                abi.encodeWithSelector(
                    INonfungiblePositionManager.increaseLiquidity.selector,
                    INonfungiblePositionManager.IncreaseLiquidityParams({
                        tokenId: params.tokenId,
                        amount0Desired: balanceOf(params.token0),
                        amount1Desired: balanceOf(params.token1),
                        amount0Min: params.amount0Min,
                        amount1Min: params.amount1Min,
                        deadline: type(uint256).max // deadline should be checked via multicall
                    })
                )
            );
}
```
:::

**概要**
既存の**Uniswap V3**のポジションに対して流動性を追加する関数。

**詳細**
指定されたパラメータに基づいて既存の**Uniswap V3**のポジションに流動性を追加します。
ポジションのトークン0とトークン1の情報、追加するトークン0量とトークン1量、最小のトークン0量とトークン1量が指定されます。トークンの承認は、前述の方法で行われます。

**引数**
- `params`
	- 流動性を追加するポジションのパラメータが含まれる構造体。

**戻り値**
- `result`
	- 流動性追加の結果を示すデータ。

## MulticallExtended

### multicall

:::details multicall
```solidity
function multicall(uint256 deadline, bytes[] calldata data)
        external
        payable
        override
        checkDeadline(deadline)
        returns (bytes[] memory)
    {
        return multicall(data);
}
```
:::

**概要**
複数のコールをまとめて実行する関数。

**詳細**
この関数は、指定されたデータの配列に含まれる複数のコールをまとめて実行します。
デッドライン（締め切り時刻）や前のブロックハッシュの確認も行われます。
実行結果は、それぞれのコールに対応する結果の配列として返されます。

**引数**
- `deadline`
	- 実行のデッドライン（締め切り時刻）。
- `data`
	- 実行するコールのデータの配列。

**戻り値**
- `bytes[] memory`
	- 各コールの実行結果を示すデータの配列。

---

### multicall (previousBlockhash version)

:::details multicall
```solidity
function multicall(bytes32 previousBlockhash, bytes[] calldata data)
        external
        payable
        override
        checkPreviousBlockhash(previousBlockhash)
        returns (bytes[] memory)
    {
        return multicall(data);
}
```
:::

**概要**
複数のコールをまとめて実行する関数。

**詳細**
この関数は、指定されたデータの配列に含まれる複数のコールをまとめて実行します。
この時、前のブロックハッシュの確認が行われます。
実行結果は、それぞれのコールに対応する結果の配列として返されます。

**引数**
- `previousBlockhash`
	- 実行の前のブロックハッシュ。
- `data`
	- 実行するコールのデータの配列。

**戻り値**
- `bytes[] memory`
	- 各コールの実行結果を示すデータの配列。

:::message
2つの`multicall`関数の違いをわかりやすく説明します。

### multicall

**概要**
指定されたデータの配列に含まれる複数のコールをまとめて実行する関数。

**適用条件**
デッドライン（締め切り時刻）が指定されている。

**詳細**
指定されたデータの配列に含まれる複数のコールをまとめて実行します。
デッドラインのチェックが行われ、締め切り時刻を過ぎていないことが確認されます。
実行結果は、それぞれのコールに対応する結果の配列として返されます。

### multicall (previousBlockhash version)

**概要**
指定されたデータの配列に含まれる複数のコールをまとめて実行する関数。

**適用条件**
前のブロックハッシュが指定されてい。

**詳細**
指定されたデータの配列に含まれる複数のコールをまとめて実行します。
前のブロックハッシュのチェックが行われ、実行時の前のブロックハッシュと指定された前のブロックハッシュが一致することが確認されます。
実行結果は、それぞれのコールに対応する結果の配列として返されます。

2つの関数の違いは、実行の制約条件に関連しています。
1つ目の関数はデッドラインを、2つ目の関数は前のブロックハッシュを確認することで、安全な実行を保証します。
:::

## ImmutableState

### factoryV2

:::details factoryV2
```solidity
address public immutable override factoryV2;
```
:::

**概要**
**Uniswap V2**のファクトリーコントラクトのアドレスを格納する変数。

**詳細**
**Uniswap V2**のファクトリーコントラクトのアドレスを不変で公開可能な形で変数に格納しています。
**Uniswap V2**のファクトリーコントラクトは、トークンの取引を管理するための基盤となるコントラクトです。

---

### positionManager

:::details positionManager
```solidity
address public immutable override positionManager;
```
:::

**概要**
ポジションマネージャーコントラクトのアドレスを格納する変数。

**詳細**
この変数はポジションマネージャーコントラクトのアドレスを不変で公開可能な形で変数に格納しています。
ポジションマネージャーコントラクトは、**Uniswap V3**のポジションを管理するためのコントラクトです。

---

### constructor

:::details constructor
```solidity
constructor(address _factoryV2, address _positionManager) {
        factoryV2 = _factoryV2;
        positionManager = _positionManager;
}
```
:::

**概要**
コントラクトのコンストラクタは初期化時に実行される関数で、変数の初期値を設定します。

**詳細**
このコンストラクタは`factoryV2`と`positionManager`変数を初期化します。
コントラクトをデプロイする際に提供されたアドレスがそれぞれの変数に設定されます。

**引数**:
- `_factoryV2`
	- **Uniswap V2**のファクトリーコントラクトのアドレス。
- `_positionManager`
	- ポジションマネージャーコントラクトのアドレス。

## PeripheryPaymentsWithFeeExtended

### unwrapWETH9WithFee

:::details unwrapWETH9WithFee
```solidity
function unwrapWETH9WithFee(
        uint256 amountMinimum,
        uint256 feeBips,
        address feeRecipient
    ) external payable override {
        unwrapWETH9WithFee(amountMinimum, msg.sender, feeBips, feeRecipient);
}
```
:::

**概要**
**WETH9**トークンを手数料付きでアンラップする関数。

**詳細**
指定された最小量の**WETH9**トークンをアンラップします。
手数料が差し引かれた後、残りのトークンが指定されたアドレスに送信されます。

**引数**:
- `amountMinimum`
	- 最小アンラップ量として要求される**WETH9**トークンの量。
- `feeBips`
	- 手数料として支払われる基準ポイントの量。
- `feeRecipient`
	- 手数料の受取先となるアドレス。

**戻り値**: なし

---

### sweepTokenWithFee

:::details sweepTokenWithFee
```solidity
function sweepTokenWithFee(
        address token,
        uint256 amountMinimum,
        uint256 feeBips,
        address feeRecipient
    ) external payable override {
        sweepTokenWithFee(token, amountMinimum, msg.sender, feeBips, feeRecipient);
}
```
:::

**概要**
指定されたトークンを手数料付きでスウィープする関数。

**詳細**
指定されたトークンをアンラップして手数料が差し引かれた後、残りのトークンが指定されたアドレスに送信されます。

**引数**:
- `token`: スウィープされる対象のトークンのアドレス。
- `amountMinimum`: 最小スウィープ量として要求されるトークンの量。
- `feeBips`: 手数料として支払われる基準ポイントの量。
- `feeRecipient`: 手数料の受取先となるアドレス。

**戻り値**: なし

:::message
**PeripheryPaymentsWithFeeExtended**コントラクトは、手数料を含むトークンのアンラップ（**ETH**に変換する操作）やトークンのスウィープ（指定されたアドレスにトークンを送る操作）を拡張した機能を提供するものです。
これを具体的な例を用いて説明します。

例えば、あるDeFi向けのプラットフォームがあり、ユーザーがそのプラットフォーム上でトークンのアンラップやスウィープを行いたい場合を考えてみましょう。
このプラットフォームでは、ユーザーがトークンを**ETH**にアンラップしたり、特定のトークンを指定のアドレスに送る操作を行う際に手数料が発生することがあります。

この場合、**PeripheryPaymentsWithFeeExtended**コントラクトが使用される可能性があります。
ユーザーはこのコントラクトを介して、手数料を含むトークンのアンラップやスウィープを行うことができます。以下に具体的な使用例を示します。

**トークンのアンラップ（Unwrap）**

ユーザーがプラットフォーム上でトークンをETHにアンラップしたい場合、次のように呼び出すことができます。

```solidity
contractInstance.unwrapWETH9WithFee(amountMinimum, feeBips, feeRecipient);
```

ここで、`amountMinimum`はアンラップする最小量、`feeBips`は手数料の割合、`feeRecipient`は手数料を受け取るアドレスです。

**トークンのスウィープ（Sweep）**

ユーザーが特定のトークンを指定のアドレスに送る操作を行いたい場合、次のように呼び出すことができます。

```solidity
contractInstance.sweepTokenWithFee(token, amountMinimum, feeBips, feeRecipient);
```

ここで、`token`は送信するトークンのアドレス、`amountMinimum`は送信する最小量、`feeBips`は手数料の割合、`feeRecipient`は手数料を受け取るアドレスです。

**PeripheryPaymentsWithFeeExtended**コントラクトユーザーが手数料を考慮してトークンのアンラップやスウィープを行う場面で利用され、手数料の支払いや受取り先の指定を簡便に行うためのものです。
:::

## PeripheryPaymentsExtended

### unwrapWETH9

:::details unwrapWETH9
```solidity
function unwrapWETH9(uint256 amountMinimum) external payable override {
        unwrapWETH9(amountMinimum, msg.sender);
}
```
:::

**概要**
**ETH**に変換された**WETH（Wrapped Ether）** トークンをアンラップし、元の**ETH**に戻す関数。

**詳細**
ユーザーが**WETH**トークンをアンラップし、最低限必要な量の**ETH**を受け取るために使用されます。
アンラップする**WETH**の最小量を指定する必要があります。

**引数**:
- `amountMinimum`
	- アンラップする最小の**WETH**量。

---

### wrapETH

:::details wrapETH
```solidity
function wrapETH(uint256 value) external payable override {
        IWETH9(WETH9).deposit{value: value}();
}
```
:::

**概要**
**ETH**を**WETH（Wrapped Ether）** トークンに変換する関数。

**引数**:
- `value`
	- 変換する**ETH**の量。

---

### sweepToken

:::details sweepToken
```solidity
function sweepToken(address token, uint256 amountMinimum) external payable override {
        sweepToken(token, amountMinimum, msg.sender);
}
```
:::

**概要**
指定されたトークンを指定のアドレスに送る関数。

**詳細**
ユーザーが指定されたトークンを指定されたアドレスに送るために使用されます。
送信するトークンのアドレスと送信する最小量を指定する必要があります。

**引数**:
- `token`
	- 送信するトークンのアドレス。
- `amountMinimum`
	- 送信する最小のトークン量。

---

### pull

:::details pull
```solidity
function pull(address token, uint256 value) external payable override {
        TransferHelper.safeTransferFrom(token, msg.sender, address(this), value);
}
```
:::

**概要**
他のアドレスからトークンを受け取る関数。

**詳細**
他のアドレスからトークンを受け取るために使用されます。
受け取るトークンのアドレスと受け取る量を指定する必要があります。

**引数**:
- `token`
	- 受け取るトークンのアドレス。
- `value`
	- 受け取るトークンの量。

---

## OracleSlippage

### getBlockStartingAndCurrentTick

:::details getBlockStartingAndCurrentTick
```solidity
function getBlockStartingAndCurrentTick(IUniswapV3Pool pool)
        internal
        view
        returns (int24 blockStartingTick, int24 currentTick)
    {
        uint16 observationIndex;
        uint16 observationCardinality;
        (, currentTick, observationIndex, observationCardinality, , , ) = pool.slot0();

        // 2 observations are needed to reliably calculate the block starting tick
        require(observationCardinality > 1, 'NEO');

        // If the latest observation occurred in the past, then no tick-changing trades have happened in this block
        // therefore the tick in `slot0` is the same as at the beginning of the current block.
        // We don't need to check if this observation is initialized - it is guaranteed to be.
        (uint32 observationTimestamp, int56 tickCumulative, , ) = pool.observations(observationIndex);
        if (observationTimestamp != uint32(_blockTimestamp())) {
            blockStartingTick = currentTick;
        } else {
            uint256 prevIndex = (uint256(observationIndex) + observationCardinality - 1) % observationCardinality;
            (uint32 prevObservationTimestamp, int56 prevTickCumulative, , bool prevInitialized) =
                pool.observations(prevIndex);

            require(prevInitialized, 'ONI');

            uint32 delta = observationTimestamp - prevObservationTimestamp;
            blockStartingTick = int24((tickCumulative - prevTickCumulative) / delta);
        }
}
```
:::

**概要**
現在のブロックの開始時点と現在の時点のティック（価格の変動幅）を返す関数。

**詳細**
指定された**Uniswap V3**プールにおける現在のティックと開始時点のティックを取得します。
これにより、ブロック内での価格変動を把握し、スリッページを評価するのに役立ちます。

**引数**:
- `pool`
	- 操作対象の**Uniswap V3**プールのアドレス。

**戻り値**:
- `blockStartingTick`
	- ブロックの開始時点のティック（価格の変動幅）。
- `currentTick`: 現在のティック（価格の変動幅）。

---

### getPoolAddress

:::details getPoolAddress
```solidity
function getPoolAddress(
        address tokenA,
        address tokenB,
        uint24 fee
    ) internal view virtual returns (IUniswapV3Pool pool) {
        pool = IUniswapV3Pool(PoolAddress.computeAddress(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee)));
}
```
:::

**概要**
プールのアドレスを取得する関数。

**詳細**
トークンA、トークンB、および手数料率を指定してプールのアドレスを取得します。

**引数**:
- `tokenA`
	- トークンAのアドレス。
- `tokenB`
	- トークンBのアドレス。
- `fee`
	- プールの手数料率。

**戻り値**:
- `pool`
	- 取得されたプールのインターフェース。

---

### getSyntheticTicks

:::details getSyntheticTicks
```solidity
function getSyntheticTicks(bytes memory path, uint32 secondsAgo)
        internal
        view
        returns (int256 syntheticAverageTick, int256 syntheticCurrentTick)
    {
        bool lowerTicksAreWorse;

        uint256 numPools = path.numPools();
        address previousTokenIn;
        for (uint256 i = 0; i < numPools; i++) {
            // this assumes the path is sorted in swap order
            (address tokenIn, address tokenOut, uint24 fee) = path.decodeFirstPool();
            IUniswapV3Pool pool = getPoolAddress(tokenIn, tokenOut, fee);

            // get the average and current ticks for the current pool
            int256 averageTick;
            int256 currentTick;
            if (secondsAgo == 0) {
                // we optimize for the secondsAgo == 0 case, i.e. since the beginning of the block
                (averageTick, currentTick) = getBlockStartingAndCurrentTick(pool);
            } else {
                (averageTick, ) = OracleLibrary.consult(address(pool), secondsAgo);
                (, currentTick, , , , , ) = IUniswapV3Pool(pool).slot0();
            }

            if (i == numPools - 1) {
                // if we're here, this is the last pool in the path, meaning tokenOut represents the
                // destination token. so, if tokenIn < tokenOut, then tokenIn is token0 of the last pool,
                // meaning the current running ticks are going to represent tokenOut/tokenIn prices.
                // so, the lower these prices get, the worse of a price the swap will get
                lowerTicksAreWorse = tokenIn < tokenOut;
            } else {
                // if we're here, we need to iterate over the next pool in the path
                path = path.skipToken();
                previousTokenIn = tokenIn;
            }

            // accumulate the ticks derived from the current pool into the running synthetic ticks,
            // ensuring that intermediate tokens "cancel out"
            bool add = (i == 0) || (previousTokenIn < tokenIn ? tokenIn < tokenOut : tokenOut < tokenIn);
            if (add) {
                syntheticAverageTick += averageTick;
                syntheticCurrentTick += currentTick;
            } else {
                syntheticAverageTick -= averageTick;
                syntheticCurrentTick -= currentTick;
            }
        }

        // flip the sign of the ticks if necessary, to ensure that the lower ticks are always worse
        if (!lowerTicksAreWorse) {
            syntheticAverageTick *= -1;
            syntheticCurrentTick *= -1;
        }
}
```
:::

**概要**
指定されたパスと時間から、合成的な時加重平均ティックを取得する関数。

**詳細**
指定されたパス（トークン間の経路）と時間から、指定時間内の合成的な時加重平均ティックを取得します。
また、現在のティックも取得します。
この合成ティックは、スリッページの評価に使用されます。

**引数**:
- `path`
	- トークン間の経路を示すバイト列。
- `secondsAgo`
	- 遡る時間（秒単位）。

**戻り値**:
- `syntheticAverageTick`
	- 合成的な時加重平均ティック。
- `syntheticCurrentTick`
	- 現在のティック。

:::message
**合成的な時加重平均ティック**

例えば、以下のようなトークン間の経路（パス）が考えられるとします。

```
トークンA → トークンB → トークンC
```

このパスは、トークンAをトークンBにスワップし、その後トークンBをトークンCにスワップする経路を表しています。
このとき、各スワップにおけるティックの平均値と現在のティックを求めることで、合成的な時加重平均ティックを計算できます。

例えば、`トークンA → トークンB`のスワップでの平均ティックが`100`、現在のティックが`105`であり、`トークンB → トークンC`のスワップでの平均ティックが`110`、現在のティックが`115`とします。

この場合、各スワップの重みを考慮して合成的な時加重平均ティックを計算します。
`トークンA → トークンB`のスワップが`トークンB → トークンC`のスワップよりも重要である場合、合成的な時加重平均ティックは`(100 + 110) / 2 = 105`となります。
逆に、`トークンB → トークンC`のスワップがより重要である場合、合成的な時加重平均ティックは `(105 + 115) / 2 = 110`となります。

このようにして、複数のスワップパスにおけるティックの影響を考慮しながら、合成的な時加重平均ティックを計算することができます。
合成的な時加重平均ティックは、スワップにおける価格変動を総合的に評価するために使用され、スリッページの予測や取引の最適化に役立ちます。
:::

---

### toInt24

:::details toInt24
```solidity
function toInt24(int256 y) private pure returns (int24 z) {
        require((z = int24(y)) == y);
}
```
:::

**概要**
`int256`を`int24`にキャストする関数。

**詳細**
`int256`を`int24`にキャストします。
オーバーフローやアンダーフローが発生した場合、リバート（エラーを発生）します。

**引数**:
- `y`
	- キャストされる`int256`の値。

**戻り値**:
- `z`
	- キャストされた`int24`の値。

:::message
`toInt24`関数は、`int256`型の値を`int24`型にキャストする際に使用されます。
`int24`型は`-2^23`から`2^23-1`までの整数値を表現するための型であり、一方で`int256`型はより広い範囲の整数値を表現できる型です。
キャストを行う際には、値が`int24`型の範囲内に収まるかどうかを確認する必要があります。

具体例を挙げて、`toInt24`関数の使用場面を説明します。

例えば、以下のような状況を考えてみましょう。

```solidity
function calculateTotal(uint256[] memory values) external pure returns (int24) {
    int256 total = 0;
    for (uint256 i = 0; i < values.length; i++) {
        total += int256(values[i]);
    }
    return toInt24(total);
}
```

この関数は、`values`という配列に含まれる整数値の合計を計算し、その合計値を`int24`型で返すものです。

例えば、`values`が`[100, 200, -150]`という配列の場合、合計は`150`になります。
しかし、この合計値は`int256`型の範囲内ですが、`int24`型にキャストするためには範囲が`-2^23`から`2^23-1`までに収まっている必要があります。
そのため、キャストを行う前に`toInt24`関数を使用して確認します。
もし合計値がこの範囲外になる場合、関数はリバートしてエラーを発生させます。

このように、`toInt24`関数は整数値のキャストを行う際に、値が指定の範囲内に収まることを確認するために使用されます。
これにより、範囲外の値が不正な結果をもたらすことを防ぐことができます。
:::

---

### getSyntheticTicks

:::details getSyntheticTicks
```solidity
function getSyntheticTicks(
        bytes[] memory paths,
        uint128[] memory amounts,
        uint32 secondsAgo
    ) internal view returns (int256 averageSyntheticAverageTick, int256 averageSyntheticCurrentTick) {
        require(paths.length == amounts.length);

        OracleLibrary.WeightedTickData[] memory weightedSyntheticAverageTicks =
            new OracleLibrary.WeightedTickData[](paths.length);
        OracleLibrary.WeightedTickData[] memory weightedSyntheticCurrentTicks =
            new OracleLibrary.WeightedTickData[](paths.length);

        for (uint256 i = 0; i < paths.length; i++) {
            (int256 syntheticAverageTick, int256 syntheticCurrentTick) = getSyntheticTicks(paths[i], secondsAgo);
            weightedSyntheticAverageTicks[i].tick = toInt24(syntheticAverageTick);
            weightedSyntheticCurrentTicks[i].tick = toInt24(syntheticCurrentTick);
            weightedSyntheticAverageTicks[i].weight = amounts[i];
            weightedSyntheticCurrentTicks[i].weight = amounts[i];
        }

        averageSyntheticAverageTick = OracleLibrary.getWeightedArithmeticMeanTick(weightedSyntheticAverageTicks);
        averageSyntheticCurrentTick = OracleLibrary.getWeightedArithmeticMeanTick(weightedSyntheticCurrentTicks);
}
```
:::

**概要**
複数のパスにおける合成的な時加重平均ティックを取得し、それらのティックの加重平均を計算する関数。

**詳細**
複数のパスと指定された時間内でのティックの合成的な時加重平均を取得し、それらのティックの加重平均を計算します。
異なる経路でのスリッページを評価する際に使用されます。
すべてのパスは同じトークンで始まり終わる必要があります。

**引数**:
- `paths`
	- 複数のトークン間の経路を示すバイト列の配列。
- `amounts`
	- 各経路に対する入力量（量の配列）。
- `secondsAgo`
	- 遡る時間（秒単位）。

**戻り値**:
- `averageSyntheticAverageTick`
	- 加重平均された合成的な時加重平均ティック。
- `averageSyntheticCurrentTick`
	- 加重平均された現在のティック。

---

### checkOracleSlippage

:::details checkOracleSlippage
```solidity
function checkOracleSlippage(
        bytes memory path,
        uint24 maximumTickDivergence,
        uint32 secondsAgo
    ) external view override {
        (int256 syntheticAverageTick, int256 syntheticCurrentTick) = getSyntheticTicks(path, secondsAgo);
        require(syntheticAverageTick - syntheticCurrentTick < maximumTickDivergence, 'TD');
}
```
:::

**概要**
スリッページを評価し、指定されたティックの最大乖離を超えるかどうかを確認する関数。

**詳細**
指定されたパスと指定された時間内でのティックの最大乖離を評価し、最大乖離を超える場合にエラーを発生させます。
スリッページを制御するために使用されます。

**引数**:
- `path`
	- トークン間の経路を示すバイト列。
- `maximumTickDivergence`
	- 最大ティック乖離。

離値。
- `secondsAgo`
	- 遡る時間（秒単位）。

:::message
`checkOracleSlippage`関数は、指定したパスにおけるオラクルのティックの変動が、指定された最大ティックの偏差以下であるかどうかを確認するために使用されます。
ここでは、具体的な例を通じてその動作を説明します。

例えば、以下のような関数があるとします。

```solidity
function tradeWithSlippage(bytes memory path, uint24 maximumTickDivergence, uint32 secondsAgo) external view {
    OracleSlippage oracle = OracleSlippage(addressOfOracle); // 仮のアドレスです

    // オラクルスリッページをチェック
    oracle.checkOracleSlippage(path, maximumTickDivergence, secondsAgo);

    // ここにトレードのロジックを記述する
    // ...
}
```

この関数は、指定したパス（トークンの経路）、最大ティックの偏差、および過去の時間からの秒数を受け取ります。
関数内で、`OracleSlippage`コントラクトのインスタンスを作成し、そのインスタンスの`checkOracleSlippage`関数を呼び出しています。

この関数の目的は、指定したパスにおけるオラクルのティックの変動が、指定された最大ティックの偏差以下であることを確認することです。
これにより、トレードを実行する前に、ティックの変動が許容範囲内であるかどうかを確認することができます。

具体的な例を考えてみましょう。
`path`が`[TokenA, TokenB]`とし、`maximumTickDivergence`が`100`、`secondsAgo`が`3600`（1時間前のデータを使用）の場合を考えます。
この関数は、1時間前のデータを使用して指定したパスのティック変動を確認し、最大ティックの偏差が`100`以下であるかどうかをチェックします。
もし指定された偏差を超えてティックが変動している場合、関数はエラーをスローしてトレードを中止します。

このように、`checkOracleSlippage`関数は、トレードの前にオラクルのティックの変動を確認する重要な手段です。
ティックの変動が大きすぎる場合、トレードの実行が予想外の結果をもたらす可能性があるため、この関数を使用してリスクを管理することができます。
:::

---

### checkOracleSlippage

:::details checkOracleSlippage
```solidity
function checkOracleSlippage(
        bytes[] memory paths,
        uint128[] memory amounts,
        uint24 maximumTickDivergence,
        uint32 secondsAgo
    ) external view override {
        (int256 averageSyntheticAverageTick, int256 averageSyntheticCurrentTick) =
            getSyntheticTicks(paths, amounts, secondsAgo);
        require(averageSyntheticAverageTick - averageSyntheticCurrentTick < maximumTickDivergence, 'TD');
}
```
:::

**概要**
複数のパスにおけるスリッページを評価し、指定されたティックの最大乖離を超えるかどうかを確認する関数。

**詳細**
複数のパスと指定された時間内でのティックの最大乖離を評価し、最大乖離を超える場合にエラーを発生させます。
異なる経路でのスリッページを評価する際に使用されます。

**引数**:
- `paths`
	- 複数のトークン間の経路を示すバイト列の配列。
- `amounts`
	- 各経路に対する入力量（量の配列）。
- `maximumTickDivergence`
	- 最大ティック乖離値。
- `secondsAgo`
	- 遡る時間（秒単位）。

---

## PeripheryValidationExtended

### checkPreviousBlockhash

:::details checkPreviousBlockhash
```solidity
modifier checkPreviousBlockhash(bytes32 previousBlockhash) {
        require(blockhash(block.number - 1) == previousBlockhash, 'Blockhash');
        _;
}
```
:::
:::message
`checkPreviousBlockhash`は、関数が実行される前に特定の条件をチェックし、条件が満たされていれば関数の実行を続行するという制御構造を提供します。

具体例を交えて説明します。

```solidity
abstract contract PeripheryValidationExtended is PeripheryValidation {
    modifier checkPreviousBlockhash(bytes32 previousBlockhash) {
        require(blockhash(block.number - 1) == previousBlockhash, 'Blockhash');
        _; // チェックを通過したら関数本体を実行
    }
}
```

この `checkPreviousBlockhash`修飾子は、関数が呼び出される前に前のブロックのハッシュが指定されたハッシュと一致するかどうかを確認します。
もし一致しない場合、関数の呼び出しは失敗し、エラーメッセージ**'Blockhash'** と共に失敗の理由を示します。

例を挙げてみましょう。

```solidity
contract MyContract is PeripheryValidationExtended {
    bytes32 private previousBlockhash;

    function setPreviousBlockhash(bytes32 _blockhash) external {
        previousBlockhash = _blockhash;
    }

    function performAction() external checkPreviousBlockhash(previousBlockhash) {
        // ブロックハッシュのチェックをパスした場合にのみ実行される
        // ...
    }
}
```

上記の`MyContract`コントラクトでは、`performAction`関数が `checkPreviousBlockhash(previousBlockhash)`修飾子を使用しています。
この`modifier`は、指定された`previousBlockhash`と前のブロックのハッシュが一致するかを確認し、一致していれば関数を実行します。
一致しない場合、関数は実行されずにエラーが発生します。

これにより、前のブロックのハッシュが特定の値と一致することが必要な場面で、関数の実行条件を確認することができるようになります。
:::

# イベント

特になし。

# コード

### インターフェース

- `SwapRouter02/contracts/interfaces`

### ベースコントラクト

- `SwapRouter02/contracts/base`

### ライブラリ

- `SwapRouter02/contracts/libraries`

### uniswap core

- `SwapRouter02/@uniswap`

### openzeppelin contract

- SwapRouter02/@openzeppelin`

# 最後に

今回の記事では、Bunzzの新機能『**DeCipher**』を使用して、**UniswapV3**の「**SwapRouter02**」のコントラクトを見てきました。
いかがだったでしょうか？
今後も特定のNFTやコントラクトをピックアップしてまとめて行きたいと思います。

普段はブログやQiitaでブロックチェーンやAIに関する記事を挙げているので、よければ見ていってください！

https://chaldene.net/

https://qiita.com/cardene
