mcpField
V
v4-security-foundations

v4-security-foundations

github.com/uniswapセキュリティ2361.2k 回インストール2 日前に更新
リポジトリ

v4-security-foundations について

Uniswap v4 の Hook を開発する人向けのセキュリティガイドです。権限のリスク、トークン処理、Delta 決済、デプロイ前の確認項目を扱い、特に危険な return-delta 権限と会計上の問題を解説します。

主な機能

  1. 01Hook の脅威モデルと権限リスク
  2. 02NoOp 攻撃の分析と検出
  3. 03Delta 会計と決済パターン
  4. 04アクセス制御とルーター検証
  5. 05トークンの危険性とチェックリスト
  6. 06ガス予算とリスク評価

活用シーン

  1. 01Uniswap v4 Hook の実装前に設計を確認
  2. 02beforeSwapReturnDelta を有効にした Hook の評価
  3. 03コールバック権限と Delta 決済ロジックの確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/uniswap/uniswap-ai --skill v4-security-foundations

インストール後、Claude Code で/v4-security-foundationsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
audit-checklist.md
base-hook-template.md
vulnerabilities-catalog.md

詳細

v4 フックのセキュリティ基礎

Uniswap v4 フックを構築するためのセキュリティ優先ガイドです。フックの脆弱性によってユーザー資金が流出する可能性があります。フックのコードを書く前に、これらの概念を理解してください。

脅威モデル

コードを書く前に、v4 のセキュリティ状況を理解してください。

脅威領域説明緩和策
呼び出し元の検証フック関数を呼び出すのは PoolManager のみにする必要がありますmsg.sender == address(poolManager) を検証する
送信者の識別msg.sender は常に PoolManager であり、エンドユーザーではありませんユーザーの識別には sender パラメーターを使用します
ルーターのコンテキストsender パラメーターが識別するのはユーザーではなくルーターですルーターの許可リストを実装します
状態の公開トランザクション実行中にフックの状態を読み取ることができますオンチェーンに機密データを保存しないようにします
再入可能性のリスクフックからの外部呼び出しによって再入可能性が生じる可能性があります再入可能性ガードを使用し、外部呼び出しを最小限に抑えます

パーミッションフラグのリスクマトリクス

リスクレベルを付した、14 個すべてのフックパーミッションです。

パーミッションフラグリスクレベル説明セキュリティ上の注意
beforeInitialize低プール作成前に呼び出されますプールパラメーターを検証します
afterInitialize低プール作成後に呼び出されます状態の初期化に安全です
beforeAddLiquidity中LP 預け入れ前正当な LP をブロックする可能性があります
afterAddLiquidity低LP 預け入れ後追跡や報酬に安全です
beforeRemoveLiquidity高LP 引き出し前ユーザー資金を引き出せなくする可能性があります
afterRemoveLiquidity低LP 引き出し後追跡に安全です
beforeSwap高スワップ実行前価格を操作する可能性があります
afterSwap中スワップ実行後最終状態を観察できます
beforeDonate低寄付前アクセス制御のみ
afterDonate低寄付後追跡に安全です
beforeSwapReturnDelta重大カスタムのスワップ数量を返しますNoOp 攻撃ベクトル
afterSwapReturnDelta高スワップ後の数量を変更します価値を抜き取る可能性があります
afterAddLiquidityReturnDelta高LP トークン数量を変更しますLP に不利益を与える可能性があります
afterRemoveLiquidityReturnDelta高引き出し数量を変更します資金を盗む可能性があります
リスクのしきい値
  • 低:資金損失を引き起こす可能性は低いです
  • 中:慎重な実装が必要です
  • 高:実装を誤ると資金損失を引き起こす可能性があります
  • 重大:資金の完全な窃取を可能にするおそれがあります

重大:NoOp ラグプル攻撃

BEFORE_SWAP_RETURNS_DELTA パーミッション(ビット 3)は、最も危険なフックパーミッションです。悪意のあるフックは、次のことができます。

  1. スワップ全体を処理したと主張するデルタを返す
  2. PoolManager がこれを受け入れ、取引を決済する
  3. フックは出力トークンを渡さずに入力トークンをすべて保持する
  4. ユーザーはスワップ額の全額を失う
