# V4 Hook Delta Accounting

> Detect Uniswap V4 hooks that fail to settle currency deltas with the PoolManager. Every credit/debit a hook creates (BeforeSwapDelta, afterSwap hookDelta, take/mint, donate, settle/sync) is tracked in the manager's transient nonzeroDeltaCount; if the books aren't flat when unlock returns, the whole transaction reverts (CurrencyNotSettled), and mismatched take/settle/donate either strands hook fun…

- **Type:** Skill
- **Install:** `agentstack add skill-omermaksutii-rugproof-v4-hook-delta-accounting`
- **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-delta-accounting
- **Website:** https://omermaksutii.github.io/RugProof

## Install

```sh
agentstack add skill-omermaksutii-rugproof-v4-hook-delta-accounting
```

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

## About

# Uniswap V4 hook delta-accounting detection

## When this applies

Trigger on any of:

- Callbacks returning `BeforeSwapDelta` or a non-zero `int128` `hookDelta` from `afterSwap`/`afterAddLiquidity`/`afterRemoveLiquidity`
- Calls to `poolManager.take`, `settle`, `sync`, `mint`, `burn`, `donate`, `clear`
- Custom `unlockCallback` that moves currency in/out of the manager
- Hooks that charge custom fees, skim, or rebate by adjusting deltas
- `currencyDelta` reads, or accounting that must net to zero before `unlock` returns
- Donations to a pool, or take/settle pairs that should balance

## Detection patterns

### Hook takes currency but never settles (HIGH)
```solidity
function afterSwap(address, PoolKey calldata key, ..., BalanceDelta, bytes calldata)
    external override returns (bytes4, int128)
{
    poolManager.take(key.currency0, address(this), feeAmount);  // ← creates a -debt for the hook
    return (this.afterSwap.selector, 0);                         // ← returns 0 delta, never settles
}
```
`take` debits the hook's currency balance in the manager; with no matching `settle`/returned delta, `nonzeroDeltaCount != 0` and the entire `unlock` reverts `CurrencyNotSettled` — every swap on the pool reverts.
**Signal:** `take`/`mint` without a balancing `settle`/`burn` or a non-zero returned `hookDelta` accounting for it.

### Returned delta not backed by a real transfer (HIGH)
```solidity
return (this.afterSwap.selector, int128(feeAmount));   // claims to owe the pool feeAmount...
// ...but the hook never `sync` + `settle`s those tokens into the manager
```
A positive `hookDelta` says "the hook owes the pool X"; if the hook never actually transfers and settles X, the books don't flatten → revert, or in the inverse direction the swapper leaves with unpaid debt.
**Signal:** non-zero returned delta with no corresponding `sync`/`settle` (or `take`) of the same currency/amount.

### Donate / take imbalance (HIGH)
```solidity
poolManager.donate(key, amount0, amount1, "");  // adds to pool reserves...
// hook forgot to settle the donated tokens it owes
```
`donate` increases what the hook owes the pool; the tokens must be `settle`d. Imbalanced donate strands funds or reverts.
**Signal:** `donate` without settling the donated amounts, or `take` of donated funds with no offsetting credit.

### BeforeSwapDelta sign/units error (HIGH)
```solidity
BeforeSwapDelta d = toBeforeSwapDelta(int128(amtSpecified), 0);  // wrong: specified vs unspecified swapped
```
`BeforeSwapDelta` packs (specified, unspecified) deltas; swapping the two halves or the sign mis-accounts the swap and either reverts or hands the swapper free output.
**Signal:** `toBeforeSwapDelta` arguments in the wrong slot/sign, or specified-delta not reconciled with the actual swap amount.

## Severity rubric

| Pattern | Severity | Notes |
|---|---|---|
| take/mint with no matching settle → CurrencyNotSettled | **High** | Pool-wide swap DoS |
| Returned hookDelta not backed by settle → revert or free funds | **High** | Loss or DoS |
| Donate without settling owed tokens | **High** | Stranded funds / revert |
| BeforeSwapDelta sign/slot error | **High** | Mis-accounted swap, value leak |
| Hook over-settles (pays more than owed) | **Medium** | Hook self-loss, no swapper gain |
| Deltas always net to zero within callback | **Info** | Correct accounting |

## Remediation patterns

1. **Net every delta to zero before unlock returns** — for each currency the hook touches, pair `take` with `settle` (or a correct returned `hookDelta`).
2. **sync → transfer → settle** — call `poolManager.sync(currency)`, transfer the tokens in, then `settle()` so the manager credits the exact owed amount.
3. **Use the official delta helpers** — `toBeforeSwapDelta(specified, unspecified)` with correct argument order and signs; reconcile `specified` against `params.amountSpecified`.
4. **Assert flatness in tests** — after a swap, assert `poolManager.currencyDelta(hook, currency) == 0` for every currency.
5. **Settle donations** — every `donate` must be followed by transferring + settling the donated amounts.

## False-positive notes

- A hook returning `BeforeSwapDeltaLibrary.ZERO_DELTA` / `0` hookDelta and never calling take/settle/donate has nothing to settle — Info.
- Settlement done inside a shared internal helper called at the end of the callback may look unbalanced locally — trace the full callback before flagging.
- Over-settling (hook pays extra) is a hook self-loss, not a protocol-loss — Medium, not High.

## Related

- [[v4-hook-permission-flags-mismatch]] — returnDelta flags must be set for deltas to apply
- [[v4-hook-reentrancy-via-unlock]] — settlement ordering vs. reentrancy
- [[unchecked-calls]]

## 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-delta-accounting
- 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%.
