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

> UniversalRouterコントラクトは、私たちのdAppエコシステムにおける重要な要素です。このコントラクトは、プラットフォーム内で発生するさまざまなトランザクションの中枢として機能します。

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

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

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

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

https://www.bunzz.dev/decipher

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

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

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

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

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

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

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

# 概要

**UniversalRouter**コントラクトは、私たちのdAppエコシステムにおける重要な要素です。
このコントラクトは、プラットフォーム内で発生するさまざまなトランザクションの中枢として機能します。
具体的には、スワップ、支払い、報酬の収集など、異なるモジュールや外部プロトコルを横断する多様なトランザクションをスムーズに処理する役割を果たします。

## 目的
ユーザーに統一されたエクスペリエンスを提供することです。
ユーザーは様々なプロトコルやモジュールと簡単にやりとりでき、トークンのスワップ、支払い、報酬の収集などの複雑なアクションを円滑かつ効率的に実行できます。

:::message
ユーザーに統一されたエクスペリエンスを提供することは、ユーザーが**Uniswap**のdAppエコシステム内で異なるプロトコルやモジュールと対話する際に、一貫性のある操作や体験を提供することを意味します。

具体的には、ユーザーがトークンのスワップ、支払い、報酬の収集などの複雑なアクションを実行する際に、どのプロトコルやモジュールを使用すればいいのかを迷うことなく、簡単かつ円滑に行うことができるようになります。
ユーザーが異なるアクションを実行するために、別々のツールや手順を学ぶ必要はありません。
代わりに、**UniversalRouter**コントラクトが異なるアクションを適切な場所に導いてくれます。

この統一されたエクスペリエンスにより、ユーザーは技術的な詳細や違い、異なるアクションがどのように実行されるかを気にすることなく、dAppエコシステム内で必要なアクションを選択し、効率的に処理することができます。
:::

## 責任

### トランザクションのルーティング
異なるタイプのトランザクションを対応するモジュールやプロトコルに効率的に接続します。
特定のモジュールやプロトコルに関係なく、すべてのトランザクションが正確に安全に実行されることを確認します。

:::message
トランザクションのルーティングは、異なる種類のトランザクションを、それに適したモジュールやプロトコルにスムーズに繋ぐプロセスです。
例えば、トークンのスワップや支払い、報酬の収集などの異なるアクションは、それぞれ異なるモジュールやプロトコルで処理される場合があります。

このプロセスは特定のモジュールやプロトコルに依存せず、すべてのトランザクションが適切な方法で処理されることを確保します。
例えば、トークンのスワップが必要な場合、それはスワップ専用のモジュールにルーティングされ、支払いが必要な場合は支払い専用のモジュールにルーティングされます。
これにより、ユーザーが特定のアクションを実行する際に、どのモジュールやプロトコルを使用すればいいのかを気にすることなく、スムーズに処理できるようになります。

重要なのは、これらのトランザクションが正確で安全に実行されることです。
**UniversalRouter**は、各トランザクションを適切なモジュールやプロトコルに正確に導き、実行が間違いなく行われるようにします。
このプロセスによって、ユーザーはトランザクションの種類にかかわらず、信頼性の高い処理を期待できます。
:::

### インターオペラビリティ
さまざまなモジュールやプロトコル間の相互運用性を促進することで、ユーザーが技術的な詳細を理解する必要なく、dAppエコシステムの異なるコンポーネントと簡単に対話できるようにします。

:::message
インターオペラビリティは、異なるモジュールやプロトコルが協力して動作し、シームレスな連携を可能にする概念です。
これにより、ユーザーは技術的な詳細を知る必要なく、dAppエコシステム内の異なるコンポーネントと容易にコミュニケーションを取ることができます。

具体的には、異なるプロトコルやモジュールが互いに協力して動作するため、ユーザーはそれぞれの詳細や動作について深く理解する必要がありません。
**UniversalRouter**コントラクトを介して、これらの異なるコンポーネントが統合され、ユーザーは単一のインターフェースを通じて複雑なアクションを実行できるようになります。

例えば、トークンのスワップを考えてみましょう。
このアクションには複数のステップやプロトコルが関与する場合がありますが、ユーザーはその詳細を理解する必要はありません。
**UniversalRouter**コントラクトが異なるプロトコルを調整し、ユーザーが単にスワップしたいトークンを指定するだけで、適切なプロトコルが選択されて実行されます。

インターオペラビリティのおかげで、ユーザーは複雑なプロトコルの背後にある技術的な側面を理解することなく、dAppエコシステム内で自由に操作できるようになります。
この一貫性と簡便さによって、ユーザーの体験が向上し、技術的な壁を乗り越えることなく、異なるコンポーネントとやりとりできるようになります。
:::

### トランザクションの検証
トランザクションが必要な条件を満たしているかを確認し、トークンの残高、資金の適切な確認、ユーザーの許可などを検証します。
これにより、不正なトランザクションが実行されるのを防ぎます。

### イベントの記録
重要なイベントやトランザクションの詳細を記録し、透明性と監査可能性を確保します。
ユーザーや開発者はトランザクションの実行状況を確認し、エコシステムの信頼性を保つためにこれらの情報を使用できます。

### エラーハンドリング
トランザクションの実行中に問題が発生した場合、エラーを適切に処理してユーザーに適切なフィードバックを提供します。
これにより、エクスペリエンスの中断や資金の損失を最小限に抑えます。

:::message
エラーハンドリングは、トランザクションの実行中に問題やエラーが発生した場合に、そのエラーを適切に取り扱い、ユーザーに適切な情報やフィードバックを提供するプロセスです。

トランザクションの実行中には、さまざまな理由でエラーが発生する可能性があります。
例えば、トランザクションが不正確な情報を含んでいる場合や、必要な条件が満たされていない場合などが考えられます。
エラーハンドリングは、こうした状況に対処するための仕組みを提供します。

エラーハンドリングの目的は、エラーが発生したときにユーザーに適切なフィードバックを提供することです。
これにより、ユーザーは何が問題であるかを理解し、適切なアクションを取ることができます。
例えば、トークンのスワップが失敗した場合、エラーメッセージや具体的な指示を通じて、ユーザーが何を修正すべきかを知ることができます。

また、エラーハンドリングはエクスペリエンスの中断や資金の損失を最小限に抑えるためにも重要です。
エラーが適切に処理されない場合、ユーザーはトランザクションが失敗したり、資金が不正確に処理されたりする可能性があります。これによってユーザーの信頼性が損なわれ、エコシステム全体の信頼性も低下することがあります。
エラーハンドリングは、これらのリスクを軽減し、ユーザーとエコシステムの両方にとって良好な体験を確保する役割を果たします。
:::

**UniversalRouter**コントラクトは私たちのdAppエコシステム内でトランザクションを円滑かつ安全に実行するための重要な要素です。
ユーザーはさまざまなモジュールやプロトコルと対話することができ、同時に効率性、相互運用性、セキュリティを確保します。

# 使い方

**UniversalRouter**コントラクトは、異なるプロトコルでさまざまな種類のトランザクションを実行するためのユニバーサルなルーターとして機能するスマートコントラクトです。
このコントラクトは、異なるプロトコルとのやりとりに単一のインターフェースを提供し、指定されたプロトコルに基づいてトランザクションのルーティングを処理します。

## コントラクトの目的
**UniversalRouter**コントラクトの目的は、異なるプロトコルでトランザクションを実行するための統一されたインターフェースを提供することです。
このコントラクトを使用することで、開発者は単一のコントラクトを介して複数のプロトコルとやりとりできるため、異なるプロトコルと個別にやりとりする複雑さを軽減し、統合プロセスを簡素化できます。

## 利用方法
コントラクトの目標を達成するために、以下のステップを実行します。

1. **UniversalRouter**コントラクトをデプロイします。
2. コントラクトを必要なパラメータで初期化します。
3. ルート関数を呼び出して異なるプロトコル上でトランザクションを実行します。

### UniversalRouterコントラクトのデプロイ
**UniversalRouter**コントラクトをデプロイするには、次の手順に従います。

1. `UniversalRouter.sol`コントラクトをコンパイルします。
2. コンパイルされたコントラクトを希望するブロックチェーンネットワークにデプロイします。

### コントラクトの初期化
**UniversalRouter**コントラクトは、次のパラメータで初期化できます。

- `dispatcher`
	- **Dispatcher**コントラクトのアドレス。
- `rewardsCollector`
	- **RewardsCollector**コントラクトのアドレス。
- `routerImmutables`
	- **RouterImmutables**コントラクトのアドレス。

### トランザクションの実行
異なるプロトコルでトランザクションを実行するには、`route`関数を呼び出します。
`route`関数は次のパラメータを受け取ります。

- `command`
	- 指定されたプロトコルで実行されるコマンド。
- `data`
	- コマンドの実行に必要なデータ。
- `path`
	- コマンドを実行するために使用するプロトコルのパス。

`route`関数は以下のステップを実行します。

1. コマンドとデータを検証します。
2. 指定されたパスに基づいてプロトコルの実装アドレスを取得します。
3. プロトコルの実装コントラクトの`execute`関数をコマンドとデータと共に呼び出します。

## 関数
**UniversalRouter**コントラクトは以下の関数を提供します。

- `constructor`
	- 指定されたパラメータでコントラクトを初期化します。
- `route`
	- 指定されたコマンド、データ、およびパスに基づいて、異なるプロトコル上でトランザクションを実行します。

## イベント
**UniversalRouter**コントラクトは以下のイベントを発行します。

- `RouteExecuted`
	- トランザクションがプロトコル上で正常に実行されたときに発行されます。
	- 以下の情報を含みます。
		- `protocol`
			- トランザクションが実行されたプロトコル。
		- `command`
			- 実行されたコマンド。
		- `data`
			- 実行されたデータ。

## 関連EIP/ERC

- ERC721
- ERC1155
- ERC20
- IERC721Receiver
- IERC1155Receiver
- IERC20
- IUniswapV2Pair
- IUniswapV3Pool
- IUniswapV3SwapCallback
- IERC165
- IAllowanceTransfer
- ICryptoPunksMarket
- IWETH9

## 参考