攻撃パターン
// MALICIOUS - DO NOT USE
function beforeSwap(
    address,
    PoolKey calldata,
    IPoolManager.SwapParams calldata params,
    bytes calldata
) external override returns (bytes4, BeforeSwapDelta, uint24) {
    // Claim to handle the swap but steal tokens
    int128 amountSpecified = int128(params.amountSpecified);
    BeforeSwapDelta delta = toBeforeSwapDelta(amountSpecified, 0);
    return (BaseHook.beforeSwap.selector, delta, 0);
}
検出

beforeSwapReturnDelta: true を持つフックとやり取りする前に、必ず以下を実施してください。

  1. フックのコードを監査する - 正当なユースケースであることを確認します
  2. 所有権を確認する - アップグレード可能ですか?誰がアップグレードできますか?
  3. 実績を確認する - 信頼できる監査会社による監査を受けていますか?
  4. 少額から始める - まず最小額でテストします
正当な用途

NoOp パターンは、以下の用途で有効です。

  • ジャストインタイム流動性(JIT)
  • カスタム AMM カーブ
  • インテントベースの取引システム
  • RFQ/PMM との統合

ただし、それぞれ慎重な実装と監査が必要です。

デルタ会計の基礎

v4 では、PoolManager を通じた貸借システムが使用されます。

コア不変条件
For every transaction: sum(deltas) == 0

PoolManager は、各アドレスが支払うべき額、または受け取るべき額を追跡します。トランザクションの終了時には、すべての債務を清算する必要があります。

主な関数
関数目的方向
take(currency, to, amount)PoolManager からトークンを引き出すトークンを受け取ります
settle(currency)PoolManager にトークンを支払うトークンを送ります
sync(currency)PoolManager の残高追跡を更新する清算の準備
清算パターン
// Correct pattern: sync before settle
poolManager.sync(currency);
currency.transfer(address(poolManager), amount);
poolManager.settle(currency);
よくある間違い
  1. sync を忘れる:sync しないと清算に失敗します
  2. 順序を間違える:sync → transfer → settle の順に実行する必要があります
  3. 一部のみ清算する:トランザクションが無効な状態で残ります
  4. 二重に清算する:会計上のエラーが発生します

アクセス制御パターン

PoolManager の検証

すべてのフックコールバックで、呼び出し元を必ず検証する必要があります。

modifier onlyPoolManager() {
    require(msg.sender == address(poolManager), "Not PoolManager");
    _;
}

function beforeSwap(
    address sender,
    PoolKey calldata key,
    IPoolManager.SwapParams calldata params,
    bytes calldata hookData
) external override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) {
    // Safe to proceed
}
重要な理由

このチェックがないと、以下の問題が発生します。

  • 誰でもフック関数を直接呼び出せます
  • 攻撃者がフックの状態を操作できます
  • 偽のコールバックを通じて資金が流出する可能性があります

ルーター検証パターン

sender パラメーターはエンドユーザーではなく、ルーターを示します。ユーザー ID が必要なフックでは、以下を使用します。

許可リストパターン
mapping(address => bool) public allowedRouters;

function beforeSwap(
    address sender,  // This is the router
    PoolKey calldata key,
    IPoolManager.SwapParams calldata params,
    bytes calldata hookData
) external override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) {
    require(allowedRouters[sender], "Router not allowed");
    // Proceed with swap
}
hookData によるユーザー ID の取得
function beforeSwap(
    address sender,
    PoolKey calldata key,
    IPoolManager.SwapParams calldata params,
    bytes calldata hookData
) external override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) {
    // Decode user address from hookData (router must include it)
    address user = abi.decode(hookData, (address));
    // CAUTION: Router must be trusted to provide accurate user
}
msg.sender の落とし穴
// WRONG - msg.sender is always PoolManager in hooks
function beforeSwap(...) external {
    require(msg.sender == someUser); // Always fails or wrong
}

// CORRECT - Use sender parameter
function beforeSwap(address sender, ...) external {
    require(allowedRouters[sender], "Invalid router");
}

