最近、Uniswap Lab は次世代 AMM Uniswap V4 の開発進捗状況を正式に発表し、ホワイト ペーパーとコード リポジトリを公開しました。今回の V4 のホワイト ペーパーは 3 ページのみです。その理由は、V4 では AMM のコア アルゴリズム ロジックに多くの変更が加えられておらず、より多くのシナリオのニーズを満たすために V3 に基づいていくつかの新機能が追加されているためです。 SharkTeam は、現在オープンされているソース コードを使用して、V4 がもたらす新機能を確認し、V4 によって開始された重要な機能をフックするためのアプリケーションのベスト プラクティスを分析します。

1. V4とV3の違い

1.1 アムム

AMM アルゴリズム レベルでは、Uniswap V4 は V3 を変更しておらず、定数積 x*y=k に基づく流動性アルゴリズムを引き続き使用しています。

Uniswap V3 では、各取引ペアに 4 つのプール (当初は 3 つでしたが、後に新しい 1bp プールが追加されました) を持つことができ、それぞれ 0.01%、0.05%、0.3%、1% の手数料率のプールを表します。これらのプールは、ティックに対応します。スペースも異なります。プールを作成するときは、これら 4 つのタイプのいずれか 1 つしか選択できません。

Uniswap V4 では、理論上、各取引ペアは任意の数のプールを持つことができ、各プールの手数料率も任意の値にすることができ、これらのプールのティック スペースも任意の値にすることができます。

これはまた問題を引き起こします。Uniswap V4 の取引ペアの流動性は断片化されるため、ユーザーが最適な取引パスを見つけられるようにするには、より効果的なルーター/アグリゲーターが必要です。

1.2 フック

フックは、サードパーティまたは Uniswap 担当者によって開発された一連のコントラクトです。プールを作成するときに、プールは、後でトランザクションの特定の段階で、それにバインドされたフック コントラクトを自動的に呼び出すことができます。 Uniswap V4 は、フック コントラクト コードを実行できる次のステージを定義します。

  • 初期化前

  • 初期化後

  • 位置変更前

  • 位置変更後

  • スワップ前

  • スワップ後

  • 寄付する前

  • 寄付後

これは、プールの初期化、流動性の追加/削除、取引、寄付、その他の操作の前後にフック コントラクトを呼び出すことができることを意味します。

フック コントラクトでは、上記のどのステージを実行するかを明示的に指定する必要があり、プールはガスを節約するために、対応するフックを特定のステージで実行する必要があるかどうかを知る必要があります。これらのフラグはコントラクトに保存されません。ただし、フックが特定のアドレスを使用して示す必要があります。具体的な判定コードは以下の通りです。

フック アドレスの最初の 8 ビットは、フックを特定の段階で実行する必要があるかどうかを示すフラグとして使用されていないことがわかります。

したがって、フック開発者は、コントラクトをデプロイするときに、プールの要件を満たすアドレスを生成する必要があります。これを実現するには、通常、Create2 とランダムな Salt の計算を使用する必要があります。

以下は、ホワイトペーパーのフック実行の例です。

スワップの実行前後に、プールはまず、プールの対応するフックに対応するフラグがオンになっているかどうかを確認し、オンになっている場合は、フック コントラクトの対応する関数が自動的に呼び出されることを確認できます。

1.3 動的な手数料率

フックは、特定の段階でコードを実行するだけでなく、特定のプールのスワップ手数料率と引き出し率を決定することもできます。引き出しレートとは、流動性を削除するときにユーザーがフックに支払う必要があるレートを指します。さらに、フックはスワップ手数料の一部を自分自身に指定することもできます。

プールを作成するときは、料金パラメーター (uint24) の最初の 4 ビットを使用して、このプールが動的料金を使用するかどうか、およびフック スワップ料金と引き出し料金を開始するかどうかをマークする必要があります。

動的手数料が有効な場合、プールは各スワップの前にフック コントラクトを呼び出して現在のスワップ手数料比率を取得します。フック コントラクトは getFee() 関数を実装して現在のスワップ手数料比率を返す必要があります。