- UniversalRouter.sol
- Dispatcher.sol
- RewardsCollector.sol
- RouterImmutables.sol
- Commands.sol
- IUniversalRouter.sol
- V2SwapRouter.sol
- V3SwapRouter.sol
- BytesLib.sol
- Payments.sol
- Callbacks.sol
- LockAndMsgSender.sol
- ERC721.sol
- ERC1155.sol
- ERC20.sol
- IAllowanceTransfer.sol
- ICryptoPunksMarket.sol
- SafeTransferLib.sol
- IRewardsCollector.sol
- IWETH9.sol
- IERC721Receiver.sol
- IERC1155Receiver.sol
- IUniswapV2Pair.sol
- UniswapV2Library.sol
- Permit2Payments.sol
- Constants.sol
- V3Path.sol
- SafeCast.sol
- IUniswapV3Pool.sol
- IUniswapV3SwapCallback.sol
- IERC165.sol
- IERC20.sol
- SafeCast160.sol
- IUniswapV3PoolImmutables.sol
- IUniswapV3PoolState.sol
- IUniswapV3PoolDerivedState.sol
- IUniswapV3PoolActions.sol
- IUniswapV3PoolOwnerActions.sol
- IUniswapV3PoolEvents.sol

# パラメータ

以下は、各項目をリストではなくh3の見出しで示したものです。

### permit2
permit2コントラクトのアドレス。

### weth9
weth9コントラクトのアドレス。

### seaportV1_5
seaportV1_5コントラクトのアドレス。

### seaportV1_4
seaportV1_4コントラクトのアドレス。

### openseaConduit
openseaConduitコントラクトのアドレス。

### nftxZap
nftxZapコントラクトのアドレス。

### x2y2
x2y2コントラクトのアドレス。

### foundation
foundationコントラクトのアドレス。

### sudoswap
sudoswapコントラクトのアドレス。

### elementMarket
elementMarketコントラクトのアドレス。

### nft20Zap
nft20Zapコントラクトのアドレス。

### cryptopunks
cryptopunksコントラクトのアドレス。

### looksRareV2
looksRareV2コントラクトのアドレス。

### routerRewardsDistributor
routerRewardsDistributorコントラクトのアドレス。

### looksRareRewardsDistributor
looksRareRewardsDistributorコントラクトのアドレス。

### looksRareToken
looksRareTokenコントラクトのアドレス。

### v2Factory
v2Factoryコントラクトのアドレス。

### v3Factory
v3Factoryコントラクトのアドレス。

### pairInitCodeHash
ペア初期化コードのハッシュコード。

### poolInitCodeHash
プール初期化コードのハッシュコード。

# コントラクト

## UniversalRouter

### RouterImmutables

**概要**
**RouterImmutables**はコントラクトの初期化パラメータを保持するためのコントラクト。

**詳細**
**RouterImmutables**は**UniversalRouter**コントラクトの初期化時に使用されます。
コントラクトのパラメータを設定し、後続のトランザクション実行においてこれらのパラメータを参照する役割を果たします。

**引数**
- `params`
	- **RouterParameters**構造体型の初期化パラメータです。
	- これによってコントラクトの設定が行われます。

---

### execute

:::details execute
```solidity
function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline)
        external
        payable
        checkDeadline(deadline)
    {
        execute(commands, inputs);
}
```
:::

**概要**
トランザクションを実行する関数。
指定されたコマンドと入力に基づいてトランザクションを実行します。

**詳細**
**IUniversalRouter**インターフェースに準拠し、指定されたコマンドと入力を使用してトランザクションを実行します。
また、指定された締め切り時間を超えて実行しようとすると、エラーが発生します。

**引数**
- `commands`
	- 実行するコマンドのバイトデータ配列。
- `inputs`
	- 各コマンドに対する入力のバイトデータ配列。
- `deadline`
	- トランザクションの締め切り時間を表すタイムスタンプ。

**戻り値**
なし

---

### execute (override)

:::details execute
```solidity
function execute(bytes calldata commands, bytes[] calldata inputs) public payable override isNotLocked {
        bool success;
        bytes memory output;
        uint256 numCommands = commands.length;
        if (inputs.length != numCommands) revert LengthMismatch();

        // loop through all given commands, execute them and pass along outputs as defined
        for (uint256 commandIndex = 0; commandIndex < numCommands;) {
            bytes1 command = commands[commandIndex];

            bytes calldata input = inputs[commandIndex];

            (success, output) = dispatch(command, input);

            if (!success && successRequired(command)) {
                revert ExecutionFailed({commandIndex: commandIndex, message: output});
            }

            unchecked {
                commandIndex++;
            }
        }
}
```
:::

**概要**
`execute`関数のオーバーロードで、デッドラインのチェックを行わないバージョン。

**詳細**
`execute`関数のオーバーロードであり、締め切り時間のチェックを行わないバージョンです。
内部で同じ名前の`execute`関数を呼び出してトランザクションを実行します。

**引数**
- `commands`
	- 実行するコマンドのバイトデータ配列。
- `inputs`
	- 各コマンドに対する入力のバイトデータ配列。

**戻り値**
なし

---

### successRequired

:::details successRequired
```solidity
function successRequired(bytes1 command) internal pure returns (bool) {
        return command & Commands.FLAG_ALLOW_REVERT == 0;
}
```
:::

**概要**
特定のコマンドに対して実行の結果が成功である必要があるかどうかを判定する関数。

**詳細**
コマンドが`FLAG_ALLOW_REVERT`を含まない場合は成功する必要があります。

**引数**
- `command`
	- 判定対象のコマンドバイトデータです。

**戻り値**
- `bool`
	- 成功が必要かどうかを示す真偽値です。

:::message
`successRequired`関数は、特定のコマンドが実行された際に処理結果が成功である必要があるかどうかを判定するための関数です。

コマンドが「`FLAG_ALLOW_REVERT`」という特定のフラグを含まない場合、つまり失敗してもリバート（状態を元に戻すこと）を許可しない場合、この関数は「**成功である必要がある**」を示す真偽値（`true`）を返します。
この判定は、トランザクションの実行において成功と失敗のハンドリング方法を制御するために使用されます。

例えば、あるトランザクションがトークンを移動するコマンドを含む場合を考えてみましょう。
このトランザクションが成功すればトークンが移動し、失敗すればトークンは移動しません。
ここで、このコマンドの成功が必要かどうかを判断するために`successRequired`関数が使用されます。

もしもコマンドに「`FLAG_ALLOW_REVERT`」フラグが含まれている場合、つまりトークン移動が失敗しても`revert`（元に戻すこと）が可能な場合、`successRequired`関数は「**成功が必要でない**」と判断します。
なぜなら、トークン移動が失敗した場合でも、トランザクションはリバートによって元の状態に戻るからです。

一方で、コマンドに「`FLAG_ALLOW_REVERT`」フラグが含まれていない場合、つまりトークン移動が失敗した場合にはリバートが許可されず、状態が変更される場合、`successRequired`関数は「**成功が必要**」と判断します。
なぜなら、トークン移動が失敗した場合には元の状態に戻せないため、トランザクションの実行が必ず成功する必要があるからです。

このように、`successRequired`関数はコマンドの性質に基づいて、トランザクションの実行結果が成功でなければならないかどうかを判断し、トランザクションのハンドリング方法を制御します。
:::

---

### receive

:::details receive
```solidity
receive() external payable {}
```
:::

**概要**
**WETH**やNFTプロトコルから**ETH**を受け取るための関数。

**詳細**
外部から**ETH**を受け取る際に呼び出されます。
**WETH**やNFTプロトコルからの**ETH**受け取りに使用されます。

## Dispatcher

複数の異なるコマンドを実行し、その結果を扱うためのコントラクト。
異なるコマンドのタイプに応じて、該当する関数を呼び出し、必要な引数やデータを処理します。
複数の異なるスマートコントラクトとやりとりするための中継役を果たします。
コマンドをデコードし、それぞれのコマンドタイプに対応する関数を呼び出すことで、異なる操作を実現しています。

**パラメータ**
- `commands`
	-  実行するコマンドのバイト列。
	-  各コマンドは1バイトで表されます。
- `inputs`
	- 各コマンドに対する入力データのバイト列の配列。

**戻り値**
なし

---

### dispatch

