From 0bc98a5c777ce384636290db2b015abc10396783 Mon Sep 17 00:00:00 2001 From: Nick Koutrelakos Date: Sat, 8 Aug 2026 18:09:34 -0700 Subject: [PATCH 1/3] docs(v4): add AI-augmented hook development guide Adds a guide that walks through the full v4 hook lifecycle (scaffold, implement, test, audit, deploy) using an AI coding agent alongside the v4-template, backed by the existing v4-security-foundations and viem-integration skills. Prompts are agent-agnostic plain text, not Claude-specific. Closes ECO-144. --- .../hooks/ai-augmented-hook-development.mdx | 156 ++++++++++++++++++ .../v4/guides/hooks/getting-started.mdx | 1 + content/protocols/v4/guides/hooks/meta.json | 3 +- 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx diff --git a/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx b/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx new file mode 100644 index 000000000..0008d5c62 --- /dev/null +++ b/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx @@ -0,0 +1,156 @@ +--- +title: AI-Augmented Hook Development +description: Scaffold, implement, test, audit, and deploy a Uniswap v4 hook end to end using AI coding-agent skills alongside the v4-template. +--- + +## What You'll Build + +A v4 hook developed end to end with an AI coding agent: scaffolded from the official [v4-template](https://github.com/uniswapfoundation/v4-template) with a security-first base, implemented with the hook logic you need, tested with Foundry (unit, fuzz, and invariant tests), reviewed against a structured security checklist, and deployed with a small TypeScript script. + +Each step below pairs a plain-language prompt with the underlying Uniswap AI skill that backs it. The prompts are agent-agnostic — they work with any coding agent that can read a repository and run shell commands, whether or not it has the skill installed. If your agent supports the [Skills CLI](#install-the-skills) or the Claude Code plugin marketplace, install the skills first so the agent has the full reference material (threat models, checklists, code templates) loaded as context; otherwise the prompts still work, just with less structured guidance behind them. + +## Prerequisites + +- [Foundry](https://book.getfoundry.sh/getting-started/installation) installed +- Node.js and npm (for the deployment script) +- An AI coding agent — Claude Code, Cursor, or any agent that can read files and run shell commands +- Familiarity with the v4 hook lifecycle — see [Hooks Overview](/docs/protocols/v4/guides/hooks/getting-started) if this is your first hook + +## Install the Skills + +This guide uses two existing [Uniswap AI](/docs/uniswap-ai/overview) skills — no new skills are needed: + +- **`v4-security-foundations`** — threat model, permission-flag risk matrix, gas budgets, and a pre-deployment audit checklist for v4 hooks +- **`viem-integration`** — TypeScript client setup and deployment patterns using [viem](https://viem.sh) + +Install them with the Skills CLI (works with any compatible agent): + +```bash +npx skills add Uniswap/uniswap-ai +``` + +Or, if you use Claude Code: + +```text +/plugin marketplace add uniswap/uniswap-ai +/plugin install uniswap-hooks +/plugin install uniswap-viem +``` + +See [Uniswap AI Overview](/docs/uniswap-ai/overview) for other install paths and [Uniswap Skills](/docs/uniswap-ai/skills) for the full skill catalog. + +## Step 1: Scaffold + +Start from the official template rather than an empty Foundry project — it ships with `v4-core` and `v4-periphery` pre-installed and the remappings already configured. + +```bash +git clone https://github.com/uniswapfoundation/v4-template.git my-hook +cd my-hook +forge install +forge test +``` + +Prompt your agent to scaffold the hook contract with a security-first permission set — start every permission disabled and enable only what the hook actually needs: + +```text +Using this v4-template repo, create a new hook contract at src/MyHook.sol +that extends BaseHook. Start with all 14 permission flags in +getHookPermissions() set to false, then enable only the flags this hook +needs: [describe the swap/liquidity behavior you want, e.g. "afterSwap, +to track a per-user counter"]. Follow the caller-verification pattern — +every callback must be safe to call only from the PoolManager. Do not +enable beforeSwapReturnDelta or afterSwapReturnDelta unless I explicitly +ask for a NoOp/custom-curve pattern. +``` + +With `v4-security-foundations` installed, the agent has the permission-flag risk matrix (which flags are LOW vs. CRITICAL risk) and the base hook template already in context, so it will default to the minimal-permission scaffold rather than enabling flags speculatively. + +## Step 2: Implement + +Describe the hook's actual behavior in plain language and let the agent fill in the callback bodies. Be explicit about anything that touches user identity or external calls — these are the two areas most likely to introduce vulnerabilities. + +```text +Implement the [afterSwap / beforeAddLiquidity / ...] callback in MyHook.sol +to [describe the behavior]. Remember: msg.sender in a hook callback is +always the PoolManager, never the end user — if this hook needs to know +who the user is, use the sender parameter (which identifies the router) +or decode it from hookData, and note which one you chose and why. +``` + +If the hook needs to identify the end user (not just the router), have the agent implement the `hookData` encode/decode helpers rather than trusting `sender` directly — a router can be shared by many callers. + +## Step 3: Test + +Ask for the full pyramid — unit, fuzz, and invariant tests — rather than accepting a single happy-path test. + +```text +Write a Foundry test suite for MyHook.sol in test/MyHook.t.sol, using the +existing Fixtures/EasyPosm test utilities from this template. Include: +1. Unit tests for each hook callback's happy path. +2. Fuzz tests for any function that takes a numeric input (swap amounts, + liquidity amounts) — vary the input across realistic and edge-case + ranges. +3. An invariant test if the hook maintains any accounting invariant + (e.g. a running total that should never go negative, or a delta sum + that should always net to zero). +Run the suite and show me the results, including a gas report. +``` + +```bash +forge test --gas-report +``` + +## Step 4: Audit + +Run a structured review before deployment rather than a general "does this look okay" pass. The `v4-security-foundations` skill's checklist gives the agent a concrete rubric instead of an open-ended judgment call. + +```text +Review MyHook.sol against a v4 hook security checklist: +1. Does every callback verify it's being called by the PoolManager? +2. Is the sender parameter used correctly (it's the router, not the + user) everywhere the hook needs identity? +3. Are there any unbounded loops that could hit the block gas limit? +4. Does any external call lack a reentrancy guard? +5. If any ReturnDelta permission is enabled, is the NoOp/custom-accounting + pattern justified and documented? +6. Are fee-on-transfer or rebasing tokens handled safely, if relevant? +7. Estimate a risk score and tell me whether this needs a professional + audit before mainnet deployment. +Report each finding with a file and line reference. +``` + +Treat "self-audit + peer review is enough" only for genuinely low-risk hooks (no `ReturnDelta` permissions, no external calls, no upgrade mechanism). Anything that enables `beforeSwapReturnDelta`/`afterSwapReturnDelta`, holds funds, or is upgradeable should get a professional audit — the agent's review is a pre-audit filter, not a substitute for one. + +## Step 5: Deploy + +Use `viem-integration` for the deployment script rather than hand-rolling transaction signing. + +```text +Write a TypeScript deployment script using viem that: +1. Reads the private key from process.env.PRIVATE_KEY (never hardcode it). +2. Creates a WalletClient for [target chain/testnet]. +3. Deploys MyHook.sol with constructor arg poolManagerAddress. +4. Confirms the deployed address encodes the same permission flags as + getHookPermissions() returns (hook addresses in v4 encode their + permissions — a mismatch means the deployment is broken). +5. Prints the deployed address and a link to the block explorer. +``` + +Before running the script against a real network, review it yourself — signing and broadcasting a deployment transaction is an action you should confirm manually, not delegate to unattended agent execution. + +## Customization Ideas + +- Swap the permission set for a different lifecycle stage (e.g. `beforeSwap` + `beforeSwapReturnDelta` for a custom-curve AMM) and re-run Step 4's audit prompt — the checklist output will change meaningfully once a `ReturnDelta` permission is in play. +- Point the Step 3 prompt at [OpenZeppelin's Hooks Library](https://docs.openzeppelin.com/uniswap-hooks/1.x/) base contracts (`BaseCustomAccounting`, `BaseDynamicFee`, etc., available in v4-template) instead of a bare `BaseHook` to skip re-implementing common patterns. +- Chain the Step 5 script into a CI job that deploys to a testnet on every merge, using the same viem client setup. + +## Where to Go Next + +- [Hooks Overview](/docs/protocols/v4/guides/hooks/getting-started) — local environment setup if you haven't built a hook before +- [Hook Deployment](/docs/protocols/v4/guides/hooks/hook-deployment) — how hook address flags work, in depth +- [Uniswap Skills](/docs/uniswap-ai/skills) — full catalog of available Uniswap AI skills +- [v4-template repository](https://github.com/uniswapfoundation/v4-template) + +## Legal Disclaimer + +Code generated by an AI agent, including in this guide, has not been audited. See the [Usage Guidelines](https://github.com/Uniswap/uniswap-ai/blob/main/DISCLAIMER.md) before deploying any hook with real funds. diff --git a/content/protocols/v4/guides/hooks/getting-started.mdx b/content/protocols/v4/guides/hooks/getting-started.mdx index 89a444a94..084abd372 100644 --- a/content/protocols/v4/guides/hooks/getting-started.mdx +++ b/content/protocols/v4/guides/hooks/getting-started.mdx @@ -254,3 +254,4 @@ The library includes: - [AsyncSwap Hooks](/docs/protocols/v4/guides/hooks/async-swap) - [Access msg.sender Inside a Hook](/docs/protocols/v4/guides/hooks/accessing-msg.sender) - [Hook Deployment](/docs/protocols/v4/guides/hooks/hook-deployment) +- [AI-Augmented Hook Development](/docs/protocols/v4/guides/hooks/ai-augmented-hook-development) diff --git a/content/protocols/v4/guides/hooks/meta.json b/content/protocols/v4/guides/hooks/meta.json index 8e3ab2e88..602befe87 100644 --- a/content/protocols/v4/guides/hooks/meta.json +++ b/content/protocols/v4/guides/hooks/meta.json @@ -6,6 +6,7 @@ "liquidity-hooks", "async-swap", "accessing-msg.sender", - "hook-deployment" + "hook-deployment", + "ai-augmented-hook-development" ] } From 7e18ea6cfd301266f18fba24102901ea804b6690 Mon Sep 17 00:00:00 2001 From: Nick Koutrelakos Date: Sat, 8 Aug 2026 18:12:40 -0700 Subject: [PATCH 2/3] fix: address independent review of AI-augmented hook guide - Deploy prompt now mines a CREATE2 salt through the deployer proxy instead of a plain CREATE deploy, which would produce a hook address that doesn't encode the enabled permission flags. - Clarify that neither sender nor hookData is trustworthy for user identity without router allowlisting, and link the existing accessing-msg.sender guide. - Move the OpenZeppelin base-contract customization tip to Step 1 (scaffold), where base-contract choice actually happens. --- .../hooks/ai-augmented-hook-development.mdx | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx b/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx index 0008d5c62..02f2fc461 100644 --- a/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx +++ b/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx @@ -77,7 +77,7 @@ who the user is, use the sender parameter (which identifies the router) or decode it from hookData, and note which one you chose and why. ``` -If the hook needs to identify the end user (not just the router), have the agent implement the `hookData` encode/decode helpers rather than trusting `sender` directly — a router can be shared by many callers. +If the hook needs to identify the end user (not just the router), neither `sender` nor `hookData` is trustworthy on its own — `sender` identifies the router, not the user, and `hookData` is caller-supplied calldata that any router could populate falsely. Both only become safe once the router itself is allowlisted; see [Access msg.sender Inside a Hook](/docs/protocols/v4/guides/hooks/accessing-msg.sender) for the allowlisting pattern before asking the agent to decode identity from either. ## Step 3: Test @@ -123,17 +123,23 @@ Treat "self-audit + peer review is enough" only for genuinely low-risk hooks (no ## Step 5: Deploy -Use `viem-integration` for the deployment script rather than hand-rolling transaction signing. +Use `viem-integration` for the deployment script rather than hand-rolling transaction signing. A v4 hook can't be deployed with a plain CREATE — its address must encode the enabled permission flags, which means mining a CREATE2 `salt` first (see [Hook Deployment](/docs/protocols/v4/guides/hooks/hook-deployment) for why). ```text Write a TypeScript deployment script using viem that: 1. Reads the private key from process.env.PRIVATE_KEY (never hardcode it). 2. Creates a WalletClient for [target chain/testnet]. -3. Deploys MyHook.sol with constructor arg poolManagerAddress. -4. Confirms the deployed address encodes the same permission flags as - getHookPermissions() returns (hook addresses in v4 encode their - permissions — a mismatch means the deployment is broken). -5. Prints the deployed address and a link to the block explorer. +3. Mines a CREATE2 salt for MyHook.sol that produces an address encoding + the same permission flags as getHookPermissions() returns — either by + shelling out to Foundry's HookMiner (forge script) or by replicating + its search in TypeScript against the same CREATE2 deployer proxy + (0x4e59b44847b379578588920cA78FbF26c0B4956C on most chains). +4. Deploys MyHook.sol through that CREATE2 deployer proxy using the mined + salt and constructor arg poolManagerAddress. +5. Confirms the deployed address matches the mined address exactly — a + mismatch means the deployment is broken and the pool will reject the + hook at initialization. +6. Prints the deployed address and a link to the block explorer. ``` Before running the script against a real network, review it yourself — signing and broadcasting a deployment transaction is an action you should confirm manually, not delegate to unattended agent execution. @@ -141,7 +147,7 @@ Before running the script against a real network, review it yourself — signing ## Customization Ideas - Swap the permission set for a different lifecycle stage (e.g. `beforeSwap` + `beforeSwapReturnDelta` for a custom-curve AMM) and re-run Step 4's audit prompt — the checklist output will change meaningfully once a `ReturnDelta` permission is in play. -- Point the Step 3 prompt at [OpenZeppelin's Hooks Library](https://docs.openzeppelin.com/uniswap-hooks/1.x/) base contracts (`BaseCustomAccounting`, `BaseDynamicFee`, etc., available in v4-template) instead of a bare `BaseHook` to skip re-implementing common patterns. +- Point the Step 1 scaffold prompt at [OpenZeppelin's Hooks Library](https://docs.openzeppelin.com/uniswap-hooks/1.x/) base contracts (`BaseCustomAccounting`, `BaseDynamicFee`, etc., available in v4-template) instead of a bare `BaseHook` to skip re-implementing common patterns. - Chain the Step 5 script into a CI job that deploys to a testnet on every merge, using the same viem client setup. ## Where to Go Next From be632a3ffd804633eeaaccad96f303bd8e1bb767 Mon Sep 17 00:00:00 2001 From: Nick Koutrelakos Date: Mon, 17 Aug 2026 17:56:36 -0700 Subject: [PATCH 3/3] fix(docs): correct v4-template and skill claims in AI-augmented hook guide Verified every factual claim against Uniswap/v4-template@main and Uniswap/uniswap-ai@main. Several were stale or wrong. Template facts: - The test harness is `BaseTest` (inheriting `Deployers`) plus `EasyPosm`. `Fixtures` no longer exists. Step 3's prompt now names the real files, matching what your-first-hook.mdx already says. - The example hook extends OpenZeppelin's `BaseHook` and overrides the internal underscore-prefixed callbacks. Scaffolding against a bare v4-periphery `BaseHook` would not compile in this template. The OZ hooks library is the template's primary dependency, not an optional swap-in. - v4-core and v4-periphery are not direct submodules; they resolve through `lib/uniswap-hooks`. Reworded "pre-installed" to describe the remappings. - Added the Foundry stable-channel requirement and `foundryup`, which the template README calls out explicitly. - Named `src/Counter.sol` and the four permissions it enables, since the first `forge test` runs against it. Deployment: - The template ships `script/00_DeployHook.s.sol`, which already mines the salt with v4-periphery's `HookMiner` and deploys through the CREATE2 proxy. Step 5 now adapts that script instead of asking the agent to re-implement salt mining in TypeScript. - `viem-integration` contains no contract-deployment material, so it now backs post-deploy verification and app integration, which is what it covers. Its description no longer claims "deployment patterns". - Added the anvil-first local flow and the `BaseScript.sol` fields that must be set, including `hookContract`. Security: - The Step 2 prompt offered a choice between `sender` and `hookData`, both untrustworthy, and corrected it only in later prose. It now requires the router-allowlist plus `msgSender()` pattern from accessing-msg.sender. - That prompt and a new Step 4 checklist item now require the allowlist setters be owner-restricted. The linked page writes them as bare `external` functions, so an agent copying it literally produces an allowlist anyone can write to. Reviewed by an independent fresh-context reviewer; its findings are included above. Co-Authored-By: Claude Opus 5 --- .../hooks/ai-augmented-hook-development.mdx | 124 +++++++++++------- 1 file changed, 79 insertions(+), 45 deletions(-) diff --git a/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx b/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx index 02f2fc461..5f8c8709e 100644 --- a/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx +++ b/content/protocols/v4/guides/hooks/ai-augmented-hook-development.mdx @@ -5,14 +5,14 @@ description: Scaffold, implement, test, audit, and deploy a Uniswap v4 hook end ## What You'll Build -A v4 hook developed end to end with an AI coding agent: scaffolded from the official [v4-template](https://github.com/uniswapfoundation/v4-template) with a security-first base, implemented with the hook logic you need, tested with Foundry (unit, fuzz, and invariant tests), reviewed against a structured security checklist, and deployed with a small TypeScript script. +A v4 hook developed end to end with an AI coding agent: scaffolded from the [v4-template](https://github.com/uniswapfoundation/v4-template) with a security-first permission set, implemented with the hook logic you need, tested with Foundry (unit, fuzz, and invariant tests), reviewed against a structured security checklist, and deployed with the template's Foundry script. Each step below pairs a plain-language prompt with the underlying Uniswap AI skill that backs it. The prompts are agent-agnostic — they work with any coding agent that can read a repository and run shell commands, whether or not it has the skill installed. If your agent supports the [Skills CLI](#install-the-skills) or the Claude Code plugin marketplace, install the skills first so the agent has the full reference material (threat models, checklists, code templates) loaded as context; otherwise the prompts still work, just with less structured guidance behind them. ## Prerequisites -- [Foundry](https://book.getfoundry.sh/getting-started/installation) installed -- Node.js and npm (for the deployment script) +- [Foundry](https://book.getfoundry.sh/getting-started/installation) on the **stable** channel — the template documents compatibility issues on Foundry Nightly, so run `foundryup` before you start +- Node.js and npm, if you plan to build a TypeScript integration on top of the deployed hook - An AI coding agent — Claude Code, Cursor, or any agent that can read files and run shell commands - Familiarity with the v4 hook lifecycle — see [Hooks Overview](/docs/protocols/v4/guides/hooks/getting-started) if this is your first hook @@ -21,7 +21,7 @@ Each step below pairs a plain-language prompt with the underlying Uniswap AI ski This guide uses two existing [Uniswap AI](/docs/uniswap-ai/overview) skills — no new skills are needed: - **`v4-security-foundations`** — threat model, permission-flag risk matrix, gas budgets, and a pre-deployment audit checklist for v4 hooks -- **`viem-integration`** — TypeScript client setup and deployment patterns using [viem](https://viem.sh) +- **`viem-integration`** — TypeScript client setup and contract-interaction patterns using [viem](https://viem.sh) Install them with the Skills CLI (works with any compatible agent): @@ -41,7 +41,9 @@ See [Uniswap AI Overview](/docs/uniswap-ai/overview) for other install paths and ## Step 1: Scaffold -Start from the official template rather than an empty Foundry project — it ships with `v4-core` and `v4-periphery` pre-installed and the remappings already configured. +Start from the template rather than an empty Foundry project — its remappings are already configured for `v4-core`, `v4-periphery`, and [OpenZeppelin's Uniswap Hooks library](https://docs.openzeppelin.com/uniswap-hooks/1.x/), and it ships a working example hook, test harness, and deployment scripts. + +The template is meant to be consumed with the [`Use this template`](https://github.com/new?template_name=v4-template&template_owner=uniswapfoundation) button, which gives you your own repository. You can also clone it directly: ```bash git clone https://github.com/uniswapfoundation/v4-template.git my-hook @@ -50,42 +52,62 @@ forge install forge test ``` +`forge test` runs against the template's example hook, `src/Counter.sol`, which counts `beforeSwap`, `afterSwap`, `beforeAddLiquidity`, and `beforeRemoveLiquidity` calls — four of the fourteen permissions enabled, and a useful reference for how `getHookPermissions()` and the callbacks line up. A green run confirms your toolchain and the nested dependencies resolved correctly. Keep `Counter.sol` as a reference while you build, or replace it once your own hook compiles. + Prompt your agent to scaffold the hook contract with a security-first permission set — start every permission disabled and enable only what the hook actually needs: ```text -Using this v4-template repo, create a new hook contract at src/MyHook.sol -that extends BaseHook. Start with all 14 permission flags in -getHookPermissions() set to false, then enable only the flags this hook -needs: [describe the swap/liquidity behavior you want, e.g. "afterSwap, -to track a per-user counter"]. Follow the caller-verification pattern — -every callback must be safe to call only from the PoolManager. Do not -enable beforeSwapReturnDelta or afterSwapReturnDelta unless I explicitly -ask for a NoOp/custom-curve pattern. +Using this v4-template repo, create a new hook contract at src/MyHook.sol. +Follow the same conventions as the template's existing src/Counter.sol: +extend the OpenZeppelin BaseHook from +@openzeppelin/uniswap-hooks/src/base/BaseHook.sol, and override the +internal underscore-prefixed callbacks (_beforeSwap, _afterSwap, etc.), +not the external ones. + +Start with all 14 permission flags in getHookPermissions() set to false, +then enable only the flags this hook needs: [describe the swap/liquidity +behavior you want, e.g. "afterSwap, to track a per-user counter"]. Every +callback must be safe to call only from the PoolManager. Do not enable +beforeSwapReturnDelta or afterSwapReturnDelta unless I explicitly ask for +a NoOp/custom-curve pattern. ``` -With `v4-security-foundations` installed, the agent has the permission-flag risk matrix (which flags are LOW vs. CRITICAL risk) and the base hook template already in context, so it will default to the minimal-permission scaffold rather than enabling flags speculatively. +With `v4-security-foundations` installed, the agent has the permission-flag risk matrix (which flags are LOW versus CRITICAL risk) and the base hook template already in context, so it will default to the minimal-permission scaffold rather than enabling flags speculatively. ## Step 2: Implement Describe the hook's actual behavior in plain language and let the agent fill in the callback bodies. Be explicit about anything that touches user identity or external calls — these are the two areas most likely to introduce vulnerabilities. +Identity is the trap worth naming up front. Inside a callback, `msg.sender` is always the `PoolManager`. The `sender` parameter is the contract that called `PoolManager.swap()` — a router, not the end user — and `hookData` is caller-supplied calldata that any router could populate falsely. Neither is trustworthy on its own. The safe pattern is to allowlist trusted routers and read the original caller back from the router, which [Access msg.sender Inside a Hook](/docs/protocols/v4/guides/hooks/accessing-msg.sender) walks through in full. Put that constraint in the prompt rather than correcting it afterward: + ```text Implement the [afterSwap / beforeAddLiquidity / ...] callback in MyHook.sol -to [describe the behavior]. Remember: msg.sender in a hook callback is -always the PoolManager, never the end user — if this hook needs to know -who the user is, use the sender parameter (which identifies the router) -or decode it from hookData, and note which one you chose and why. +to [describe the behavior]. If this hook needs to know which EOA or smart +account initiated the swap, do not read it from the sender parameter or +from hookData directly — both identify or are supplied by the router. +Instead, maintain an allowlist of trusted routers in the hook, verify the +sender against that allowlist, and call the router's msgSender() view +function to recover the original caller. The functions that add or remove +routers must be restricted to an owner or admin role — an allowlist that +anyone can write to gives an attacker a trivial way to spoof any identity. +Tell me which routers you assumed are trusted and who controls the +allowlist. ``` -If the hook needs to identify the end user (not just the router), neither `sender` nor `hookData` is trustworthy on its own — `sender` identifies the router, not the user, and `hookData` is caller-supplied calldata that any router could populate falsely. Both only become safe once the router itself is allowlisted; see [Access msg.sender Inside a Hook](/docs/protocols/v4/guides/hooks/accessing-msg.sender) for the allowlisting pattern before asking the agent to decode identity from either. +The access-control point is worth checking by hand afterward. The `addRouter` and `removeRouter` examples in [Access msg.sender Inside a Hook](/docs/protocols/v4/guides/hooks/accessing-msg.sender) are written as bare `external` functions to keep the example short, so an agent that copies them literally produces an allowlist any address can write to. + +If the hook does not need user identity at all, say so in the prompt — it is the simpler and safer design, and an agent will otherwise tend to add identity plumbing you did not ask for. ## Step 3: Test Ask for the full pyramid — unit, fuzz, and invariant tests — rather than accepting a single happy-path test. ```text -Write a Foundry test suite for MyHook.sol in test/MyHook.t.sol, using the -existing Fixtures/EasyPosm test utilities from this template. Include: +Write a Foundry test suite for MyHook.sol in test/MyHook.t.sol. Follow the +template's existing test/Counter.t.sol: extend BaseTest from +test/utils/BaseTest.sol (which inherits Deployers) and use the EasyPosm +library from test/utils/libraries/EasyPosm.sol for position management. +Include: 1. Unit tests for each hook callback's happy path. 2. Fuzz tests for any function that takes a numeric input (swap amounts, liquidity amounts) — vary the input across realistic and edge-case @@ -100,6 +122,8 @@ Run the suite and show me the results, including a gas report. forge test --gas-report ``` +Check the gas report against the per-callback budgets in `v4-security-foundations`. A callback that runs on every swap is on the hot path, and a hook that is too expensive will simply not get used. + ## Step 4: Audit Run a structured review before deployment rather than a general "does this look okay" pass. The `v4-security-foundations` skill's checklist gives the agent a concrete rubric instead of an open-ended judgment call. @@ -111,52 +135,62 @@ Review MyHook.sol against a v4 hook security checklist: user) everywhere the hook needs identity? 3. Are there any unbounded loops that could hit the block gas limit? 4. Does any external call lack a reentrancy guard? -5. If any ReturnDelta permission is enabled, is the NoOp/custom-accounting +5. Is every state-changing admin function (router allowlists, fee + setters, pause switches) restricted to an owner or role? +6. If any ReturnDelta permission is enabled, is the NoOp/custom-accounting pattern justified and documented? -6. Are fee-on-transfer or rebasing tokens handled safely, if relevant? -7. Estimate a risk score and tell me whether this needs a professional +7. Are fee-on-transfer or rebasing tokens handled safely, if relevant? +8. Estimate a risk score and tell me whether this needs a professional audit before mainnet deployment. Report each finding with a file and line reference. ``` -Treat "self-audit + peer review is enough" only for genuinely low-risk hooks (no `ReturnDelta` permissions, no external calls, no upgrade mechanism). Anything that enables `beforeSwapReturnDelta`/`afterSwapReturnDelta`, holds funds, or is upgradeable should get a professional audit — the agent's review is a pre-audit filter, not a substitute for one. +Treat "self-audit plus peer review is enough" only for genuinely low-risk hooks (no `ReturnDelta` permissions, no external calls, no upgrade mechanism). Anything that enables `beforeSwapReturnDelta` or `afterSwapReturnDelta`, holds funds, or is upgradeable should get a professional audit — the agent's review is a pre-audit filter, not a substitute for one. ## Step 5: Deploy -Use `viem-integration` for the deployment script rather than hand-rolling transaction signing. A v4 hook can't be deployed with a plain CREATE — its address must encode the enabled permission flags, which means mining a CREATE2 `salt` first (see [Hook Deployment](/docs/protocols/v4/guides/hooks/hook-deployment) for why). +A v4 hook cannot be deployed with a plain `CREATE`. Its address has to encode the permission flags the hook enables, which means mining a `CREATE2` salt first — see [Hook Deployment](/docs/protocols/v4/guides/hooks/hook-deployment) for how the encoding works. + +The template already implements this in `script/00_DeployHook.s.sol`, which mines a salt with `HookMiner` from `v4-periphery` and deploys through the `CREATE2` proxy at `0x4e59b44847b379578588920cA78FbF26c0B4956C`. Point the agent at that script rather than asking it to write a deployer from scratch: + +```text +Adapt script/00_DeployHook.s.sol in this repo to deploy MyHook instead of +Counter. Update the flags variable so it matches exactly the permissions +MyHook's getHookPermissions() returns, update the constructor args, and +leave the HookMiner and CREATE2 proxy logic as it is. Then explain, line +by line, what the script will do when I broadcast it, and tell me what I +still need to fill in before running it against a real network. +``` + +Test the whole flow against a local [anvil](https://book.getfoundry.sh/anvil/) node before touching a public network. The template's numbered scripts (`00_DeployHook`, `01_CreatePoolAndAddLiquidity`, `02_AddLiquidity`, `03_Swap`) run the full lifecycle locally, and `script/base/BaseScript.sol` holds the configuration you need to edit first — `token0`, `token1`, and `hookContract`, which defaults to the zero address and must be set to your mined hook address before scripts `01` through `03` do anything useful. + +Once the hook is deployed, use `viem-integration` to build the TypeScript side — reading hook state, and calling the hook or the pool from an app: ```text -Write a TypeScript deployment script using viem that: -1. Reads the private key from process.env.PRIVATE_KEY (never hardcode it). -2. Creates a WalletClient for [target chain/testnet]. -3. Mines a CREATE2 salt for MyHook.sol that produces an address encoding - the same permission flags as getHookPermissions() returns — either by - shelling out to Foundry's HookMiner (forge script) or by replicating - its search in TypeScript against the same CREATE2 deployer proxy - (0x4e59b44847b379578588920cA78FbF26c0B4956C on most chains). -4. Deploys MyHook.sol through that CREATE2 deployer proxy using the mined - salt and constructor arg poolManagerAddress. -5. Confirms the deployed address matches the mined address exactly — a - mismatch means the deployment is broken and the pool will reject the - hook at initialization. -6. Prints the deployed address and a link to the block explorer. +Write a TypeScript script using viem that connects to [target chain], +reads the deployed MyHook contract at [address], and verifies it is live: +confirm the deployed bytecode is non-empty, and read back [the specific +hook state you want to check]. Read any private key from process.env, +never hardcode it. Use a PublicClient for reads and only introduce a +WalletClient if a transaction is actually required. ``` -Before running the script against a real network, review it yourself — signing and broadcasting a deployment transaction is an action you should confirm manually, not delegate to unattended agent execution. +Review any script yourself before it signs or broadcasts a transaction. Broadcasting a deployment is an action to confirm manually, not to delegate to unattended agent execution. ## Customization Ideas -- Swap the permission set for a different lifecycle stage (e.g. `beforeSwap` + `beforeSwapReturnDelta` for a custom-curve AMM) and re-run Step 4's audit prompt — the checklist output will change meaningfully once a `ReturnDelta` permission is in play. -- Point the Step 1 scaffold prompt at [OpenZeppelin's Hooks Library](https://docs.openzeppelin.com/uniswap-hooks/1.x/) base contracts (`BaseCustomAccounting`, `BaseDynamicFee`, etc., available in v4-template) instead of a bare `BaseHook` to skip re-implementing common patterns. -- Chain the Step 5 script into a CI job that deploys to a testnet on every merge, using the same viem client setup. +- Swap the permission set for a different lifecycle stage (for example `beforeSwap` plus `beforeSwapReturnDelta` for a custom-curve AMM) and re-run Step 4's audit prompt — the checklist output changes meaningfully once a `ReturnDelta` permission is in play. +- Point the Step 1 scaffold prompt at a more specialized base contract from the OpenZeppelin Hooks library already vendored in the template — `BaseCustomAccounting`, `BaseCustomCurve`, `BaseDynamicFee`, and others — instead of the plain `BaseHook`, to skip re-implementing common patterns. +- Chain the Step 5 scripts into a CI job that redeploys to a testnet on every merge, then runs the viem verification script against the fresh address. ## Where to Go Next - [Hooks Overview](/docs/protocols/v4/guides/hooks/getting-started) — local environment setup if you haven't built a hook before +- [Building Your First Hook](/docs/protocols/v4/guides/hooks/your-first-hook) — the same lifecycle written by hand, without an agent - [Hook Deployment](/docs/protocols/v4/guides/hooks/hook-deployment) — how hook address flags work, in depth - [Uniswap Skills](/docs/uniswap-ai/skills) — full catalog of available Uniswap AI skills - [v4-template repository](https://github.com/uniswapfoundation/v4-template) ## Legal Disclaimer -Code generated by an AI agent, including in this guide, has not been audited. See the [Usage Guidelines](https://github.com/Uniswap/uniswap-ai/blob/main/DISCLAIMER.md) before deploying any hook with real funds. +Code generated by an AI agent, including code produced by the prompts in this guide, has not been audited. See the [Usage Guidelines](https://github.com/Uniswap/uniswap-ai/blob/main/DISCLAIMER.md) for important information about intended use and financial information disclaimers.