フックにより Uniswap V4 が開発者プラットフォームになり、AMM にさらなる可能性が与えられます。フックを使用して実装できる機能には、TWAMM (時間加重自動マーケットメーカー)、Limit Order (指値注文)、LP 再投資などが含まれます。これらについては、後続の章で詳しく紹介します。

1.4 シングルトンコントラクト

Uniswap V3 で新しいプールが作成されるたびに、新しいコントラクトをデプロイする必要があり、大量のガスを消費します。ただし、実際には、これらのプールで使用されるコードは同じですが、初期化パラメータが異なります。 Uniswap V4 では、すべてのプールを管理するための Singleton コントラクトが導入されており、新しいプールを作成するときに新しいコントラクトをデプロイする必要がなくなり、コントラクトをデプロイするためのガスが節約されます。

さらに、シングルトン コントラクトを使用する利点は、トランザクション プロセス中のトークンの転送を削減できることです。V3 では、すべてのプールが同じコントラクト内にあるため、クロス プール スワップをコントラクト内で直接完了できます。プール スワップ 異なるプール間でトークンを転送する必要があるため、ガスが増加します。

同時に、V4 では、すべてのプールが同じコントラクトを使用し、コントラクト内のトークンの会計も、フラッシュ ローン A を使用する場合、プールごとの会計ではなく、トークンごとの会計に簡素化されました。大量のトークンもより便利になります。

1.5 エクストラロード

フックと他のコントラクトの統合を容易にするために、V4 コントラクトには extload 関数が追加され、コントラクトのすべての内部状態が外部から読み取り可能になり、すべてのプールのステータスが外部に対して完全に透過的になります。

1.6 フラッシュ会計

プール スワップ間のトークン転送を減らすために、V4 はフラッシュ アカウンティングと呼ばれる方法を使用してスワップを標準化し、流動性/フラッシュ ローン プロセスをフラッシュ ローンと同様のプロセスに追加/削除します。

(1) ユーザーがロックを取得する

(2) ユーザーは、複数のプールでのスワップ、流動性の追加/削除、フラッシュ ローンによるプールからのトークンの借用などの操作を実行します。

(3) すべてのユーザー操作によって生成されたトークン転送はロックに記録されます。

(4) すべての操作が完了した後、ユーザーは取得したトークンを持ち帰ることができ、同時にロックに記録されている支払う必要のあるトークンを支払う必要があります。

これらのプロセスはトランザクション内で実行する必要があります。

このようにして、トランザクションで複数のプールにわたるスワップが必要な場合、決済中に必要な転送は 2 回だけです。たとえば、ETH->USDC-BTC のようなスワップでは、中間トークンとしての USDC は転送をまったく必要としません。

1.7 ERC1155 ミント/バーン

Flash Accounting は、ERC1155 トークンを使用することで、同じトランザクションでのスワップ トークンの転送を削減でき、複数のトランザクションでのトークンの転送をさらに削減できます。

V4 では、ERC1155 mint を通じてトークンを V4 コントラクトに保存できるため、毎回トークンを V4 コントラクトに転送することなく、複数のトランザクションでこれらのトークンを使用できます。

ERC1155 書き込みを使用して、V4 コントラクトに保存されているトークンを引き出します。

ERC1155 は、流動性のスワップや追加/削除を頻繁に行うユーザーに適しており、よく使用されるトークンを V4 コントラクトに直接保存できるため、トークン転送のガス オーバーヘッドを削減できます。

2. フックのベストプラクティスの例

2.1 TWAMM (時間加重自動マーケットメーカー)

アリスはブロックチェーン上で 1 億ドル相当のイーサを購入したいと考えています。 Uniswap などの既存の自動マーケット メーカー (AMM) プラットフォームでこの規模の注文を実行すると、法外なコストがかかります。これらのプラットフォームでは、より良い価格を得るために内部情報を使用することを防ぐためにアリスに高額の手数料を請求する可能性が高いからです。

より良い価格を得るために、Alice の最善のオプションは、注文を手動でいくつかの小さなサブ注文に分割し、数時間かけて段階的に実行することです。その考えは、彼女が内部情報を持っていないことを市場に認識させるのに十分な時間を与え、それによってより良い価格を与えることです。ただし、たとえ彼女が複数のより大きなサブ注文を送信したとしても、各サブ注文は依然として価格に重大な影響を与える一方、敵対的なトレーダーによる「サンドイッチ攻撃」に対して脆弱でもあります。