:::details dispatch
```solidity
function dispatch(bytes1 commandType, bytes calldata inputs) internal returns (bool success, bytes memory output) {
        uint256 command = uint8(commandType & Commands.COMMAND_TYPE_MASK);

        success = true;

        if (command < Commands.FOURTH_IF_BOUNDARY) {
            if (command < Commands.SECOND_IF_BOUNDARY) {
                // 0x00 <= command < 0x08
                if (command < Commands.FIRST_IF_BOUNDARY) {
                    if (command == Commands.V3_SWAP_EXACT_IN) {
                        // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool))
                        address recipient;
                        uint256 amountIn;
                        uint256 amountOutMin;
                        bool payerIsUser;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amountIn := calldataload(add(inputs.offset, 0x20))
                            amountOutMin := calldataload(add(inputs.offset, 0x40))
                            // 0x60 offset is the path, decoded below
                            payerIsUser := calldataload(add(inputs.offset, 0x80))
                        }
                        bytes calldata path = inputs.toBytes(3);
                        address payer = payerIsUser ? lockedBy : address(this);
                        v3SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer);
                    } else if (command == Commands.V3_SWAP_EXACT_OUT) {
                        // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool))
                        address recipient;
                        uint256 amountOut;
                        uint256 amountInMax;
                        bool payerIsUser;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amountOut := calldataload(add(inputs.offset, 0x20))
                            amountInMax := calldataload(add(inputs.offset, 0x40))
                            // 0x60 offset is the path, decoded below
                            payerIsUser := calldataload(add(inputs.offset, 0x80))
                        }
                        bytes calldata path = inputs.toBytes(3);
                        address payer = payerIsUser ? lockedBy : address(this);
                        v3SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer);
                    } else if (command == Commands.PERMIT2_TRANSFER_FROM) {
                        // equivalent: abi.decode(inputs, (address, address, uint160))
                        address token;
                        address recipient;
                        uint160 amount;
                        assembly {
                            token := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            amount := calldataload(add(inputs.offset, 0x40))
                        }
                        permit2TransferFrom(token, lockedBy, map(recipient), amount);
                    } else if (command == Commands.PERMIT2_PERMIT_BATCH) {
                        (IAllowanceTransfer.PermitBatch memory permitBatch,) =
                            abi.decode(inputs, (IAllowanceTransfer.PermitBatch, bytes));
                        bytes calldata data = inputs.toBytes(1);
                        PERMIT2.permit(lockedBy, permitBatch, data);
                    } else if (command == Commands.SWEEP) {
                        // equivalent:  abi.decode(inputs, (address, address, uint256))
                        address token;
                        address recipient;
                        uint160 amountMin;
                        assembly {
                            token := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            amountMin := calldataload(add(inputs.offset, 0x40))
                        }
                        Payments.sweep(token, map(recipient), amountMin);
                    } else if (command == Commands.TRANSFER) {
                        // equivalent:  abi.decode(inputs, (address, address, uint256))
                        address token;
                        address recipient;
                        uint256 value;
                        assembly {
                            token := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            value := calldataload(add(inputs.offset, 0x40))
                        }
                        Payments.pay(token, map(recipient), value);
                    } else if (command == Commands.PAY_PORTION) {
                        // equivalent:  abi.decode(inputs, (address, address, uint256))
                        address token;
                        address recipient;
                        uint256 bips;
                        assembly {
                            token := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            bips := calldataload(add(inputs.offset, 0x40))
                        }
                        Payments.payPortion(token, map(recipient), bips);
                    } else {
                        // placeholder area for command 0x07
                        revert InvalidCommandType(command);
                    }
                    // 0x08 <= command < 0x10
                } else {
                    if (command == Commands.V2_SWAP_EXACT_IN) {
                        // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool))
                        address recipient;
                        uint256 amountIn;
                        uint256 amountOutMin;
                        bool payerIsUser;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amountIn := calldataload(add(inputs.offset, 0x20))
                            amountOutMin := calldataload(add(inputs.offset, 0x40))
                            // 0x60 offset is the path, decoded below
                            payerIsUser := calldataload(add(inputs.offset, 0x80))
                        }
                        address[] calldata path = inputs.toAddressArray(3);
                        address payer = payerIsUser ? lockedBy : address(this);
                        v2SwapExactInput(map(recipient), amountIn, amountOutMin, path, payer);
                    } else if (command == Commands.V2_SWAP_EXACT_OUT) {
                        // equivalent: abi.decode(inputs, (address, uint256, uint256, bytes, bool))
                        address recipient;
                        uint256 amountOut;
                        uint256 amountInMax;
                        bool payerIsUser;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amountOut := calldataload(add(inputs.offset, 0x20))
                            amountInMax := calldataload(add(inputs.offset, 0x40))
                            // 0x60 offset is the path, decoded below
                            payerIsUser := calldataload(add(inputs.offset, 0x80))
                        }
                        address[] calldata path = inputs.toAddressArray(3);
                        address payer = payerIsUser ? lockedBy : address(this);
                        v2SwapExactOutput(map(recipient), amountOut, amountInMax, path, payer);
                    } else if (command == Commands.PERMIT2_PERMIT) {
                        // equivalent: abi.decode(inputs, (IAllowanceTransfer.PermitSingle, bytes))
                        IAllowanceTransfer.PermitSingle calldata permitSingle;
                        assembly {
                            permitSingle := inputs.offset
                        }
                        bytes calldata data = inputs.toBytes(6); // PermitSingle takes first 6 slots (0..5)
                        PERMIT2.permit(lockedBy, permitSingle, data);
                    } else if (command == Commands.WRAP_ETH) {
                        // equivalent: abi.decode(inputs, (address, uint256))
                        address recipient;
                        uint256 amountMin;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amountMin := calldataload(add(inputs.offset, 0x20))
                        }
                        Payments.wrapETH(map(recipient), amountMin);
                    } else if (command == Commands.UNWRAP_WETH) {
                        // equivalent: abi.decode(inputs, (address, uint256))
                        address recipient;
                        uint256 amountMin;
                        assembly {
                            recipient := calldataload(inputs.offset)
                            amountMin := calldataload(add(inputs.offset, 0x20))
                        }
                        Payments.unwrapWETH9(map(recipient), amountMin);
                    } else if (command == Commands.PERMIT2_TRANSFER_FROM_BATCH) {
                        (IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) =
                            abi.decode(inputs, (IAllowanceTransfer.AllowanceTransferDetails[]));
                        permit2TransferFrom(batchDetails, lockedBy);
                    } else if (command == Commands.BALANCE_CHECK_ERC20) {
                        // equivalent: abi.decode(inputs, (address, address, uint256))
                        address owner;
                        address token;
                        uint256 minBalance;
                        assembly {
                            owner := calldataload(inputs.offset)
                            token := calldataload(add(inputs.offset, 0x20))
                            minBalance := calldataload(add(inputs.offset, 0x40))
                        }
                        success = (ERC20(token).balanceOf(owner) >= minBalance);
                        if (!success) output = abi.encodePacked(BalanceTooLow.selector);
                    } else {
                        // placeholder area for command 0x0f
                        revert InvalidCommandType(command);
                    }
                }
                // 0x10 <= command
            } else {
                // 0x10 <= command < 0x18
                if (command < Commands.THIRD_IF_BOUNDARY) {
                    if (command == Commands.SEAPORT_V1_5) {
                        /// @dev Seaport 1.4 and 1.5 allow for orders to be created by contracts.
                        ///     These orders pass control to the contract offerers during fufillment,
                        ///         allowing them to perform any number of destructive actions as a holder of the NFT.
                        ///     Integrators should be aware that in some scenarios: e.g. purchasing an NFT that allows the holder
                        ///         to claim another NFT, the contract offerer can "steal" the claim during order fufillment.
                        ///     For some such purchases, an OWNER_CHECK command can be prepended to ensure that all tokens have the desired owner at the end of the transaction.
                        ///     This is also outlined in the Seaport documentation: https://github.com/ProjectOpenSea/seaport/blob/main/docs/SeaportDocumentation.md
                        (uint256 value, bytes calldata data) = getValueAndData(inputs);
                        (success, output) = SEAPORT_V1_5.call{value: value}(data);
                    } else if (command == Commands.LOOKS_RARE_V2) {
                        // equivalent: abi.decode(inputs, (uint256, bytes))
                        uint256 value;
                        assembly {
                            value := calldataload(inputs.offset)
                        }
                        bytes calldata data = inputs.toBytes(1);
                        (success, output) = LOOKS_RARE_V2.call{value: value}(data);
                    } else if (command == Commands.NFTX) {
                        // equivalent: abi.decode(inputs, (uint256, bytes))
                        (uint256 value, bytes calldata data) = getValueAndData(inputs);
                        (success, output) = NFTX_ZAP.call{value: value}(data);
                    } else if (command == Commands.CRYPTOPUNKS) {
                        // equivalent: abi.decode(inputs, (uint256, address, uint256))
                        uint256 punkId;
                        address recipient;
                        uint256 value;
                        assembly {
                            punkId := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            value := calldataload(add(inputs.offset, 0x40))
                        }
                        (success, output) = CRYPTOPUNKS.call{value: value}(
                            abi.encodeWithSelector(ICryptoPunksMarket.buyPunk.selector, punkId)
                        );
                        if (success) ICryptoPunksMarket(CRYPTOPUNKS).transferPunk(map(recipient), punkId);
                        else output = abi.encodePacked(BuyPunkFailed.selector);
                    } else if (command == Commands.OWNER_CHECK_721) {
                        // equivalent: abi.decode(inputs, (address, address, uint256))
                        address owner;
                        address token;
                        uint256 id;
                        assembly {
                            owner := calldataload(inputs.offset)
                            token := calldataload(add(inputs.offset, 0x20))
                            id := calldataload(add(inputs.offset, 0x40))
                        }
                        success = (ERC721(token).ownerOf(id) == owner);
                        if (!success) output = abi.encodePacked(InvalidOwnerERC721.selector);
                    } else if (command == Commands.OWNER_CHECK_1155) {
                        // equivalent: abi.decode(inputs, (address, address, uint256, uint256))
                        address owner;
                        address token;
                        uint256 id;
                        uint256 minBalance;
                        assembly {
                            owner := calldataload(inputs.offset)
                            token := calldataload(add(inputs.offset, 0x20))
                            id := calldataload(add(inputs.offset, 0x40))
                            minBalance := calldataload(add(inputs.offset, 0x60))
                        }
                        success = (ERC1155(token).balanceOf(owner, id) >= minBalance);
                        if (!success) output = abi.encodePacked(InvalidOwnerERC1155.selector);
                    } else if (command == Commands.SWEEP_ERC721) {
                        // equivalent: abi.decode(inputs, (address, address, uint256))
                        address token;
                        address recipient;
                        uint256 id;
                        assembly {
                            token := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            id := calldataload(add(inputs.offset, 0x40))
                        }
                        Payments.sweepERC721(token, map(recipient), id);
                    }
                    // 0x18 <= command < 0x1f
                } else {
                    if (command == Commands.X2Y2_721) {
                        (success, output) = callAndTransfer721(inputs, X2Y2);
                    } else if (command == Commands.SUDOSWAP) {
                        // equivalent: abi.decode(inputs, (uint256, bytes))
                        (uint256 value, bytes calldata data) = getValueAndData(inputs);
                        (success, output) = SUDOSWAP.call{value: value}(data);
                    } else if (command == Commands.NFT20) {
                        // equivalent: abi.decode(inputs, (uint256, bytes))
                        (uint256 value, bytes calldata data) = getValueAndData(inputs);
                        (success, output) = NFT20_ZAP.call{value: value}(data);
                    } else if (command == Commands.X2Y2_1155) {
                        (success, output) = callAndTransfer1155(inputs, X2Y2);
                    } else if (command == Commands.FOUNDATION) {
                        (success, output) = callAndTransfer721(inputs, FOUNDATION);
                    } else if (command == Commands.SWEEP_ERC1155) {
                        // equivalent: abi.decode(inputs, (address, address, uint256, uint256))
                        address token;
                        address recipient;
                        uint256 id;
                        uint256 amount;
                        assembly {
                            token := calldataload(inputs.offset)
                            recipient := calldataload(add(inputs.offset, 0x20))
                            id := calldataload(add(inputs.offset, 0x40))
                            amount := calldataload(add(inputs.offset, 0x60))
                        }
                        Payments.sweepERC1155(token, map(recipient), id, amount);
                    } else if (command == Commands.ELEMENT_MARKET) {
                        // equivalent: abi.decode(inputs, (uint256, bytes))
                        (uint256 value, bytes calldata data) = getValueAndData(inputs);
                        (success, output) = ELEMENT_MARKET.call{value: value}(data);
                    } else {
                        // placeholder for command 0x1f
                        revert InvalidCommandType(command);
                    }
                }
            }
            // 0x20 <= command
        } else {
            if (command == Commands.SEAPORT_V1_4) {
                /// @dev Seaport 1.4 and 1.5 allow for orders to be created by contracts.
                ///     These orders pass control to the contract offerers during fufillment,
                ///         allowing them to perform any number of destructive actions as a holder of the NFT.
                ///     Integrators should be aware that in some scenarios: e.g. purchasing an NFT that allows the holder
                ///         to claim another NFT, the contract offerer can "steal" the claim during order fufillment.
                ///     For some such purchases, an OWNER_CHECK command can be prepended to ensure that all tokens have the desired owner at the end of the transaction.
                ///     This is also outlined in the Seaport documentation: https://github.com/ProjectOpenSea/seaport/blob/main/docs/SeaportDocumentation.md
                (uint256 value, bytes calldata data) = getValueAndData(inputs);
                (success, output) = SEAPORT_V1_4.call{value: value}(data);
            } else if (command == Commands.EXECUTE_SUB_PLAN) {
                bytes calldata _commands = inputs.toBytes(0);
                bytes[] calldata _inputs = inputs.toBytesArray(1);
                (success, output) =
                    (address(this)).call(abi.encodeWithSelector(Dispatcher.execute.selector, _commands, _inputs));
            } else if (command == Commands.APPROVE_ERC20) {
                ERC20 token;
                RouterImmutables.Spenders spender;
                assembly {
                    token := calldataload(inputs.offset)
                    spender := calldataload(add(inputs.offset, 0x20))
                }
                Payments.approveERC20(token, spender);
            } else {
                // placeholder area for commands 0x22-0x3f
                revert InvalidCommandType(command);
            }
        }
}
```
:::

