Install
$ agentstack add skill-omermaksutii-rugproof-v4-hook-permission-flags-mismatch ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Uniswap V4 hook permission-flag mismatch detection
When this applies
Trigger on any of:
- Contracts inheriting
BaseHook/ implementingIHooks - An overridden
getHookPermissions()returning aHooks.Permissionsstruct - Implemented callbacks:
beforeSwap,afterSwap,beforeAddLiquidity,afterAddLiquidity,beforeRemoveLiquidity,afterRemoveLiquidity,beforeInitialize,afterInitialize,beforeDonate,afterDonate *ReturnDeltapermission flags (afterSwapReturnDelta,beforeSwapReturnDelta, etc.)- CREATE2 /
HookMiner.findsalt mining to encode flags into the hook address - Pool initialization that passes the hook address to
PoolManager.initialize
Detection patterns
Implemented callback whose flag bit is unset (HIGH)
function getHookPermissions() public pure override returns (Hooks.Permissions memory) {
return Hooks.Permissions({ beforeSwap: true, afterSwap: false, /* ...all else false */ });
}
function afterSwap(...) external override returns (bytes4, int128) {
_accrueFees(...); // ← real logic, but afterSwap flag is FALSE
return (this.afterSwap.selector, 0);
}
The pool reads permissions from the hook address bits, not from the function table. With the AFTER_SWAP bit unset, the PoolManager never calls afterSwap; _accrueFees silently never runs. Signal: a callback is implemented (non-reverting body) but its corresponding permission is false / the address bit is unmined.
Flag set without implementation → init reverts (HIGH)
return Hooks.Permissions({ beforeAddLiquidity: true, /* ... */ });
// but beforeAddLiquidity is NOT overridden → BaseHook reverts HookNotImplemented
Hooks.validateHookPermissions checks that each set address bit corresponds to an implemented callback; a set flag with no override makes PoolManager.initialize revert, bricking the pool. Signal: permission true (or address bit set) with no matching overridden function, or the default BaseHook stub left in place (reverts HookNotImplemented).
returnDelta flag mismatch (HIGH)
beforeSwapReturnDelta: false // but beforeSwap returns a non-zero BeforeSwapDelta
If beforeSwap returns a non-zero delta while BEFORE_SWAP_RETURNS_DELTA is unset, the manager ignores the delta (or reverts), stranding the accounting the hook tried to apply. Signal: a callback returns a non-zero BeforeSwapDelta/int128 while its *ReturnDelta permission is false.
Address bits ≠ getHookPermissions (HIGH)
address hook = address(uint160(0x...0040)); // only BEFORE_SWAP bit
// getHookPermissions() also claims afterSwap → mismatch at validateHookPermissions
Signal: the mined deployment address low bits don't equal the getHookPermissions() struct.
Severity rubric
| Pattern | Severity | Notes | |---|---|---| | Implemented callback with unset flag → silently skipped | High | Fees/limits/guards never run | | Flag set, no implementation → init reverts | High | Pool un-initializable (DoS) | | returnDelta flag mismatch → delta ignored | High | Stranded accounting, see [[v4-hook-delta-accounting]] | | Address bits disagree with getHookPermissions | High | Deterministic init revert | | Cosmetic flag set but callback is a true no-op | Low | Wasted gas only |
Remediation patterns
- Single source of truth — derive the deploy salt from
getHookPermissions()(e.g.HookMiner.findwith the exact flag set) so address bits and the struct can't drift. - Assert at construction —
Hooks.validateHookPermissions(this, getHookPermissions())in the constructor to fail fast on a wrong address. - Implement exactly the flagged callbacks — every
truehas an override; every override has atrue. Remove dead callbacks or set their flag. - Set the matching
*ReturnDeltaflag whenever a callback can return a non-zero delta. - Test against the real PoolManager init path, not a mock that skips validation.
False-positive notes
- A callback present only to satisfy an interface but truly returning zero/no-op with its flag intentionally unset is fine — Info, confirm no side effects.
- Hooks deployed via the official
HookMinerwith asserted permissions are consistent by construction.
Related
- [[v4-hook-delta-accounting]] — returnDelta flags pair with settlement
- [[access-control]]
- [[initialization]]
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: omermaksutii
- Source: omermaksutii/RugProof
- License: MIT
- Homepage: https://omermaksutii.github.io/RugProof
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.