TWAMM は、Alice に代わってトランザクションを実行することでこの問題を解決します。これは、彼女の注文を無数の小さな仮想注文に分割し、長期にわたってスムーズな実行を保証します。同時に、TWAMM は、組み込み AMM プロトコルの特別な数学的関係を利用して、これらの仮想注文間でガスコストを割り当てます。 TWAMM はブロック間のトランザクションを処理するため、「サンドイッチ攻撃」に対して脆弱ではありません。

全体として、TWAMM は、アリスに大規模な取引を実行するためのより効率的な方法を提供し、高額な手数料や市場操作の可能性を回避します。

2.1.1 原則

TWAMM には他の AMM と同様に AMM が組み込まれており、ユーザーはこの AMM を通じてスポット取引を直接行うことができ、それに流動性を追加することもできます。ただし、TWAMM には 2 つの TWAP 注文プールもあり、ユーザーが注文を送信するときにトランザクションのトークン入力の数と期間を指定すると、TWAMM は注文を 2 つの方向に実行します。同じトランザクション方向を対応するプールに転送し、指定されたトランザクション速度に従って自動的にトランザクションを実行します。ユーザーの注文が完全に実行されると、ユーザーはトランザクションから取得したトークンを取り出すことができます。もちろん、ユーザーの注文が実行される前に、ユーザーは事前に注文をキャンセルしたり、注文に必要なトークンの数を変更したりすることもできます。

イーサリアムでは、スマート コントラクトは、EOA アドレスによってアクティブに開始されたトランザクションによってのみトリガーできますが、自動的に実行することはできません。したがって、TWAMM は、注文プールで取引されるトークンを決済するためにトランザクションを定期的に送信するために EOA アカウントを必要とし、これらのトランザクションを実行するにはキーパー アカウントが必要です。

もちろん、ユーザーが操作するたびに TWAMM に注文プールを自動的に決済させることもでき、これによりキーパーのオーバーヘッドが排除されます。これは、DeFi プロトコルがストリーミング データを処理する一般的な方法でもあります。

2.1.2 このトランザクション モデルがサンドイッチ攻撃を受けにくいのはなぜですか?

ブロック内のタイムスタンプが変更されないため、この攻撃は実行が困難です。攻撃者は、次のブロックの TWAMM 決済に影響を与えるために、ブロックの最後のトランザクションでプールの価格を引き上げる必要があります。これにはサンドイッチ攻撃を複数のブロックで行う必要がありますが、他の裁定者が途中で介入して攻撃者が損失を被る可能性があるため、攻撃者に大きなリスクをもたらすことは間違いありません。

同時に、裁定取引者の存在により、そのような価格操作は持続不可能になる運命にあります。TWAP 注文の特性により、短期間にあまり多くのトークンが取引されないため、損失は限定されなければなりません。ほとんどの場合。

2.1.3 V4 の TWAMM ワークフロー

(1) このフックは 2 つの TWAP 注文プールを維持し、それぞれ 2 つのトランザクション方向の TWAP 注文を表します。

(2) ユーザーは、このフックを通じて TWAP 注文を送信できます。トランザクションのトークン、数量、および期間を指定する必要があります。

(3) このフックは、beforeSwap と beforeModifyPosition を登録します。このフックは、ユーザーがポジションを取引または調整するたびにトリガーされます。

(4) トリガーされた後、フックは 2 つの TWAP 注文プールを決済する責任を負います。

(5) ユーザーはいつでも手動で決済をトリガーすることもできます

(6) ユーザーは TWAP 注文のトークン数をキャンセルまたは変更することができます

2.1.4 事例の詳細説明

TWAMM は、フックの論理呼び出しを実行するために 3 つのステージを登録します。TWAMM はプールが初期化される前に初期化され、このフックはユーザーが取引またはポジションを調整するたびにトリガーされます。

ユーザーは、TWAMM の submitOrder 関数を手動で呼び出して、契約に実行する必要がある注文を送信できます。