**概要**
指定されたコマンドタイプに基づいて、対応する処理を実行する関数。
デコードされたコマンドタイプによって異なる操作を行います。

**詳細**
与えられたコマンドタイプに基づいて、対応する処理を選択し、その処理を実行します。
コマンドタイプに応じて、異なる引数をデコードして関数に渡します。

**引数 or パラメータ**
- `commandType`
	- 実行するコマンドのタイプを示す1バイトの値。
- `inputs`
	- コマンドの実行に必要な引数やデータのバイト列。

**戻り値**
- `success`
	- コマンドの実行が成功した場合に`true`、失敗した場合に`false`を返します。
- `output`
	- コマンドの実行結果やエラーメッセージなどが格納されたバイト列を返します。

---

### execute

:::details execute
```solidity
function execute(bytes calldata commands, bytes[] calldata inputs) external payable virtual;
```
:::

**概要**
エンコードされたコマンドとそれに対応する入力データを実行する関数。
複数のコマンドと対応する入力データが提供され、それぞれのコマンドを順番に実行します。

**詳細**
エンコードされたコマンドとそれに対応する入力データを受け取り、それぞれのコマンドを順番に実行します。
コマンドの実行結果やエラーメッセージは、個別のコマンドごとに戻り値として提供されます。

**引数**
- `commands`
	- 連結されたコマンドのバイト列。
	- 各コマンドは1バイトの長さです。
- `inputs`
	- 各コマンドに対するABIエンコードされた入力データのバイト列の配列。

**戻り値**
なし

---

### callAndTransfer721

:::details callAndTransfer721
```solidity
function callAndTransfer721(bytes calldata inputs, address protocol)
        internal
        returns (bool success, bytes memory output)
    {
        // equivalent: abi.decode(inputs, (uint256, bytes, address, address, uint256))
        (uint256 value, bytes calldata data) = getValueAndData(inputs);
        address recipient;
        address token;
        uint256 id;
        assembly {
            // 0x00 and 0x20 offsets are value and data, above
            recipient := calldataload(add(inputs.offset, 0x40))
            token := calldataload(add(inputs.offset, 0x60))
            id := calldataload(add(inputs.offset, 0x80))
        }
        (success, output) = protocol.call{value: value}(data);
        if (success) ERC721(token).safeTransferFrom(address(this), map(recipient), id);
}
```
:::

**概要**
指定されたプロトコルを呼び出し、それによって購入された**ERC721**トークンを指定された受取アドレスに転送する関数。

**詳細**
指定されたプロトコルを呼び出して購入処理を行い、その後、購入された**ERC721**トークンを指定された受取アドレスに転送します。
関数内部で、受取アドレス、トークンアドレス、トークンIDなどの情報をデコードし、トランザクションを実行します。

**引数**
- `inputs`
	- プロトコルと**ERC721**トークンの転送に関する入力データがエンコードされたバイト列。
- `protocol`
	- コールデータを渡すためのプロトコルのアドレス。

**戻り値**
- `success`
	- コマンドの実行が成功した場合に`true`、失敗した場合に`false`を返します。
- `output`
	- コマンドの実行結果やエラーメッセージなどが格納されたバイト列を返します。

:::message
`callAndTransfer721`関数は、**ERC721**トークンを購入して取引を実行し、その後トークンを指定した受取アドレスに転送する関数です。

例えば、以下のようなコマンドが与えられたとします：

```solidity
callAndTransfer721(
    inputs,
    protocolAddress
);
```

ここで、`inputs`はABIエンコードされたバイト列で、購入に関する情報が含まれています。
`protocolAddress`は取引のプロトコルを指定するためのアドレスです。

`callAndTransfer721`関数内の処理は次のようになります。

1. `inputs`のデコード
`inputs`から必要な情報をデコードします。
この情報には、受取アドレス、トークンアドレス、トークンのIDなどが含まれます。

2. 取引の実行
`protocolAddress`に対して取引を実行するために、コールを行います。
必要な金額も同時に送信されます。

3. 取引成功の判定
取引の実行結果を確認し、成功したかどうかを判定します。

4. トークンの転送:
取引が成功した場合、購入した**ERC721**トークンを現在のコントラクトから指定した受取アドレスに転送します。

この関数は、購入とトークンの転送を一連のステップで行うためのものです。
具体的なコードやABIエンコードの内容によって、取引の詳細やトークンの種類などが変わりますが、基本的なプロセスは上記のような流れです。
:::

---

### callAndTransfer1155

:::details callAndTransfer1155
```solidity
function callAndTransfer1155(bytes calldata inputs, address protocol)
        internal
        returns (bool success, bytes memory output)
    {
        // equivalent: abi.decode(inputs, (uint256, bytes, address, address, uint256, uint256))
        (uint256 value, bytes calldata data) = getValueAndData(inputs);
        address recipient;
        address token;
        uint256 id;
        uint256 amount;
        assembly {
            // 0x00 and 0x20 offsets are value and data, above
            recipient := calldataload(add(inputs.offset, 0x40))
            token := calldataload(add(inputs.offset, 0x60))
            id := calldataload(add(inputs.offset, 0x80))
            amount := calldataload(add(inputs.offset, 0xa0))
        }
        (success, output) = protocol.call{value: value}(data);
        if (success) ERC1155(token).safeTransferFrom(address(this), map(recipient), id, amount, new bytes(0));
}
```
:::

**概要**
指定されたプロトコルを呼び出し、それによって購入された**ERC1155**トークンを指定された受取アドレスに転送する関数。

**詳細**
指定されたプロトコルを呼び出して購入処理を行い、その後、購入された**ERC1155**トークンを指定された受取アドレスに転送します。
関数内部で、受取アドレス、トークンアドレス、トークンID、数量などの情報をデコードし、トランザクションを実行します。

**引数**
- `inputs`
	- プロトコルと**ERC1155**トークンの転送に関する入力データがエンコードされたバイト列。
- `protocol`
	- コールデータを渡すためのプロトコルのアドレス。

**戻り値**
- `success`
	- コマンドの実行が成功した場合に`true`、失敗した場合に`false`を返します。
- `output`
	- コマンドの実行結果やエラーメッセージなどが格納されたバイト列を返します。

### getValueAndData

:::details getValueAndData
```solidity
function getValueAndData(bytes calldata inputs) internal pure returns (uint256 value, bytes calldata data) {
        assembly {
            value := calldataload(inputs.offset)
        }
        data = inputs.toBytes(1);
}
```
:::

**概要**
入力データから`value`と`data`パラメータを抽出するためのヘルパー関数。

**詳細**
入力データから`value`と`data`の2つのパラメータを抽出します。
`value`は最初のパラメータとして、`data`は2番目のパラメータとして想定されています。

**引数 or パラメータ**
- `inputs`
	- `value`と`data`パラメータを含むバイト列。

**戻り値**
- `value`
	- 256ビットの整数値。
- `data`
	- データのバイト列。

:::message
`getValueAndData`関数は、与えられたバイト列から値とデータを取り出すための関数です。

例えば、以下のようなコードが与えられたとします：

```solidity
(uint256 value, bytes memory data) = getValueAndData(inputs);
```

ここで、`inputs`はABIエンコードされたバイト列です。

`getValueAndData`関数内の処理は次のようになります。

1. バイト列のデコード
`inputs`から値とデータの情報をデコードします。
この処理によって、バイト列の先頭にある値とデータが取得されます。

2. 値の取得
デコードされた情報から値（`value`）を取得します。
この値は通常、取引に使用するための金額などの数値データです。

3. データの取得
デコードされた情報からデータ（`data`）を取得します。
このデータは通常、取引の呼び出しに必要なコールデータです。
このデータはバイト列として表現され、トランザクションの呼び出しに使用されます。

4. 値とデータの返却
取得した値とデータを変数`value`と`data`に格納し、それらの変数を返します。

この関数を使用することで、与えられたバイト列から値とデータを正しく取り出すことができます。
具体的なコードやABIエンコードの内容によって、取得される値とデータの内容が変わりますが、基本的なプロセスは上記のような流れです。
:::

## RewardsCollector

### collectRewards

