# V4 Hook Reentrancy Via Unlock

> Detect reentrancy in Uniswap V4 hooks via the PoolManager unlock/lock callback. V4 uses a singleton PoolManager with transient lock state; all pool mutations happen inside an unlockCallback. A hook that makes external calls during beforeSwap/afterSwap/before*Liquidity (to tokens with hooks, arbitrary routers, or user-controlled contracts) can be re-entered, and because the manager is already unlo…

- **Type:** Skill
- **Install:** `agentstack add skill-omermaksutii-rugproof-v4-hook-reentrancy-via-unlock`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [omermaksutii](https://agentstack.voostack.com/s/omermaksutii)
- **Installs:** 0
- **Category:** [Security](https://agentstack.voostack.com/c/security)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [omermaksutii](https://github.com/omermaksutii)
- **Source:** https://github.com/omermaksutii/RugProof/tree/main/rules/uniswap-v4-hooks/skills/v4-hook-reentrancy-via-unlock
- **Website:** https://omermaksutii.github.io/RugProof

## Install

```sh
agentstack add skill-omermaksutii-rugproof-v4-hook-reentrancy-via-unlock
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Uniswap V4 hook reentrancy via unlock detection

## When this applies

Trigger on any of:

- Hook callbacks (`beforeSwap`, `afterSwap`, `beforeAddLiquidity`, `afterRemoveLiquidity`, `beforeDonate`, ...) that perform external calls
- External calls to ERC-777 / ERC-1155 / callback-bearing tokens, arbitrary routers, or user-supplied addresses inside a callback
- A hook that itself calls `poolManager.unlock(...)` or `swap`/`modifyLiquidity`/`take`/`settle` re-entrantly
- Custom `unlockCallback` implementations
- Hook state (fee accumulators, TWAP buffers, custom accounting) read/written across an external call within one callback
- `safeTransfer` / `transferFrom` of tokens that invoke recipient hooks during settlement

## Detection patterns

### External call before state finalize inside a callback (HIGH)
```solidity
function afterSwap(address, PoolKey calldata key, ..., int128) external override returns (bytes4, int128) {
    uint256 reward = _pending[key.toId()];
    rewardToken.safeTransfer(msg.sender, reward);  // ← ERC-777 hook re-enters here
    _pending[key.toId()] = 0;                       // ← cleared AFTER the external call
    return (this.afterSwap.selector, 0);
}
```
During the transfer the recipient re-enters `swap` (manager is unlocked), triggering `afterSwap` again while `_pending` is still non-zero → double reward.
**Signal:** hook state mutated after an external call inside a callback, with the PoolManager unlocked (CEI violated in hook context).

### Recursive unlock / nested swap (HIGH)
```solidity
function beforeSwap(...) external override returns (bytes4, BeforeSwapDelta, uint24) {
    router.call(userData);   // user contract calls poolManager.swap again, re-entrant
    ...
}
```
The transient lock is already held; the manager permits nested operations, so the hook's pre-swap invariants can be violated mid-flight.
**Signal:** arbitrary/user-controlled external call inside a callback whose result feeds the same swap's accounting.

### Read-only reentrancy on hook-exposed price (HIGH)
```solidity
function getTwap(PoolKey calldata key) external view returns (uint256) {
    return _twap[key.toId()];   // read by a victim mid-callback, before this hook updates it
}
```
A consumer reads the hook's oracle while the hook is mid-callback and its accumulator is stale.
**Signal:** a public view exposing hook state that is updated inside a callback, readable during reentrancy. See [[reentrancy]].

## Severity rubric

| Pattern | Severity | Notes |
|---|---|---|
| State cleared after external call in callback → double-spend | **High** | Direct value extraction |
| User-controlled call inside callback enabling nested swap | **High** | Invariant break mid-swap |
| Read-only reentrancy on hook oracle/TWAP | **High** | Often missed, see [[oracle-manipulation]] |
| External call to a fixed, trusted contract only | **Medium** | Bounded by trust assumption |
| Callback with no external calls / pure accounting | **Info** | No reentrancy surface |

## Remediation patterns

1. **CEI inside the hook** — finalize hook state (zero out pending, update accumulators) before any external call within a callback.
2. **Per-hook reentrancy guard** — a transient (`tstore`) guard on callbacks; the PoolManager's own lock does NOT protect hook-internal state.
3. **Avoid callback-bearing tokens** in callbacks, or pull/settle through the manager (`take`/`settle`) rather than raw transfers to arbitrary recipients.
4. **Guard view functions** that expose hook state used as a price, or serve a "settled" snapshot that only updates outside callbacks.
5. **No arbitrary external calls** inside callbacks; restrict to allowlisted, non-reentrant targets.

## False-positive notes

- A callback that performs no external calls and only mutates its own storage has no reentrancy path — Info.
- Calls strictly to the PoolManager's `take`/`settle`/`sync` (no recipient hooks) don't re-enter the hook.
- A callback already wrapped in a transient reentrancy guard with state finalized first — downgrade.

## Related

- [[reentrancy]]
- [[v4-hook-delta-accounting]] — settlement ordering interacts with reentrancy
- [[oracle-manipulation]] — read-only reentrancy on hook TWAP
- [[token-compatibility]] — ERC-777 hooks widen the surface

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [omermaksutii](https://github.com/omermaksutii)
- **Source:** [omermaksutii/RugProof](https://github.com/omermaksutii/RugProof)
- **License:** MIT
- **Homepage:** https://omermaksutii.github.io/RugProof

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-omermaksutii-rugproof-v4-hook-reentrancy-via-unlock
- Seller: https://agentstack.voostack.com/s/omermaksutii
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