ユーザーが実行する必要がある注文をコントラクトに追加すると、その注文はプールが swap およびmodifyPosition 操作を実行するたびに自動的に実行されます。

ユーザーが v4 の swap 関数を呼び出して取引したり、modifyPosition 関数を呼び出してポジションを変更したりするたびに、TWAMM の実行関数がトリガーされ、内部関数 _executeTWAMMOrders が呼び出されて、以前に完了していない注文の実行を継続します。

_executeTWAMMOrders 関数

以上が注文更新の実行プロセスです。実行が完了すると、現在のtwamm注文実行時間が更新されます。

2.2 指値注文

最後の市場価格で即座に約定する成行注文とは異なり、指値注文は所定の価格に達するとすぐに約定します。自動マーケットメーカー (AMM) に基づくほとんどの DEX は、デフォルトで成行注文システムを選択します。シンプルで初心者にもわかりやすい。成行注文は、最大価格への影響などのパラメーターにより、実行されるか失敗します。指値注文では、資産価格が指値価格に達した場合にのみ注文が約定され、それ以外の場合、注文はオープンされたままになります。

たとえば、ETHが現在ETH/DAIプールで1 ETH = 1500 DAIで取引されているとします。ユーザーは利益確定注文を行うことができ、その主な内容は「1 ETH = 2000 DAI の場合、私の ETH をすべて売却する」です。この価格に達すると、ユーザーの ETH は完全に分散型の方法で完全にオンチェーンで DAI に自動的に交換されます。

Uniswap の以前のバージョンでは、指値注文は事実上不可能でした。ほとんどの AMM では市場での売買のみが許可されています。 V4 バージョンでは、フックの強力な機能とスケーラビリティにより、v4 での指値注文の実装の基盤が確立されています。

2.2.1 原則

指値注文はtwammよりも設計原理が単純であり、現在では流動性を追加するための指値注文が実装されており、取引に対する指値注文の実装が容易になります。

v4にはtickLowerとtickUpperが存在しており、プールの取引状況の変化に応じて、ユーザーが流動性を追加するときに、現在の価格で追加したくない場合に使用できます。この要件を実行するには、フックに対応する価格を設定します。フックは、設定された価格に達すると、対応する流動性を追加します。

2.2.2 V4 の指値注文ワークフロー

1. Limit は、さまざまな下値および取引方向に対する指値注文として複数のエポックを維持します。

2. このフックを通じて、ユーザーは自分の価格の下限と取引方向を送信し、指値注文を契約に追加します。

3. このフックは afterSwap を登録し、各スワップ終了後に価格が変化した場合にのみトリガーされます。

4. トリガーされた後、フックは現在の価格範囲を検証し、エポックからの流動性の追加を必要とする現在の価格での指値注文があるかどうかを確認します。

5. ユーザーはいつでもお金を引き出したり、流動性を直接追加したりできます

2.2.3 事例の詳細説明

フックはコントラクト登録の 2 段階でトリガーされ、プールが初期化された後にフックが初期化され、各交換の後にフック ロジックがトリガーされます。

ユーザーは、place 関数を呼び出して流動性を追加したい数量、価格、取引方向を渡します。フックはまずユーザーが追加したい流動性をプールに追加して保存し、次に対応する制限を作成します。ユーザー向けの注文。

このフックは各スワップ後にトリガーされ、価格範囲内に存在する指値注文が現在の価格範囲に基づいて流動性を追加する操作を完了します。

ユーザーは、指値注文が完了する前に kill 関数を呼び出して指値注文をキャンセルし、この追加された流動性によるメリットを得ることができます。

ユーザーは、流動性を削除したい場合に引き出し関数を呼び出して、必要な流動性を抽出できます。

一般に、この指値注文フックは、ユーザーが流動性を追加したいときに価格を設定できる、より便利な方法を提供します。プール内の価格範囲がその価格に達すると、フックによってユーザーのプールに自動的に価格が追加されます。流動性を追加する操作が実行され、ユーザーはいつでも流動性をキャンセルおよび引き出しできます。

TWAMM と指値注文に加えて、LP 再投資、動的な手数料変更、その他の機能もフックに基づいて実装できます。スペースの都合上、これらについては後続の分析で紹介します。