:::details collectRewards
```solidity
function collectRewards(bytes calldata looksRareClaim) external {
        (bool success,) = LOOKS_RARE_REWARDS_DISTRIBUTOR.call(looksRareClaim);
        if (!success) revert UnableToClaim();

        uint256 balance = LOOKS_RARE_TOKEN.balanceOf(address(this));
        LOOKS_RARE_TOKEN.transfer(ROUTER_REWARDS_DISTRIBUTOR, balance);
        emit RewardsSent(balance);
}
```
:::

**概要**
報酬を受け取り、転送する関数。
`LOOKS_RARE_REWARDS_DISTRIBUTOR`を呼び出して報酬を受け取り、`LOOKS_RARE_TOKEN`の残高を取得し、`ROUTER_REWARDS_DISTRIBUTOR`に転送します。
操作の成功や失敗に応じてイベントを発行します。

**詳細**
まず、`LOOKS_RARE_REWARDS_DISTRIBUTOR`の`call`関数を使用して`looksRareClaim`を実行し、報酬を受け取ります。
その後、`LOOKS_RARE_TOKEN`の残高を取得し、その残高を`ROUTER_REWARDS_DISTRIBUTOR`に転送します。
最後に、転送した報酬の量を`RewardsSent`イベントとして記録します。

**引数**
- `looksRareClaim`
	- 報酬を受け取るためのデータ。

**注意**
`revert UnableToClaim();`の部分は、`success`が`false`の場合、報酬の受け取りに失敗したことを示すエラーメッセージとして処理を中断します。

## Callbacks

### onERC721Received

:::details onERC721Received
```solidity
function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        return this.onERC721Received.selector;
}
```
:::

**概要**
**ERC721**トークンが転送されたときに呼び出される関数。
**ERC721**トークンを受け取るためのコントラクトで実装されます。

**詳細**
**ERC721**トークンがこのコントラクトに送信されたときに自動的に実行されます。
通常、トークンの受け入れに関する特定の処理やイベントの発行などが行われます。

**引数**
- `operator`
	- トークンを転送したオペレーターのアドレス。
- `from`
	- トークンを送信したアドレス。
- `tokenId`
	- 転送されたトークンのID。
- `data`
	- 任意のデータ。

**戻り値**
- `bytes4`
	- この関数が実装されていることを示す関数シグネチャ。

---

### onERC1155Received

:::details onERC1155Received
```solidity
function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) {
        return this.onERC1155Received.selector;
}
```
:::

**概要**
**ERC1155**トークンが転送されたときに呼び出される関数。
**ERC1155**トークンを受け取るためのコントラクトで実装されます。

**詳細**
**ERC1155**トークンがこのコントラクトに送信されたときに呼び出されます。
**ERC1155**トークンは、複数の異なるトークンIDと数量を含む場合があります。

**引数**
- `operator`
	- トークンを転送したオペレーターのアドレス。
- `from`
	- トークンを送信したアドレス。
- `id`
	- 転送されたトークンのID。
- `value`
	- 転送されたトークンの数量。
- `data`
	- 任意のデータ。

**戻り値**
- `bytes4`
	- この関数が実装されていることを示す関数シグネチャ。

---

### onERC1155BatchReceived

:::details onERC1155BatchReceived
```solidity

```
:::

**概要**
複数の**ERC1155**トークンがまとめて転送されたときに呼び出される関数。

**詳細**
複数の**ERC1155**トークンがこのコントラクトにまとめて送信されたときに呼び出されます。
それぞれのトークンは異なるトークンIDと数量を持つ可能性があります。

**引数**
- `operator`
	- トークンを転送したオペレーターのアドレス。
- `from`
	- トークンを送信したアドレス。
- `ids`
	- 転送されたトークンのIDの配列。
- `values`
	- 転送されたトークンの数量の配列。
- `data`
	- 任意のデータ。

**戻り値**
- `bytes4`
	- この関数が実装されていることを示す関数シグネチャ。

---

### supportsInterface

**概要**
指定されたインターフェースがサポートされているかどうかをチェックする関数。
**ERC165**のインターフェースIDを指定して、そのインターフェースがサポートされているかどうかを確認できます。

**詳細**
指定されたインターフェースIDがこのコントラクトがサポートしている**ERC165**のインターフェースIDと一致するかどうかを確認します。

**引数**
- `interfaceId`
	- 確認したいインターフェースのID。

**戻り値**
- `bool`
	- 指定されたインターフェースがサポートされている場合は`true`、そうでない場合は`false`を返す。

## LockAndMsgSender

ロック機構とメッセージ送信者に関する機能を提供するコントラクト。
外部からのアクセスを制限し、ロック状態でのコマンド実行を防ぐ仕組みを提供します。
また、メッセージ送信者に関する情報を管理し、特定のコマンドでその情報を使用します。

---

### ContractLocked

:::details ContractLocked
```solidity
error ContractLocked();
```
:::

**概要**
ロック状態のコントラクトで関数が呼び出された時に発生するエラー。
コントラクトがロックされている状態での操作を防ぐために使用されます。

**詳細**
このエラーは、`isNotLocked` 修飾子内でロック状態のチェックが行われ、ロックされている場合にこのエラーが発生します。
これにより、ロック中のコントラクトで関数が呼び出されるのを防ぎます。

---

### NOT_LOCKED_FLAG

:::details NOT_LOCKED_FLAG
```solidity
address internal constant NOT_LOCKED_FLAG = address(1);
```
:::

**概要**
ロック状態を示すフラグとして使用されるアドレス。

**詳細**
このアドレスは、コントラクトがロックされていない状態を示すために使用されます。
`lockedBy` 変数がこのアドレスでない場合、コントラクトはロックされているとみなされます。


---

### lockedBy

:::details lockedBy
```solidity
address internal lockedBy = NOT_LOCKED_FLAG;
```
:::

**概要**
コントラクトのロック状態を管理するためのアドレス変数。
この変数は、コントラクトがロックされているときにロックを行ったアドレスを保持します。