トークン処理の危険性

すべてのトークンが標準的な ERC-20 のように動作するわけではありません。

トークンの種類危険性対策
手数料徴収型受取額が送金額を下回る実際の残高変化を測定する
リベース型送金なしで残高が変化する生の残高を保存しない
ERC-777送金コールバックによってリエントランシーが可能になるリエントランシーガードを使用する
一時停止可能送金がブロックされる送金失敗を適切に処理する
ブロックリスト特定のアドレスがブロックされる本番環境のアドレスでテストする
小数桁が少ない計算で精度が失われる適切なスケーリングを使用する
安全な残高確認パターン
function safeTransferIn(
    IERC20 token,
    address from,
    uint256 amount
) internal returns (uint256 received) {
    uint256 balanceBefore = token.balanceOf(address(this));
    token.safeTransferFrom(from, address(this), amount);
    received = token.balanceOf(address(this)) - balanceBefore;
}

基本フックのテンプレート

すべての権限を無効にした状態から始め、必要な権限のみを有効にします。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {BaseHook} from "v4-periphery/src/base/hooks/BaseHook.sol";
import {Hooks} from "v4-core/src/libraries/Hooks.sol";
import {IPoolManager} from "v4-core/src/interfaces/IPoolManager.sol";
import {PoolKey} from "v4-core/src/types/PoolKey.sol";
import {BeforeSwapDelta, BeforeSwapDeltaLibrary} from "v4-core/src/types/BeforeSwapDelta.sol";

contract SecureHook is BaseHook {
    constructor(IPoolManager _poolManager) BaseHook(_poolManager) {}

    function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
        return Hooks.Permissions({
            beforeInitialize: false,
            afterInitialize: false,
            beforeAddLiquidity: false,
            afterAddLiquidity: false,
            beforeRemoveLiquidity: false,
            afterRemoveLiquidity: false,
            beforeSwap: false,           // Enable only if needed
            afterSwap: false,            // Enable only if needed
            beforeDonate: false,
            afterDonate: false,
            beforeSwapReturnDelta: false,      // DANGER: NoOp attack vector
            afterSwapReturnDelta: false,       // DANGER: Can extract value
            afterAddLiquidityReturnDelta: false,
            afterRemoveLiquidityReturnDelta: false
        });
    }

    // Implement only the callbacks you enabled above
}

完全な実装テンプレートについては、references/base-hook-template.md を参照してください。

セキュリティチェックリスト

フックをデプロイする前に、以下を確認してください。

#チェック項目状況
1すべてのフックコールバックで msg.sender == poolManager を検証している[ ]
2必要に応じてルーターの許可リストを実装している[ ]
3OOG の原因となる、上限のないループがない[ ]
4外部呼び出しに対する再入防止策を講じている[ ]
5デルタの合計がゼロになる[ ]
6送金時に手数料が発生するトークンに対応している[ ]
7アドレスをハードコードしていない[ ]
8スリッページパラメーターを遵守している[ ]
9機密データをオンチェーンに保存していない[ ]
10アップグレードの仕組みが保護されている(該当する場合)[ ]
11有効にする場合、beforeSwapReturnDelta の使用理由が明確である[ ]
12ファズテストが完了している[ ]
13不変条件テストが完了している[ ]

ガス予算のガイドライン

フックのコールバックは、PoolManager のトランザクションコンテキスト内で実行されます。ガス消費が過剰になると、スワップがリバートしたり、経済的に成立しなくなったりする可能性があります。

