v4-security-foundations
Uniswap v4のフック開発におけるセキュリティを最優先に対応するスキルです。「v4 hooks」「hook security」「PoolManager」「beforeSwap」「afterSwap」などのキーワードが挙がった際や、V4フックのベストプラクティス・脆弱性・監査要件について質問があった場合に使用します。
description の原文を見る
Security-first Uniswap v4 hook development. Use when user mentions "v4 hooks", "hook security", "PoolManager", "beforeSwap", "afterSwap", or asks about V4 hook best practices, vulnerabilities, or audit requirements.
SKILL.md 本文
v4 Hook セキュリティ基礎
Uniswap v4 hooks を構築するためのセキュリティ第一ガイド。Hook の脆弱性はユーザー資金を流出させる可能性があります。Hook コードを記述する前に、これらの概念を理解してください。
脅威モデル
コードを記述する前に、v4 セキュリティコンテキストを理解してください:
| 脅威領域 | 説明 | 緩和策 |
|---|---|---|
| 呼び出し元検証 | PoolManager のみが hook 関数を呼び出すべき | msg.sender == address(poolManager) を確認 |
| 送信元ID | msg.sender は常に PoolManager で、エンドユーザーではない | sender パラメータでユーザー ID を使用 |
| ルーターコンテキスト | sender パラメータはルーターを識別し、ユーザーではない | ルーター許可リストを実装 |
| 状態露出 | Hook 状態はトランザクション実行中に読み取り可能 | 機密データをオンチェーンに保存しない |
| リエントランシー表面 | Hook からの外部呼び出しでリエントランシーが可能 | リエントランシーガード を使用; 外部呼び出しを最小化 |
パーミッションフラグリスクマトリックス
すべての 14 個の hook パーミッションと関連するリスクレベル:
| パーミッションフラグ | リスクレベル | 説明 | セキュリティ上の注意 |
|---|---|---|---|
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 パーミッション (ビット 10) は最も危険な hook パーミッションです。悪意のある hook は次のことができます:
- スワップ全体を処理したと主張するデルタを返す
- PoolManager がこれを受け入れ、トレードを決済する
- Hook はアウトプットを提供せずにすべての入力トークンを保持する
- ユーザーはスワップ全額を失う
攻撃パターン
// 悪意のある - 使用しないこと
function beforeSwap(
address,
PoolKey calldata,
IPoolManager.SwapParams calldata params,
bytes calldata
) external override returns (bytes4, BeforeSwapDelta, uint24) {
// スワップを処理すると主張しながらトークンを盗む
int128 amountSpecified = int128(params.amountSpecified);
BeforeSwapDelta delta = toBeforeSwapDelta(amountSpecified, 0);
return (BaseHook.beforeSwap.selector, delta, 0);
}
検出
beforeSwapReturnDelta: true を持つ ANY hook と相互作用する前に:
- Hook コードを監査 - 正当な用途を検証
- 所有権を確認 - アップグレード可能か? 誰が?
- 実績を確認 - 評判の良い企業による監査を受けているか?
- 小額から始める - 最初に最小額でテスト
正当な使用
NoOp パターンは以下に有効です:
- Just-in-time liquidity (JIT)
- カスタム AMM カーブ
- インテントベース取引システム
- RFQ/PMM 統合
ただし、各々は慎重な実装と監査が必要です。
デルタ会計の基礎
v4 は PoolManager を通じたクレジット/デビットシステムを使用します:
コアイン変量
すべてのトランザクションについて: sum(deltas) == 0
PoolManager は各アドレスが何を負っているか、何を受け取るべきかを追跡します。トランザクション終了時に、すべての債務を決済する必要があります。
主要な関数
| 関数 | 目的 | 方向 |
|---|---|---|
take(currency, to, amount) | PoolManager からトークンを引き出す | トークンを受け取る |
settle(currency) | PoolManager にトークンを支払う | トークンを送信する |
sync(currency) | PoolManager 残高追跡を更新 | settle の準備 |
決済パターン
// 正しいパターン: settle 前に sync
poolManager.sync(currency);
currency.transfer(address(poolManager), amount);
poolManager.settle(currency);
よくある間違い
- sync の忘却: sync がないと決済が失敗
- 順序が違う: sync → transfer → settle の順序が必須
- 部分的な決済: トランザクションを無効な状態のままにする
- 二重決済: 会計エラーを引き起こす
アクセス制御パターン
PoolManager 検証
すべての hook コールバックは呼び出し元を検証する必要があります:
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) {
// 安全に続行可能
}
なぜこれが重要か
このチェックなしでは:
- 誰でも直接 hook 関数を呼び出せる
- 攻撃者は hook 状態を操作できる
- 偽の callback を通じて資金が流出する可能性
ルーター検証パターン
sender パラメータはエンドユーザーではなくルーターです。ユーザー ID が必要な hook の場合:
許可リストパターン
mapping(address => bool) public allowedRouters;
function beforeSwap(
address sender, // これはルーター
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
bytes calldata hookData
) external override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) {
require(allowedRouters[sender], "Router not allowed");
// スワップを続行
}
hookData 経由でのユーザー ID
function beforeSwap(
address sender,
PoolKey calldata key,
IPoolManager.SwapParams calldata params,
bytes calldata hookData
) external override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) {
// hookData からユーザー アドレスをデコード (ルーターがそれを含める必要がある)
address user = abi.decode(hookData, (address));
// 注意: ルーターは正確なユーザーを提供するために信頼される必要があります
}
msg.sender トラップ
// 間違い - hook では msg.sender は常に PoolManager
function beforeSwap(...) external {
require(msg.sender == someUser); // 常に失敗するか、間違い
}
// 正しい - sender パラメータを使用
function beforeSwap(address sender, ...) external {
require(allowedRouters[sender], "Invalid router");
}
トークン処理の危険性
すべてのトークンが標準的な ERC-20 のように動作するわけではありません:
| トークンタイプ | 危険性 | 緩和策 |
|---|---|---|
| 手数料転送 | 受け取り額 < 送信額 | 実際の残高変化を測定 |
| リベース | 転送なしで残高が変化 | 生の残高の保存を避ける |
| ERC-777 | トランザファー callback でリエントランシー可能 | リエントランシー ガードを使用 |
| 一時停止可能 | 転送がブロック可能 | 転送失敗を優雅に処理 |
| ブロックリスト | 特定のアドレスがブロック | 本番アドレスでテスト |
| 低デシマル | 計算での精度喪失 | 適切なスケーリングを使用 |
安全な残高チェック パターン
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;
}
ベース Hook テンプレート
すべてのパーミッションを無効化した状態で開始します。必要なもののみを有効化:
// 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, // 必要な場合のみ有効化
afterSwap: false, // 必要な場合のみ有効化
beforeDonate: false,
afterDonate: false,
beforeSwapReturnDelta: false, // 危険: NoOp 攻撃ベクトル
afterSwapReturnDelta: false, // 危険: 価値を抽出可能
afterAddLiquidityReturnDelta: false,
afterRemoveLiquidityReturnDelta: false
});
}
// 上で有効化したコールバックのみを実装
}
完全な実装テンプレートについては references/base-hook-template.md を参照してください。
セキュリティチェックリスト
任意の hook をデプロイする前に:
| # | チェック項目 | ステータス |
|---|---|---|
| 1 | すべての hook callback が msg.sender == poolManager を検証 | [ ] |
| 2 | 必要な場合、ルーター許可リストが実装されている | [ ] |
| 3 | OOG を引き起こす可能性のある無制限ループがない | [ ] |
| 4 | 外部呼び出しにリエントランシーガードがある | [ ] |
| 5 | デルタ会計が 0 に合計される | [ ] |
| 6 | 手数料転送トークンが処理される | [ ] |
| 7 | ハードコードされたアドレスがない | [ ] |
| 8 | スリッページパラメータが尊重されている | [ ] |
| 9 | 機密データがオンチェーンに保存されていない | [ ] |
| 10 | アップグレードメカニズムが保護されている (該当する場合) | [ ] |
| 11 | beforeSwapReturnDelta が有効な場合、正当化されている | [ ] |
| 12 | ファズテストが完了している | [ ] |
| 13 | イン変量テストが完了している | [ ] |
ガス予算ガイドライン
Hook callback は PoolManager のトランザクションコンテキスト内で実行されます。過度なガス消費はスワップをリバートさせるか、経済的に実行不可能にする可能性があります。
コールバック別ガス予算
| コールバック | 目標予算 | ハード上限 | 注釈 |
|---|---|---|---|
beforeSwap | < 50,000 gas | 150,000 gas | すべてのスワップで実行; 精によく保つ |
afterSwap | < 30,000 gas | 100,000 gas | 分析/追跡のみ |
beforeAddLiquidity | < 50,000 gas | 200,000 gas | アクセス制御を含む可能性 |
afterAddLiquidity | < 30,000 gas | 100,000 gas | 報酬追跡 |
beforeRemoveLiquidity | < 50,000 gas | 200,000 gas | ロック検証 |
afterRemoveLiquidity | < 30,000 gas | 100,000 gas | 追跡/会計 |
| 外部呼び出しを伴うコールバック | < 100,000 gas | 300,000 gas | 外部 DEX ルーティング、オラクル |
よくあるガスの落とし穴
- 無制限ループ: 動的配列 (たとえば、すべてのアクティブなポジション) を反復処理するとブロック ガス制限を超える可能性があります。配列サイズを制限するか、ページネーションを使用してください。
- ホットパスでの SSTORE: 新しいストレージ スロットごとに約 20,000 ガスがかかります。トランザクションを超えて永続化しないデータについては、トランジェント ストレージ (
tstore/tload) を推奨します。Solidity >= 0.8.24 と EVM ターゲットがcancun以降に設定されている必要があります。 - 外部呼び出し: クロスコントラクト呼び出しは約 2,600 ガスのベース コスト、さらに呼び出し先の実行を追加します。可能な限り呼び出しをバッチ処理します。
- 文字列操作: コールバック内で
string操作を避けます。識別子にはbytes32を使用します。 - 冗長な読み取り:
poolManager呼び出しをキャッシュします —getSlot0()やgetLiquidity()の読み取りは毎回ガスがかかります。
ガスの測定
# Foundry で特定の hook コールバックをプロファイル
forge test --match-test test_beforeSwapGas --gas-report
# すべてのテストをまたいでガス使用量をスナップショット
forge snapshot --match-contract MyHookTest
リスク スコアリング システム
hook のリスク スコアを計算します (0-33):
| カテゴリ | ポイント | 基準 |
|---|---|---|
| パーミッション | 0-14 | 有効化されたパーミッション リスク レベルの合計 |
| 外部呼び出し | 0-5 | 外部相互作用の数とタイプ |
| 状態の複雑性 | 0-5 | 変更可能な状態の量 |
| アップグレード メカニズム | 0-5 | プロキシ、管理者機能など |
| トークン処理 | 0-4 | 非標準トークン サポート |
監査レベルの推奨事項
| スコア | リスク レベル | 推奨事項 |
|---|---|---|
| 0-5 | 低 | 自己監査 + ピアレビュー |
| 6-12 | 中 | 専門家による監査が推奨される |
| 13-20 | 高 | 専門家による監査が必須 |
| 21-33 | 致命的 | 複数の監査が必須 |
絶対禁止事項
hook では決してこれらのことをしないでください:
msg.senderをユーザー ID として信頼しない — これは常に PoolManager です- NoOp 攻撃を理解せずに
beforeSwapReturnDeltaを有効化しない - パスワード、キー、個人識別情報をオンチェーン に保存しない
- ETH の転送に
transfer()を使用しない —call{value:}("")を使用 - トークン デシマルを仮定しない — 常にトークンをクエリ
- ランダム性のために
block.timestampを使用しない - 呼び出しでガス制限をハードコードしない
- 外部呼び出しの戻り値を無視しない
- 認可のために
tx.originを使用しない — これはフィッシング ベクトルです。悪意のあるコントラクトは元のユーザーのtx.originで呼び出しをリレーできます
デプロイ前の監査チェックリスト
| # | 項目 | 必須対象 |
|---|---|---|
| 1 | セキュリティ重視の開発者によるコード レビュー | すべての hook |
| 2 | すべてのコールバックの単体テスト | すべての hook |
| 3 | Foundry によるファズ テスト | すべての hook |
| 4 | イン変量テスト | デルタ リターンの hook |
| 5 | メインネット上でのフォーク テスト | すべての hook |
| 6 | ガス プロファイリング | すべての hook |
| 7 | 形式検証 | 致命的な hook |
| 8 | Slither/Mythril 分析 | すべての hook |
| 9 | 外部監査 | 中+ リスク の hook |
| 10 | バグ バウンティ プログラム | 高+ リスク の hook |
| 11 | モニタリング/アラート設定 | すべての本番 hook |
詳細な監査要件については references/audit-checklist.md を参照してください。
本番環境 Hook リファレンス
監査済みの本番 hook から学びます:
| プロジェクト | 説明 | 注目すべきセキュリティ機能 |
|---|---|---|
| Flaunch | トークン起動プラットフォーム | マルチシグ管理者、タイムロック |
| EulerSwap | 貸出統合 | マーケット別リスク分離 |
| Zaha TWAMM | 時間加重 AMM | 段階的実行で MEV を低減 |
| Bunni | LP 管理 | 集中流動性ガード |
外部リソース
公式ドキュメント
セキュリティ リソース
コミュニティ
- @igoryuzo による v4-hooks-skill - このガイドにインスピレーションを与えたコミュニティ スキル
- v4hooks.dev - コミュニティ hook リソース
その他の参考資料
ベース Hook テンプレート- 完全な実装スターター脆弱性カタログ- よくあるパターンと緩和策監査チェックリスト- 詳細なデプロイ前チェックリスト
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- uniswap
- リポジトリ
- uniswap/uniswap-ai
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/uniswap/uniswap-ai / ライセンス: MIT
関連スキル
secure-code-guardian
認証・認可の実装、ユーザー入力の保護、OWASP Top 10の脆弱性対策が必要な場合に使用します。bcrypt/argon2によるパスワードハッシング、パラメータ化ステートメントによるSQLインジェクション対策、CORS/CSPヘッダーの設定、Zodによる入力検証、JWTトークンの構築などのカスタムセキュリティ実装に対応します。認証、認可、入力検証、暗号化、OWASP Top 10対策、セッション管理、セキュリティ強化全般で活用できます。ただし、構築済みのOAuth/SSO統合や単独のセキュリティ監査が必要な場合は、より特化したスキルの検討をお勧めします。
claude-authenticity
APIエンドポイントが本物のClaudeによって支えられているか(ラッパーやプロキシ、偽装ではないか)を、claude-verifyプロジェクトを模した9つの重み付きルールベースチェックで検証できます。また、Claudeの正体を上書きしているプロバイダーから注入されたシステムプロンプトも抽出します。完全に自己完結しており、httpx以外の追加パッケージは不要です。Claude APIキーまたはエンドポイントを検証したい場合、サードパーティのClaudeサービスが本物か確認したい場合、APIプロバイダーのClaude正当性を監査したい場合、複数モデルを並行してテストしたい場合、またはプロバイダーが注入したシステムプロンプトを特定したい場合に使用できます。
anth-security-basics
Anthropic Claude APIのセキュリティベストプラクティスを適用し、キー管理、入力値の検証、プロンプトインジェクション対策を実施します。APIキーの保護、Claudeに送信する前のユーザー入力検証、コンテンツセーフティガードレールの実装が必要な場合に活用できます。「anthropic security」「claude api key security」「secure anthropic」「prompt injection defense」といったフレーズでトリガーされます。
x-ray
x-ray.mdプレ監査レポートを生成します。概要、強化された脅威モデル(プロトコルタイプのプロファイリング、Gitの重み付け攻撃面分析、時間軸リスク分析、コンポーザビリティ依存関係マッピング)、不変条件、統合、ドキュメント品質、テスト分析、開発者・Gitの履歴をカバーしています。「x-ray」「audit readiness」「readiness report」「pre-audit report」「prep this protocol」「protocol prep」「summarize this protocol」のキーワードで実行されます。
semgrep
Semgrepスタティック分析スキャンを実行し、カスタム検出ルールを作成します。Semgrepでのコードスキャン、セキュリティ脆弱性の検出、カスタムYAMLルールの作成、または特定のバグパターンの検出が必要な場合に使用します。重要:ユーザーが「バグをスキャンしたい」「コード品質を確認したい」「脆弱性を見つけたい」「スタティック分析」「セキュリティlint」「コード監査」または「コーディング標準を適用したい」と尋ねた場合も、Semgrepという名称を明記していなくても、このスキルを使用してください。Semgrepは30以上の言語に対応したパターンベースのコードスキャンに最適なツールです。
ghost-bits-cast-attack
Java「ゴーストビッツ」/キャストアタック プレイブック(Black Hat Asia 2026)。16ビット文字が8ビットバイトに暗黙的に縮小されるJavaサービスへの攻撃時に使用します。WAF/IDSを回避して、SQLインジェクション、デシリアライゼーション型RCE、ファイルアップロード(Webシェル)、パストトラバーサル、CRLF インジェクション、リクエストスマグリング、SMTPインジェクションを実行できます。Tomcat、Spring、Jetty、Undertow、Vert.x、Jackson、Fastjson、Apache Commons BCEL、Apache HttpClient、Angus Mail、JDK HttpServer、Lettuce、Jodd、XMLWriterに影響し、WAFバイパスにより多くの「パッチ済み」CVEを再度有効化します。