**詳細**
この変数は、`isNotLocked 修飾子内でのみ変更できます。
ロック状態の間、この変数にはロックを行ったアドレスが格納され、ロック解除後に `NOT_LOCKED_FLAG`に戻ります。

---

### isNotLocked

:::details isNotLocked
```solidity
modifier isNotLocked() {
        if (msg.sender != address(this)) {
            if (lockedBy != NOT_LOCKED_FLAG) revert ContractLocked();
            lockedBy = msg.sender;
            _;
            lockedBy = NOT_LOCKED_FLAG;
        } else {
            _;
        }
}
```
:::

**概要**
コントラクトがロックされていないことを確認する修飾子。
関数実行前にこの修飾子が適用され、ロック状態での関数実行を防ぎます。

**詳細**
この修飾子は、関数実行前にロック状態を確認し、ロック中の場合はエラーを発生させます。
ロックされていない場合、関数の実行を許可します。

---

### map

:::details map
```solidity
function map(address recipient) internal view returns (address) {
        if (recipient == Constants.MSG_SENDER) {
            return lockedBy;
        } else if (recipient == Constants.ADDRESS_THIS) {
            return address(this);
        } else {
            return recipient;
        }
}
```
:::

**概要**
コマンドの宛先アドレスを計算する関数。
特定のフラグやアドレスに基づいて、宛先アドレスを返します。

**詳細**
この関数は、`recipient`パラメータに基づいて宛先アドレスを計算します。`Constants.MSG_SENDER`や`Constants.ADDRESS_THIS`の場合、それぞれロックを行ったアドレスやコントラクト自体のアドレスを返します。

**引数**
- `recipient`
	- コマンドの宛先となるアドレスまたはフラグ。

**戻り値**
- `address`
	- 計算されたコマンドの宛先アドレス。

:::message
**map関数**
仮想通貨取引を行うスマートコントラクトを考えましょう。
このコントラクトには「**送金**」コマンドがあり、送金を行う際には以下のようにして宛先アドレスを指定します。

- `recipient`が`Constants.MSG_SENDER`の場合
	- ロックされているアドレスに送金される。
- `recipient`が`Constants.ADDRESS_THIS`の場合
	- このコントラクトのアドレスに送金される。
- それ以外の場合
	- 指定されたアドレスに送金される。

例を見てみましょう。

1. `recipient`が`Constants.MSG_SENDER`の場合
```
address recipient = Constants.MSG_SENDER;
address result = map(recipient);  // この場合、resultはlockedByのアドレスになる
```

2. `recipient`が`Constants.ADDRESS_THIS`の場合
```
address recipient = Constants.ADDRESS_THIS;
address result = map(recipient);  // この場合、resultはこのコントラクトのアドレスになる
```

3. `recipient`が特定のアドレスの場合
```
address recipient = 0x1234567890abcdef1234567890abcdef12345678;  // 仮想アドレス
address result = map(recipient);  // この場合、resultは指定したアドレスになる
```

このように、`map` 関数は特定のフラグやアドレスに基づいて宛先アドレスを計算し、返す役割を果たします。
送金の場合以外にも、様々なコマンドにおいて宛先アドレスを動的に指定する際に利用されることがあります。

どのような状況でも、`map`関数は指定された`recipient`に応じて適切な宛先アドレスを計算し、それを戻り値として返します。
:::

# モジュール

## Payments

### pay

:::details pay
```solidity
function pay(address token, address recipient, uint256 value) internal {
        if (token == Constants.ETH) {
            recipient.safeTransferETH(value);
        } else {
            if (value == Constants.CONTRACT_BALANCE) {
                value = ERC20(token).balanceOf(address(this));
            }

            ERC20(token).safeTransfer(recipient, value);
        }
}
```
:::

**概要**
**ETH**または**ERC20**トークンを受取アドレスに支払う関数。

**詳細**
指定されたトークンと受取アドレスに指定した量を支払います。
トークンが**ETH**の場合、`safeTransferETH`関数を使用して**ETH**を安全に転送します。
トークンが**ERC20**の場合、指定された量を受取アドレスに対して安全に転送します。
また、指定された量が`Constants.CONTRACT_BALANCE`の場合、コントラクトの残高を支払い額として使用します。

**引数**
- `token`
	- 支払うトークンのアドレス。
- `recipient`
	- 受取アドレスのアドレス。
- `value`
	- 支払う量。

**戻り値**
なし

---

### approveERC20

:::details approveERC20
```solidity
function approveERC20(ERC20 token, Spenders spender) internal {
        // check spender is one of our approved spenders
        address spenderAddress;
        /// @dev use 0 = Opensea Conduit for both Seaport v1.4 and v1.5
        if (spender == Spenders.OSConduit) spenderAddress = OPENSEA_CONDUIT;
        else if (spender == Spenders.Sudoswap) spenderAddress = SUDOSWAP;
        else revert InvalidSpender();

        // set approval
        token.safeApprove(spenderAddress, type(uint256).max);
}
```
:::

**概要**
プロトコルが**ERC20**トークンを支出できるように承認する関数。

**詳細**
指定された**ERC20**トークンとスペンダーアドレスを受け取り、トークンの最大量をスペンダーに承認します。
スペンダーアドレスは、事前に定義されたスペンダー（**Opensea**、**Conduit**、**Sudoswap**など）のいずれかである必要があります。

**引数**
- `token`
	- 承認するトークンの**ERC20**インスタンス。
- `spender`
	- 承認するスペンダーアドレス。

**戻り値**
なし

---

### payPortion

:::details payPortion
```solidity
function payPortion(address token, address recipient, uint256 bips) internal {
        if (bips == 0 || bips > FEE_BIPS_BASE) revert InvalidBips();
        if (token == Constants.ETH) {
            uint256 balance = address(this).balance;
            uint256 amount = (balance * bips) / FEE_BIPS_BASE;
            recipient.safeTransferETH(amount);
        } else {
            uint256 balance = ERC20(token).balanceOf(address(this));
            uint256 amount = (balance * bips) / FEE_BIPS_BASE;
            ERC20(token).safeTransfer(recipient, amount);
        }
}
```
:::

**概要**
コントラクトの**ETH**または**ERC20**トークンの一部を受取アドレスに支払う関数。

**詳細**
指定されたトークンと受取アドレスに指定した割合を支払います。
割合は`bips（basis points）`として与えられ、`FEE_BIPS_BASE`（ベースとなる`bips`）で割った値に基づいて計算されます。
トークンが**ETH**の場合、コントラクトの残高から計算された支払額を安全に転送します。
トークンが**ERC20**の場合も同様です。

**引数**
- `token`
	- 支払うトークンのアドレス。
- `recipient`
	- 受取アドレスのアドレス。
- `bips`
	- 支払う割合（basis points）。

**戻り値**
なし

:::message
`payPortion`は、指定されたトークンのコントラクトから一定の割合のトークンを取得し、指定されたアドレスに送信するためのものです。

例えば、この関数を以下のように呼び出すとします。

```solidity
payPortion(tokenAddress, recipientAddress, 500); // 500 bips (5%)
```

ここで、`tokenAddress`はトークンのアドレスを示し、`recipientAddress`は受取アドレスです。
`500`は支払いの割合を表しており、`500 bips`は全体の残高の`5%`に相当します。

1. 最初に、指定された割合`500`が `FEE_BIPS_BASE`（`10000`）以下であることを確認します。
これにより、割合が正当な値であることを確認します。
もし割合が`0`または`10000`を超える場合、`InvalidBips`エラーが発生します。

2. 次に、指定されたトークンが**ETH**であるかどうかをチェックします。
もしトークンが**ETH**であれば、コントラクトの残高を取得し、指定された割合に基づいて送信する量を計算します。
計算された量は、コントラクトの残高の`5%`（指定された割合）に相当します。

3. もしトークンが**ETH**ではない場合、**ERC20**トークンとして処理します。
指定されたトークンのコントラクトからコントラクトのアドレスに対する残高を取得し、同様に割合に基づいて送信する量を計算します。

4. 最後に、計算された量を`recipient.safeTransferETH(amount)`または`ERC20(token).safeTransfer(recipient, amount)`を使用して指定されたアドレスに送信します。
この操作により、指定された割合のトークンが受取アドレスに送られます。

この関数を使用することで、異なるトークンをコントラクト内で一定の割合で分配することができます。
:::

---

### sweep

:::details sweep
```solidity
function sweep(address token, address recipient, uint256 amountMinimum) internal {
        uint256 balance;
        if (token == Constants.ETH) {
            balance = address(this).balance;
            if (balance < amountMinimum) revert InsufficientETH();
            if (balance > 0) recipient.safeTransferETH(balance);
        } else {
            balance = ERC20(token).balanceOf(address(this));
            if (balance < amountMinimum) revert InsufficientToken();
            if (balance > 0) ERC20(token).safeTransfer(recipient, balance);
        }
}
```
:::

**概要**
コントラクトの全ての**ERC20**トークンまたは**ETH**を指定したアドレスに送る関数。

**詳細**
指定されたトークンと送付先アドレスに対して、コントラクトの残高が指定された最小額を超える場合にトークンを送ります。
**ETH**の場合、送付された額が指定された最小額を下回る場合はエラーを発生させます。

**引数**
- `token`
	- 送付するトークンのアドレス。
- `recipient`
	- 送付先のアドレス。
- `amountMinimum`
	- 最小の送付額。

**戻り値**
なし

---

### sweepERC721

:::details sweepERC721
```solidity
function sweepERC721(address token, address recipient, uint256 id) internal {
        ERC721(token).safeTransferFrom(address(this), recipient, id);
}
```
:::

**概要**
コントラクト内の**ERC721**トークンを指定したアドレスに送る関数。

**詳細**
この関数は、指定された**ERC721**トークンと受取アドレス、トークンのIDを受け取り、コントラクト内の指定された**ERC721**トークンを受取アドレスに安全に転送します。

**引数 or パラメータ**
- `token`
	- 送付する**ERC721**トークンのアドレス。
- `recipient`
	- 受取アドレス。
- `id`
	- 送付する**ERC721**トークンのID。

**戻り値**
なし

---

### sweepERC1155

:::details sweepERC1155
```solidity
function sweepERC1155(address token, address recipient, uint256 id, uint256 amountMinimum) internal {
        uint256 balance = ERC1155(token).balanceOf(address(this), id);
        if (balance < amountMinimum) revert InsufficientToken();
        ERC1155(token).safeTransferFrom(address(this), recipient, id, balance, bytes(''));
}
```
:::

**概要**
コントラクト内の**ERC1155**トークンを指定したアドレスに送る関数。

**詳細**
指定された**ERC1155**トークンと受取アドレス、トークンのID、最小額を受け取り、コントラクト内の指定された**ERC1155**トークンを受取アドレスに安全に転送します。

**引数**
- `token`
	- 送付する**ERC1155**トークンのアドレス。
- `recipient`
	- 受取アドレス。
- `id`
	- 送付する**ERC1155**トークンID。
- `amountMinimum`
	- 最小の送付額。

**戻り値**
なし

---

### wrapETH

:::details wrapETH
```solidity
function wrapETH(address recipient, uint256 amount) internal {
        if (amount == Constants.CONTRACT_BALANCE) {
            amount = address(this).balance;
        } else if (amount > address(this).balance) {
            revert InsufficientETH();
        }
        if (amount > 0) {
            WETH9.deposit{value: amount}();
            if (recipient != address(this)) {
                WETH9.transfer(recipient, amount);
            }
        }
}
```
:::

**概要**
指定した量の**ETH**を**WETH**に変換し、指定した受取アドレスに送る関数。

**詳細**
**ETH**を**WETH（Wrapped Ether）** に変換するプロセスを実行します。
**WETH**は、Ethereumネットワーク上でトークンとして取り扱うことができる**ETH**のラッパートークンです。
指定された量の**ETH**を**WETH**に変換し、受取アドレスに送ります。
また、特定の場合には、コントラクトの**ETH**残高を全て変換することもできます。

**引数**:
- `recipient`
	- **WETH**を受け取るアドレス。
- `amount`
	- 変換する**ETH**の量（`Constants.CONTRACT_BALANCE`と指定することで、コントラクトの**ETH**残高を全て変換する）。

### unwrapWETH9

:::details unwrapWETH9
```solidity
function unwrapWETH9(address recipient, uint256 amountMinimum) internal {
        uint256 value = WETH9.balanceOf(address(this));
        if (value < amountMinimum) {
            revert InsufficientETH();
        }
        if (value > 0) {
            WETH9.withdraw(value);
            if (recipient != address(this)) {
                recipient.safeTransferETH(value);
            }
        }
}
```
:::

**概要**
コントラクト内の全ての**WETH**を**ETH**に変換し、指定した最小量の**ETH**を以上の場合に受け取りアドレスに送る関数。

**詳細**
コントラクト内の全ての**WETH**を**ETH**に変換するプロセスを実行します。
指定した最小量の**ETH**を以上の場合、**ETH**を受取アドレスに送ります。

**引数**:
- `recipient`
	- **ETH**を受け取るアドレス。
- `amountMinimum`
	- 送信する最小量の**ETH**

---

## Permit2Payments

### permit2TransferFrom

:::details permit2TransferFrom
```solidity
function permit2TransferFrom(address token, address from, address to, uint160 amount) internal {
        PERMIT2.transferFrom(from, to, amount, token);
}
```
:::

**概要**
**Permit2**コントラクトの`transferFrom`を実行する関数。

**詳細**
トークンを指定された送信元アドレスから受信先アドレスへ指定された量だけ転送します。

**引数**
- `token`
	- 転送するトークンのアドレス。
- `from`
	- トークンを転送する元のアドレス。
- `to`
	- トークンを転送する先のアドレス。
- `amount`
	- 転送するトークン量。

---

### permit2TransferFrom

:::details permit2TransferFrom
```solidity
function permit2TransferFrom(IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails, address owner)
        internal
    {
        uint256 batchLength = batchDetails.length;
        for (uint256 i = 0; i < batchLength; ++i) {
            if (batchDetails[i].from != owner) revert FromAddressIsNotOwner();
        }
        PERMIT2.transferFrom(batchDetails);
}
```
:::

**概要**
**Permit2**トークンのバッチ転送を実行する関数。

**詳細**
指定されたバッチの各転送詳細に基づいて、トークンを転送します。
オーナーアドレスが一致しない場合にはエラーを発生させます。

**引数**
- `batchDetails`
	- 各転送詳細を含む配列。
- `owner`
	- オーナーアドレス。

---

### payOrPermit2Transfer

:::details payOrPermit2Transfer
```solidity
function payOrPermit2Transfer(address token, address payer, address recipient, uint256 amount) internal {
        if (payer == address(this)) pay(token, recipient, amount);
        else permit2TransferFrom(token, payer, recipient, amount.toUint160());
}
```
:::

**概要**
支払いまたは**Permit2**コントラクトの`transferFrom`を実行する関数。
支払いの際には`payer`が自身のアドレスと一致するかどうかを確認し、そうでない場合には**Permit2**コントラクトの`transferFrom`を行います。

**詳細**
指定された条件に基づいて支払いまたは**Permit2**コントラクトの`transferFrom`を実行します。
`payer`が自身のアドレスであれば、通常の支払いを行います。
それ以外の場合は、**Permit2**コントラクトの`transferFrom`操作を行い、指定されたトークンを指定された受け取りアドレスへ転送します。

**引数**
- `token`
	- 転送するトークンのアドレス。
- `payer`
	- 支払いを行うアドレス。
- `recipient`
	- トークンを受け取るアドレス。
- `amount`
	- 転送するトークンの量。

---

# ライブラリ

## Commands

### FLAG_ALLOW_REVERT

:::details FLAG_ALLOW_REVERT
```solidity
bytes1 internal constant FLAG_ALLOW_REVERT = 0x80;
```
:::

**説明**
コマンドのフラグとして使用されます。
特定のビットが立っているかどうかを確認するために使用され、リバートを許可するかどうかを示すフラグです。

---

### COMMAND_TYPE_MASK

:::details COMMAND_TYPE_MASK
```solidity
bytes1 internal constant COMMAND_TYPE_MASK = 0x3f;
```
:::

**説明**
コマンドのタイプを取得するためのマスクとして使用されます。
コマンドタイプを抽出するために使用されるビットマスクです。

:::message
この定数は、特定のビットをマスクしてコマンドのタイプ情報を抽出するために使用されます。

バイト（bytes1）は`8`ビットのデータ型であり、各ビットは`0`または`1`の値を持ちます。
`0x3f`は16進数表記であり、バイナリで`00111111`に相当します。
このバイナリのパターンは、下位`6`ビットが`1`で上位`2`ビットが`0`であることを示しています。

この`COMMAND_TYPE_MASK`は、コマンドのバイト表現からコマンドのタイプ情報だけを抽出するのに使用されます。
ビットマスクとして使用されることで、上位`2`ビットが無視され、下位`6`ビットの情報のみが保持されます。

以下はビットマスクの使用例です。

```
bytes1 commandByte = 0x27;  // 00100111 in binary
bytes1 commandType = commandByte & COMMAND_TYPE_MASK;
```

上記の例では、`commandByte`の下位`6`ビットを`commandType`に抽出して格納します。
結果として`commandType`には、`0x07`（バイナリでは `00000111`）が格納されることになります。
これにより、コマンドのタイプ情報を抽出するためにビットマスクが使用されていることがわかります。

このようにして、`COMMAND_TYPE_MASK`を使用することで、バイト表現のコマンドから必要な情報を取得するために特定のビットを抽出できます。
:::

:::message
**ビットマスク**
ビットマスクとは、特定のビットを抽出したり、操作したりするために、特定のビットパターンを使用するテクニックです。
ビットマスクは、ビットの特定の位置に対して論理演算を行い、そのビットを取得したり変更したりするのに使用されます。
主にプログラミングやデータ処理において使用されます。

以下は具体的な例を使ってビットマスクの概念を説明します。
考えているのは、`8`ビットのバイナリデータで、各ビットが`0`か`1`の値を持っている場合です。
例として、バイト`0x6B`（バイナリで `01101011`）を考えます。

1. 特定のビットを取得する例
ビットマスクを使用して、特定のビットの値を取得することができます。
例えば、3番目のビット（最下位ビットを0番目と数えた場合）の値を取得する場合を考えてみましょう（AND演算）。
   
```plaintext
バイト:  01101011
マスク:  00000100 (1を左に3ビットシフト)
-----------------
結果:    00000000 (0番目から3番目のビットは0)
```
   
結果として、3番目のビットの値は0になります。

2. 特定のビットを設定する例
ビットマスクを使用して、特定のビットを`1`に設定することもできます。
例えば、5番目のビットを1に設定する場合を考えてみましょう（OR演算）。
   
```plaintext
バイト:  01101011
マスク:  00100000 (1を左に5ビットシフト)
-----------------
結果:    01101011 (5番目のビットが1に設定されました)
```

結果として、5番目のビットが1に設定されました。

3. 特定のビットをクリアする例
ビットマスクを使用して、特定のビットを0にクリアすることもできます。
例えば、2番目のビットを0にクリアする場合を考えてみましょう（AND演算）。
   
```plaintext
バイト:  01101011
マスク:  11111011 (0を左に2ビットシフトし、反転)
-----------------
結果:    01101011 (2番目のビットは変更されずに残りました)
```

結果として、2番目のビットは変更されずに残りました。

このように、ビットマスクは特定のビット操作を行う際に便利なテクニックです。
ビット演算を使用して特定のビットの値を取得、設定、クリアすることができ、プログラミングにおいてさまざまな用途に利用されます。
:::

---

### V3_SWAP_EXACT_IN

:::details V3_SWAP_EXACT_IN
```solidity
uint256 constant V3_SWAP_EXACT_IN = 0x00;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
バージョン3のスワップ（**Exact In**）コマンドを表します。
トークンの入力量が指定され、出力量が計算されるスワップ操作です。