コールバック別のガス予算
コールバック目標予算上限備考
beforeSwap< 50,000 gas150,000 gasすべてのスワップで実行されるため、軽量に保つ
afterSwap< 30,000 gas100,000 gas分析/トラッキングのみ
beforeAddLiquidity< 50,000 gas200,000 gasアクセス制御を含む場合がある
afterAddLiquidity< 30,000 gas100,000 gas報酬のトラッキング
beforeRemoveLiquidity< 50,000 gas200,000 gasロックの検証
afterRemoveLiquidity< 30,000 gas100,000 gasトラッキング/会計
外部呼び出しを伴うコールバック< 100,000 gas300,000 gas外部 DEX へのルーティング、オラクル
よくあるガス関連の落とし穴
  1. 上限のないループ:動的配列(例:すべてのアクティブなポジション)を反復処理すると、ブロックのガス上限を超える可能性があります。配列サイズに上限を設けるか、ページネーションを使用してください。
  2. ホットパスでの SSTORE:新しいストレージスロットごとに約 20,000 gas かかります。トランザクション終了後に保持する必要のないデータには、一時ストレージ(tstore/tload)を優先してください。Solidity >= 0.8.24 が必要で、EVM ターゲットを cancun 以降に設定する必要があります。
  3. 外部呼び出し:コントラクト間の呼び出しごとに、約 2,600 gas の基本コストに加えて、呼び出し先の実行コストがかかります。可能な場合は呼び出しをまとめてください。
  4. 文字列操作:コールバック内での string 操作は避け、識別子には bytes32 を使用してください。
  5. 冗長な読み取り:poolManager の呼び出しをキャッシュしてください。getSlot0() や getLiquidity() を繰り返し読み取るたびにガスがかかります。
ガスの測定
# Profile a specific hook callback with Foundry
forge test --match-test test_beforeSwapGas --gas-report

# Snapshot gas usage across all tests
forge snapshot --match-contract MyHookTest

リスクスコアリングシステム

フックのリスクスコアを計算してください(0~33)。

カテゴリ点数基準
権限0-14有効にした権限のリスクレベルの合計
外部呼び出し0-5外部とのやり取りの数と種類
状態の複雑さ0-5変更可能な状態の量
アップグレードの仕組み0-5プロキシ、管理者関数など
トークンの取り扱い0-4標準外トークンへの対応
監査ティアの推奨事項
スコアリスクレベル推奨事項
0-5低セルフ監査 + ピアレビュー
6-12中プロフェッショナル監査を推奨
13-20高プロフェッショナル監査が必須
21-33重大複数回の監査が必須

絶対禁止事項

フックでは、次のことを絶対に行わないでください。

  1. ユーザーの識別に msg.sender を絶対に使わないでください - 常に PoolManager です
  2. NoOp 攻撃を理解せずに beforeSwapReturnDelta を絶対に有効にしないでください
  3. パスワード、キー、個人を特定できる情報をオンチェーンに絶対に保存しないでください
  4. ETH の送金に transfer() を絶対に使わないでください - call{value:}("") を使用してください
  5. トークンの小数桁数を絶対に決めつけないでください - 必ずトークンに問い合わせてください
  6. 乱数生成に block.timestamp を絶対に使わないでください
  7. 呼び出しでガス上限を絶対にハードコードしないでください
  8. 外部呼び出しの戻り値を絶対に無視しないでください
  9. 認可に tx.origin を絶対に使わないでください - フィッシング攻撃の経路となります。悪意のあるコントラクトは、元のユーザーの tx.origin を維持したまま呼び出しを中継できます

デプロイ前監査チェックリスト

#項目必須対象
1セキュリティ重視の開発者によるコードレビューすべてのフック
2すべてのコールバックの単体テストすべてのフック
3Foundry によるファズテストすべてのフック
4不変条件テストデルタを返すフック
5メインネットのフォークテストすべてのフック
6ガスプロファイリングすべてのフック
7フォーマル検証重大なフック
8Slither/Mythril による分析すべてのフック
9外部監査中リスク以上のフック
10バグ報奨金プログラム高リスク以上のフック
11監視/アラートのセットアップすべての本番フック

詳細な監査要件については、references/audit-checklist.md を参照してください。

本番フックの参考例

監査済みの本番フックから学びましょう。

プロジェクト説明主なセキュリティ機能
Flaunchトークンローンチプラットフォームマルチシグ管理者、タイムロック
EulerSwapレンディング連携市場ごとのリスク分離
Zaha TWAMM時間加重 AMM段階的な実行により MEV を低減
BunniLP 管理集中流動性ガード

外部リソース

公式ドキュメント
セキュリティリソース
コミュニティ

追加の参考資料

コメント