LP 再投資: ユーザーはフックを使用して流動性を追加、変更、削除できます。フックは呼び出し用に afterSwap および afterModifyPosition を登録できます。ユーザーは一様にフックを使用して流動性を追加するため、フックはトリガーされるたびに現在の時刻を確認し、流動性報酬を受け取ることを選択します。 LP トークンは流動性を追加するためにプールに再度追加され、それによってユーザーのメリットが自動的に最適化されます。

動的な料金変更: フックは beforeSwap などのインターフェイスを登録して、スワップ前に動的な料金を変更できます。動的手数料は単に時間に応じて直線的に変化するだけでなく、単一のスワップによって生成されるティックジャンプの数に基づいてボラティリティを定量化することができ、それによって手数料を動的に変更し、LPの永続的なリスクに対するヘッジを実現します。これにより、流動性プロバイダーに対する取引によって引き起こされる一時的な損失の影響が軽減されます。

プール呼び出しフックに関連する関数ロジックでは、ロールバック ステートメントの使用を減らすようにしてください。プール コントラクトとフック コントラクトには共通の関係があるため、フックでトランザクションのロールバックが発生すると、プール内のトランザクションもロールバックされます。ロールバックされます。プール内の通常のトランザクションに関連しないフック内の Rollback ステートメントにより、ユーザーはプール内の関数を正常に使用できなくなる可能性があります。

フック内で selfdestruct 関数を使用しないでください。フック内で selfdestruct 関数を呼び出すと、フック内のロジックに問題が発生するだけでなく、プール内の関数が適切に動作しなくなり、アセットが破損する可能性があります。プール全体が失われ、機能が無効になります。通常どおりに続行してください。

権限を厳密に制御する

フック コントラクトで権限を厳密に制御して過剰な権限を持つロールを回避し、特権ロールのマルチシグネチャ管理を実行してシングルポイント攻撃を防ぎます。特権ロールがコントラクト ステータス変数を自由に変更できる状況は避けてください。ロジック エラーによりトランザクション全体がロールバックされ、プールの通常の使用に影響を与える可能性があります。最小特権の原則を検証するには、openzeppelin の AccessControl コントラクトを使用して、よりきめ細かいアクセス許可へのアクセスを制御する必要があります。これは、この方法では、各システム コンポーネントが最小特権の原則に従うように制限されるためです。

再入国制限の実施

プールの外部拡張コードとして、フックはコントラクト内の再入攻撃の可能性にも注意する必要があります。たとえば、指値注文でネイティブ トークンの転送を実行するときに再入が発生し、その結果コントラクト資産が失われる可能性があります。外部コントラクトまたはいわゆる「check-validate-interact」パターンを呼び出す前に、すべての状態を確認して更新を試みます。こうすれば、再入力してもすべての状態が更新されているため影響はありません。 。

契約アップグレード管理

開発者によっては、プロキシ コントラクトを使用してフック ロジックにその後の変更やアップグレードを行う場合もありますが、コントラクトのアップグレードで発生する可能性のある問題にも注意する必要があります。まず、フックでプロキシモードが採用されている場合でも、beforeSwapなどのプロキシコントラクト内で対応するステージを宣言する必要があります。そうしないと、プールは正しい戻り値を検証できません。次に、delegatecall を呼び出す前にターゲット コントラクトが存在するかどうかを確認します。Solidity はこのチェックを実行しません。変数がパッケージ化されて保存される可能性があるため、変数の宣言の順序を慎重に検討してください。同じスロット、ガスコスト、メモリレイアウト、デリゲートコールの結果などに影響を与える問題。

私たちについて

SharkTeam のビジョンは、Web3 の世界を包括的に保護することです。このチームは、世界中の経験豊富なセキュリティ専門家と上級研究者で構成されており、ブロックチェーンとスマート コントラクトの基礎理論に精通しており、スマート コントラクトの監査、オンチェーン分析、緊急対応などのサービスを提供しています。 Polkadot、Moonbeam、polygon、OKC、Huobi Global、imToken、ChainIDE など、ブロックチェーン エコシステムのさまざまな分野の主要企業と長期的な協力関係を確立しています。