---

### V3_SWAP_EXACT_OUT

:::details V3_SWAP_EXACT_OUT
```solidity
uint256 constant V3_SWAP_EXACT_OUT = 0x01;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
バージョン3のスワップ（**Exact Out**）コマンドを表します。
トークンの出力量が指定され、入力量が計算されるスワップ操作です。

---

### PERMIT2_TRANSFER_FROM

:::details PERMIT2_TRANSFER_FROM
```solidity
uint256 constant PERMIT2_TRANSFER_FROM = 0x02;
```
:::

**説明**
この変数は、コマンドタイプを示すコマンドの一部です。トークンのトランスファー操作を許可するための `permit` 機能を使用した際のコマンドを表します。

---

### PERMIT2_PERMIT_BATCH

:::details PERMIT2_PERMIT_BATCH
```solidity
uint256 constant PERMIT2_PERMIT_BATCH = 0x03;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
複数のトークンに対して`permit`機能を使用した際のバッチ処理のコマンドを表します。

---

### SWEEP

:::details SWEEP
```solidity
uint256 constant SWEEP = 0x04;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
トークンを集める（`sweep`）操作を表します。
複数のアドレスからトークンを集めるために使用されるコマンドです。

---

### TRANSFER

:::details TRANSFER
```solidity
uint256 constant TRANSFER = 0x05;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
トークンのトランスファー操作を行うためのコマンドを表します。

---

### PAY_PORTION

:::details PAY_PORTION
```solidity
uint256 constant PAY_PORTION = 0x06;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
特定のトークンの一部を支払うためのコマンドを表します。
たとえば、特定のトークンの所有者に対してトークンの一部を支払う際に使用されます。

---

### FIRST_IF_BOUNDARY

:::details FIRST_IF_BOUNDARY
```solidity
uint256 constant FIRST_IF_BOUNDARY = 0x08;
```
:::

**説明**
コマンドタイプが一定範囲内にある場合、特定の条件を満たすコマンドを表す境界を示します。
この境界を超えるコマンドタイプは別の条件で評価されます。

:::message
コマンドの実行を効率的に行うために、ネストされた条件分岐（`if`文）のブロック内でコマンドを分割する境界を定義しています。
この定数がどのように使用されるかを詳しく説明します。

1. `FIRST_IF_BOUNDARY`の値は`0x08`です。
16進数表記で、これは`00001000`のバイナリ表現に相当します。

2. コマンドは、実行するための種類や条件に応じて複数のタイプに分類されます。
コマンドタイプは`0x00`から`0x3f`の範囲にあります。

3. コマンドタイプが`FIRST_IF_BOUNDARY`より小さい（`0`から`7`まで）場合、最初のネストされた条件分岐のブロック内でコマンドが実行されます。
これにより、コマンドの種類によって最初の条件分岐が効率的に実行されることが保証されます。

4. コマンドタイプが`FIRST_IF_BOUNDARY`より大きい（`8`以上）場合、最初の条件分岐のブロックでは実行されず、さらに深くネストされた条件分岐のブロック内でコマンドが実行されます。
これにより、異なるタイプのコマンドを適切にグループ化して処理することができます。

このように`FIRST_IF_BOUNDARY`は、コマンドの種類に基づいてネストされた条件分岐のブロック内でコマンドを分割する目印のようなものです。
これにより、異なる種類のコマンドを適切な場所で実行し、処理を効率的に行うことができます。
:::

---

### V2_SWAP_EXACT_IN

:::details V2_SWAP_EXACT_IN
```solidity
uint256 constant V2_SWAP_EXACT_IN = 0x08;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
バージョン2のスワップ（**Exact In**）コマンドを表します。
トークンの入力量が指定され、出力量が計算されるスワップ操作です。

---

### V2_SWAP_EXACT_OUT

:::details V2_SWAP_EXACT_OUT
```solidity
uint256 constant V2_SWAP_EXACT_OUT = 0x09;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
バージョン2のスワップ（**Exact Out**）コマンドを表します。
トークンの出力量が指定され、入力量が計算されるスワップ操作です。

---

### PERMIT2_PERMIT

:::details PERMIT2_PERMIT
```solidity
uint256 constant PERMIT2_PERMIT = 0x0a;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
`permit`機能を使用したトークンのトランスファー操作を行うためのコマンドを表します。

---

### WRAP_ETH

:::details WRAP_ETH
```solidity
uint256 constant WRAP_ETH = 0x0b;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
**Ether（ETH）** をラップするためのコマンドを表します。
**ETH**を特定の**ERC20**トークンに変換する操作です。

---

### UNWRAP_WETH

:::details UNWRAP_WETH
```solidity
uint256 constant UNWRAP_WETH = 0x0c;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
**Wrapped Ether（WETH）** をアンラップするためのコマンドを表します。
**WETH**を**ETH**に戻す操作です。

---

### PERMIT2_TRANSFER_FROM_BATCH

:::details PERMIT2_TRANSFER_FROM_BATCH
```solidity
uint256 constant PERMIT2_TRANSFER_FROM_BATCH = 0x0d;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
複数のトークンに対して`permit`機能を使用した際のバッチ処理のコマンドを表します。

---

### BALANCE_CHECK_ERC20

:::details BALANCE_CHECK_ERC20
```solidity
uint256 constant BALANCE_CHECK_ERC20 = 0x0e;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
特定の**ERC20**トークンの残高を確認するためのコマンドを表します。

