序文
分散型金融 (DeFi) の急速な発展に伴い、Uniswap は主要な分散型取引所としてイノベーションの最前線に立ってきました。この記事では、Uniswap v3 プロトコルの中核メカニズムを詳細に分析し、集中流動性、複数レート、トークン交換、フラッシュ ローンなどの主要な機能を含む機能設計の詳細な説明を提供するとともに、関連する監査ポイントを提供します。監査人。 (注:この記事の画像は https://www.figma.com/board/QyIpAUR93MxZ4XZZf2QjDk/uniswap-v3 で高画質でご覧いただけます。)
アーキテクチャ分析
Uniswap v3 プロトコルは主に 4 つのモジュールで構成されます。
- PositionManager: ユーザーが流動性操作を実行するためのメイン インターフェイス。ユーザーはトークン プールを作成し、流動性を提供/削除し、流動性プロバイダー (LP) の資格情報として ERC721 を使用できます。
- SwapRouter: ユーザーがトークンを交換するための入り口。ユーザーはこのモジュールを通じてトークン交換操作を完了できます。
- プール: トークン取引、流動性管理、取引手数料の徴収、および Oracle データ管理機能の実装を担当します。その中で、ティックメカニズムは価格帯を複数の細かいスケールに分割します。
- ファクトリ: プール契約の作成と管理に使用されます。
工程仕分け
トークンペアを作成する
ユーザーは、createAndInitializePoolIfNecessary 関数を通じてこれを行うことができます。ユーザーは、トークン 0、トークン 1、手数料 (手数料)、およびトークン ペアの初期価格 () を渡す必要があります。まず、システムは getPool 関数を通じてトークン ペアがすでに存在するかどうかを確認します。トークン ペアが作成されていない場合は、createPool が呼び出され、CREATE2 命令が取引ペアをデプロイするために使用されます。最後に、initialize 関数を使用して、価格、手数料、ティック、オラクル、およびその他の関連パラメーターの初期化を完了します。
流動性の提供
流動性の提供
ユーザーは、mint 関数を通じて新しい流動性ポジションを作成し、対応する NFT を生成したり、increaseLiquidity 関数を通じて既存の NFT 流動性ポジションに流動性を追加したりできます。まず、システムはトランザクションが指定された時間範囲内に実行されたかどうかを確認し、次に addLiquidity 関数を呼び出して特定の操作を完了します。この関数では、プールのアドレスと流動性が最初に計算され、次に _updatePosition が呼び出されてユーザーのポジションを更新し、下限ティック、上限ティック、および合計累積手数料を変更します。その後、システムは _modifyPosition を通じて流動性を追加し、ティックが上限と下限の条件を満たしていることを確認し、計算された token0 と token1 の数 (int256) を返し、それをプールに送信します。最後に、システムはユーザーの tokenId に基づいて、対応する位置情報を更新します。
流動性の除去
ユーザーは、deciseLiquidity 関数を通じて流動性を削除できます。まず、システムは LP 証明書の権限とトランザクションの有効期限をチェックします。プールに十分な流動性があることを前提として、書き込み関数を呼び出して流動性を削除します。その後、システムは実際に削除されたトークンの数がユーザーが設定した最小要件を満たしているかどうかを検証し、それに応じてユーザーの位置情報を更新します。
スワップ
ユーザーは、exactInput 関数を使用して、支払われるトークンの数と取得が予想されるトークンの最小数を指定することも、exactOutput 関数を使用して、支払われるトークンの最大数を指定し、取得が予想されるトークンの数を設定することもできます。システムは最初にパス (パス) を解析し、次に、exactInputInternal 関数またはexactOutputInternal 関数を順番に呼び出して、スワップ操作の各ステップを完了します。
スワップ機能では、システムは最初にロック解除状態をロックして、他のトランザクションが状態変数の更新を妨げないようにします。ループに入った後、システムはティックを通じて次の取引価格を見つけ、tokenIn または tokenOut がユーザーの期待に達するまで各ステップで computeSwapStep 関数を呼び出して為替を計算します。同時に、システムは手数料、流動性、ティック、価格の関連値を更新します。目盛りが変化した場合は、Oracle データも更新する必要があります。これらの操作が完了すると、システムはユーザーに tokenOut を支払い、ユーザーはコールバック関数 uniswapV3SwapCallback を通じて tokenIn を支払います。このメカニズムはフラッシュ スワップとみなすことができます。その後、システムは契約残高が一致しているかどうかを確認し、確認後にロック解除状態を解除します。
パス内のすべてのスワップ操作が完了し、トランザクションがユーザーの期待に応えた場合、トランザクションは正常に終了します。
フラッシュ
フラッシュ機能を利用してフラッシュローン操作を行うことができます。まず、システムは借入手数料を計算し、ユーザーが必要とするトークンを指定された貸与アドレスに送信します。次に、システムはユーザーが実装した uniswapV3FlashCallback 関数をコールバックし、ユーザーはこの関数で返済操作を完了します。システムは、コールバック後の契約残高の変更をチェックして、ユーザーの融資額と一致していることを確認し、対応する手数料を更新します。フラッシュ機能に加えて、ユーザーはスワップ操作を通じて同様のフラッシュ ローン機能を実装することもできます。つまり、トランザクション プロセス中にトークンを最初に借り、次に返済します。
監査ポイント
監査ポイント
1.スワップ操作後にrefundETHが呼び出されているかどうかを確認します
ユーザーは、exactInput 関数で、支払うトークンの数と、取得が予想されるトークンの最小数を指定する必要があります。 uniswapV3SwapCallback を呼び出す前に、システムは amount0 と amount1 を再計算して、ユーザーがトークンを正確に送信できることを確認します。ただし、ETHで交換する場合、ユーザーはトランザクションと一緒にETHを送信する必要があります。トランザクション中にすべてのETHが使用されなかった場合でも、この関数は超過分を自動的に返却しません。 exactInput 関数は amountOut のみを返すため、トレーダーはこの取引所で実際に消費された ETH の量を直接知ることはできません。
さらに、誰もが払い戻しETH関数を呼び出して、未使用のETHを契約から引き出すことができます。したがって、ユーザーがプロトコルに未使用のETHを残さないようにするために、スワップ操作の後にrefundETHが呼び出されているかどうかを確認するか、MultiCall関数を使用して複数の関数呼び出しを1回の操作で完了するかどうかを確認することをお勧めします。
2.オラクル価格を取得するためにTWAPが実装されているかどうかを確認します
Uniswap を価格ソースとして使用する場合、外部プロトコルが Slot0 に直接アクセスして sqrtPriceX96 を取得すると、価格操作のリスクが生じる可能性があります。攻撃者は、取引実行時に有利な価格を取得するために、スワップやその他の方法を通じて流動性プールの状態を操作する可能性があります。
このリスクを軽減するために、開発者は価格を取得するために時間加重平均価格 (TWAP) をさらに実装することをお勧めします。TWAP は短期的な激しい価格変動の影響を効果的に軽減し、価格の操作をより困難にするためです。
3. ユーザーが自分でスリッページパラメータを設定できるようにすることをお勧めします
他のプロトコルがスワップ操作に Uniswap v3 を使用する場合、開発者はビジネス シナリオに基づいてスリッページ保護を設定し、サンドイッチ攻撃を防ぐためにユーザーがパラメータを自分で調整できるようにすることをお勧めします。このスワップ関数では、4 番目のパラメーター sqrtPriceLimitX96 を使用して、ユーザーがスワップを実行することを希望する最小価格または最大価格を指定します。このパラメータは、取引プロセス中の極端な価格変動を効果的に防止し、過度のスリッページによるユーザーの損失を軽減します。
4. 流動性プールのホワイトリストメカニズムを導入することをお勧めします
Uniswap v3 では、ERC20 トークンの同じペアが、異なる手数料に基づいて複数の流動性プール (プール) に同時に存在する場合があります。通常、少数の流動性プールが流動性の大部分を保持しますが、他のプールにはロックされた総量 (TVL) が非常に少ないか、まだ作成されていない可能性があります。これらの低い TVL プールは、価格操作のターゲットになる可能性が高くなります。
したがって、プロジェクト関係者が流動性プール データの使用を選択する場合は、単純に LP をデータ ソースとして使用することは避けるべきです。データの信頼性を確保するには、十分な流動性と操作の困難さを備えたプールを選別するホワイトリスト メカニズムを導入することをお勧めします。このメカニズムにより、リスクを大幅に軽減し、価格参照データの安全性と正確性を確保しながら、TVL が低すぎるプールの操作によって引き起こされる潜在的な損失を防ぐことができます。
5. TickMath.sol、FullMath.sol、Position.sol でチェックなしが使用されているかどうかを確認します
Uniswap v3 では、TickMath、FullMath、Position などのモジュールを使用して、Solidity のオーバーフロー処理メカニズムに依存する複雑な数学的計算を実行します。 Solidity の以前のバージョン (0.8.0 未満) では、整数のオーバーフローとアンダーフローの動作はデフォルトで例外をスローしなかったため、コードはこの前提に基づいて正常に実行できました。ただし、Solidity バージョン 0.8.0 以降、オーバーフローとアンダーフローは自動的に例外をスローするため、既存のコードの実行に影響します。これらのモジュールが Solidity 0.8.0 以降で適切に機能することを確認するには、開発者は特定の関数で未チェックのコード ブロックを使用してオーバーフロー チェックを手動で無効にする必要があります。これにより、以前のバージョンの動作が復元され、オーバーフローに敏感な操作が効率的に実行されます。
Solidity 0.8.0 以降に対して正式なサポートと調整が行われました。詳細については、このアップデート (https://github.com/Uniswap/v3-core/commit/6562c52e8f75f0c10f9deaf44861847585fc8129) を参照してください。この変更により、TickMath、FullMath、およびその他の関連モジュールが新しいバージョンのコンパイラーで引き続き正しく実行されることが保証されます。
6. パスのエンコード方法とデコード方法が同じかどうかを確認します
Uniswap v3 のexactInput関数とexactOutput関数では、ユーザーはパスパラメータを入力する必要があります。ステップバイステップのトークン交換操作では、このパラメータは固定形式、つまりtokenA-fee-tokenBに従ってエンコードおよびデコードされる必要があります。このパス構造は、トランザクションの各ホップに関与する 2 つのトークンと、それらの間の料金レベルを明示的に指定します。 Uniswap v3 のトークン交換機能を使用するときに、外部プロトコルが異なるパス デコード方法を選択すると、パス形式が Uniswap の予期されるパス形式と一致しない可能性があります。この場合、プロトコルはパスを正しく解決できないため、意図したトークン交換操作を正常に実行できない可能性があります。
したがって、開発者は、Uniswap v3 のトークン交換機能を統合するときに、外部プロトコルが Uniswap のパス エンコーディング ルールに厳密に従っていることを確認することをお勧めします。パス デコード エラーを防ぐために、外部プロトコルは、トランザクションの失敗や予期しない結果を回避するために、exactInput およびexactOutput を呼び出すときにパス パラメーターの形式を注意深くチェックする必要があります。
したがって、開発者は、Uniswap v3 のトークン交換機能を統合するときに、外部プロトコルが Uniswap のパス エンコーディング ルールに厳密に従っていることを確認することをお勧めします。パス デコード エラーを防ぐために、外部プロトコルは、トランザクションの失敗や予期しない結果を回避するために、exactInput およびexactOutput を呼び出すときにパス パラメーターの形式を注意深くチェックする必要があります。
7. トークンの順序がプロジェクト ロジックに影響するかどうかを確認する
Uniswap では、token0 は下位のトークンであり、ベース トークンとして使用されます。一方、token1 は上位のトークンで、引用トークンとして使用されます。 Uniswap は、2 つのトークンのアドレスを辞書順に並べ替えて、プール内でのトークン ペアの順序が常に一貫していることを確認します。
ただし、異なるブロックチェーン ネットワーク上の同じトークンのコントラクト アドレスは異なる場合があるため、特にチェーン全体にデプロイされたコントラクトの場合、トークンの並べ替え順序が変わる可能性があります。この変更により、token0 と token1 の役割が逆転するため、価格パフォーマンスに影響します。たとえば、一部のチェーンでは、特定のトークンが token0 である可能性がありますが、他のチェーンでは token1 として注文される場合があり、その結果、ベース トークンと引用トークンの間に異なる関係が生じ、最終的に表示価格に影響を及ぼします。したがって、開発者は、トークンの順序がプロジェクト ロジックに影響を与えるかどうかを確認することをお勧めします。特にクロスチェーン環境では、価格パフォーマンスへの悪影響を避けるために、トークンの順序によって引き起こされる可能性のある価格の問題を必ず考慮してください。トランザクションロジック。
要約する
上記の基本的なチェック項目は、Uniswap v3 の現在のバージョンに基づいており、Uniswap v3 と対話するプロジェクトをチェックするために監査人によって使用されます。プロジェクトの実施にはそれぞれ特徴があるため、監査人は契約内容を深く理解し、実態を踏まえた厳正な監査を行う必要があります。開発中のプロジェクトについて、SlowMist セキュリティ チームは、開発者がプロトコルのセキュリティと信頼性を確保するために、開発プロセス中にこれらのチェックを慎重に検討することを推奨しています。
著者 | シシケ
編集者 | リズ
全てのコメント