---

### SECOND_IF_BOUNDARY

:::details SECOND_IF_BOUNDARY
```solidity
uint256 constant SECOND_IF_BOUNDARY = 0x10;
```
:::

**説明**
コマンドタイプが一定範囲内にある場合、特定の条件を満たすコマンドを表す境界を示します
この境界を超えるコマンドタイプは別の条件で評価されます。

---

### SEAPORT_V1_5

:::details SEAPORT_V1_5
```solidity
uint256 constant SEAPORT_V1_5 = 0x10;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
SeaPortバージョン1.5の操作を表すコマンドを表します。

:::message
**SeaPort**
NFTマーケットプレイスのOpenseaが提供する売買プロトコルのことです。
:::

https://coinpost.jp/?p=359189#:~:text=Seaport%E3%81%AFOpenSea%E3%81%8C5,%E3%81%AB%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B%E3%82%82%E3%81%AE%E3%81%A0%E3%80%82&text=Seaport%E3%81%AE%E4%B8%BB%E3%81%AA%E6%94%B9%E5%96%84,%E7%B4%8435%25%E3%80%8D%E7%AF%80%E7%B4%84%E5%8F%AF%E8%83%BD%E3%80%82

---

### LOOKS_RARE_V2

:::details LOOKS_RARE_V2
```solidity
uint256 constant LOOKS_RARE_V2 = 0x11;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
LooksRareバージョン2の操作を表すコマンドを表します。

https://looksrare.org/ja

---

### NFTX

:::details NFTX
```solidity
uint256 constant NFTX = 0x12;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
NFTXに関する操作を表すコマンドを表します。

https://nftx.io/

---

### CRYPTOPUNKS

:::details CRYPTOPUNKS
```solidity
uint256 constant CRYPTOPUNKS = 0x13;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
CryptoPunksに関する操作を表すコマンドを表します。

---

### OWNER_CHECK_721

:::details OWNER_CHECK_721
```solidity
uint256 constant OWNER_CHECK_721 = 0x15;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
特定の**ERC721**トークンの所有者を確認するためのコマンドを表します。

---

### OWNER_CHECK_1155

:::details OWNER_CHECK_1155
```solidity
uint256 constant OWNER_CHECK_1155 = 0x16;
```
:::

**説明**
この変数は、コマンドタイプを示すコマンドの一部です。
特定の**ERC1155**トークンの所有者を確認するためのコマンドを表します。

---

### THIRD_IF_BOUNDARY

:::details THIRD_IF_BOUNDARY
```solidity
uint256 constant THIRD_IF_BOUNDARY = 0x18;
```
:::

**説明**
コマンドタイプが一定範囲内にある場合、特定の条件を満たすコマンドを表す境界を示します。
この境界を超えるコマンドタイプは別の条件で評価されます。

---

### X2Y2_721

:::details X2Y2_721
```solidity
uint256 constant X2Y2_721 = 0x18;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
**ERC721**トークンを交換するためのコマンドを表します。

---

### SUDOSWAP

:::details SUDOSWAP
```solidity
uint256 constant SUDOSWAP = 0x19;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
**SudoSwap**に関する操作を表すコマンドを表します。

---

### NFT20

:::details NFT20
```solidity
uint256 constant NFT20 = 0x1a;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
**ERC20**に関する操作を表すコマンドを表します。

---

### X2Y2_1155

:::details X2Y2_1155
```solidity
uint256 constant X2Y2_1155 = 0x1b;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
**ERC1155**トークンを交換するためのコマンドを表します。

---

### FOUNDATION

:::details FOUNDATION
```solidity
uint256 constant FOUNDATION = 0x1c;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
**Foundation**に関する操作を表すコマンドを表します。

---

### SWEEP_ERC1155

:::details SWEEP_ERC1155
```solidity
uint256 constant SWEEP_ERC1155 = 0x1d;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
**ERC1155**トークンを集める（`sweep`）操作を表します。

---

### ELEMENT_MARKET

:::details ELEMENT_MARKET
```solidity
uint256 constant ELEMENT_MARKET = 0x1e;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
エレメントマーケットに関する操作を表すコマンドを表します。

---

### FOURTH_IF_BOUNDARY

:::details FOURTH_IF_BOUNDARY
```solidity
uint256 constant FOURTH_IF_BOUNDARY = 0x20;
```
:::

**説明**
コマンドタイプが一定範囲内にある場合、特定の条件を満たすコマンドを表す境界を示します。
この境界を超えるコマンドタイプは別の条件で評価されます。

---

### SEAPORT_V1_4

:::details SEAPORT_V1_4
```solidity
uint256 constant SEAPORT_V1_4 = 0x20;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
SeaPortバージョン1.4の操作を表すコマンドを表します。

---

### EXECUTE_SUB_PLAN

:::details EXECUTE_SUB_PLAN
```solidity
uint256 constant EXECUTE_SUB_PLAN = 0x21;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
サブプランを実行するためのコマンドを表します。
サブプランとは、コマンドの一連の実行手順のことを指します。

---

### APPROVE_ERC20

:::details APPROVE_ERC20
```solidity
uint256 constant APPROVE_ERC20 = 0x22;
```
:::

**説明**
コマンドタイプを示すコマンドの一部です。
**ERC20**トークンの承認を行うためのコマンドを表します。
他のアドレスがあるトークンを操作できるように承認します。

---

## Constants

もちろんです。以下に、上記ライブラリ内の各変数についてわかりやすく説明します。

### CONTRACT_BALANCE

:::details 
```solidity
uint256 internal constant CONTRACT_BALANCE = 0x8000000000000000000000000000000000000000000000000000000000000000;
```
:::

**説明**
特定のトークンの残高を入力として使用する場合に識別するために使用されます
この値は、最も重要なビットに単一の1がある形式で表現され、最も上位ビットに1が設定されています。

:::message
この定数は、特定のトークンの残高を入力として使用する場合に、そのトークンの残高をこのコントラクトのバランスとして使用することを示すために使用されます。

この定数は、バイナリ表記で`1`が最も重要なビットに位置している値です。
この`1`は、「**バイナリ表記において桁の値が最も大きなビット**」として知られるビットで、他のビットよりも桁の値が大きくなります。
このビットが `1` であることは、特定の条件が成立していることを示します。

この定数の値 `0x8000000000000000000000000000000000000000000000000000000000000000`は、16進数表記で`8000000000000000000000000000000000000000000000000000000000000000`です。これは、バイナリ表記で`10000000000000000000000000000000000000000000000000000000000000000`です。

この値をビットで表すと、ビットが`1`であり、他のビットはすべて`0`です。
この特性を利用して、トークンの残高を入力として使用する際に、このビットが`1`であることを確認することができます。
:::

---

### ALREADY_PAID

:::details 
```solidity
uint256 internal constant ALREADY_PAID = 0;
```
:::

**説明**
v2のペアがすでに入力トークンを受け取った場合に識別するために使用されます。

---

### ETH

:::details 
```solidity
address internal constant ETH = address(0);
```
:::

**説明**
**ETH**（イーサリアム）のトランザクションを識別するために使用されます。
具体的には、トークンではなく**ETH**が送信される場合に使用されます。

---

### MSG_SENDER

:::details 
```solidity
address internal constant MSG_SENDER = address(1);
```
:::

**説明**
特定のアドレスを識別するためのフラグです。
この変数の値は`address(1)`であり、トランザクションの送信者のアドレスを示します。

---

### ADDRESS_THIS

:::details 
```solidity
address internal constant ADDRESS_THIS = address(2);
```
:::

**説明**
特定のアドレスを識別するためのフラグです。
この変数の値は`address(2)`であり、コントラクト自体のアドレスを示します。

---

### ADDR_SIZE

:::details 
```solidity
uint256 internal constant ADDR_SIZE = 20;
```
:::

**説明**
アドレスがエンコードされたバイト列の長さを示します。
イーサリアムアドレスは`20`バイトで表されるため、この値は`20`です。

---

### V3_FEE_SIZE

:::details 
```solidity
uint256 internal constant V3_FEE_SIZE = 3;
```
:::

**説明**
v3の手数料がエンコードされたバイト列の長さを示します。
具体的には、手数料情報は`3`バイトで表されます。

---

### NEXT_V3_POOL_OFFSET

:::details 
```solidity
uint256 internal constant NEXT_V3_POOL_OFFSET = ADDR_SIZE + V3_FEE_SIZE;
```
:::

**説明**
次のv3プール情報のオフセットを示します。
特に、アドレスと手数料がエンコードされた次のプール情報の開始位置を指します。

---

### V3_POP_OFFSET

:::details 
```solidity
uint256 internal constant V3_POP_OFFSET = NEXT_V3_POOL_OFFSET + ADDR_SIZE;
```
:::

**説明**
エンコードされたプールキーのオフセットを示します。
プールキーは、トークンアドレスと手数料がエンコードされた情報を指します。

---

### MULTIPLE_V3_POOLS_MIN_LENGTH

:::details 
```solidity
uint256 internal constant MULTIPLE_V3_POOLS_MIN_LENGTH = V3_POP_OFFSET + NEXT_V3_POOL_OFFSET;
```
:::

**説明**
複数のv3プール情報がエンコードされた際の最小エンコード長を示します。
複数のプール情報が含まれる場合、それぞれの情報を区別するためにこの最小の長さを持つエンコードが必要です。

# イベント

## RewardsSent

**概要**
報酬が送信されたときに発行されるイベント。

**詳細**
報酬が送信される場面で、その送信が行われたことを他のアプリケーションやユーザーに通知するために利用されます。

**パラメータ**
- `amount`
	- 送信された報酬の金額を表します。

# コード

## コントラクト

- `UniversalRouter/contracts`

## ライブラリ

- `UniversalRouter/lib`

## ベースコントラクト

- `UniversalRouter/contracts/base`

## ライブラリ（コントラクト）

- `UniversalRouter/contracts/libraries`

## インターフェース

- `UniversalRouter/contracts/interfaces`

## モジュール

- `UniversalRouter/contracts/modules`

# 最後に

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

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

https://chaldene.net/

https://qiita.com/cardene
