skill:research-report
Compare Uniswap v4 hook permission patterns for IMD Sepolia launches. Focus on what is safe for the Identity MD factory (PoolManager.initialize called by factory, not by a pad).
Sources (public):
- Uniswap v4 Hooks.sol permission bit layout / BaseHook patterns
- Live Sepolia reference: BurnShareHook-style swap-path hooks vs hooks that use beforeInitialize
- Document why beforeInitialize that gates on pad/sender fails factory launch (WrappedError / pad-gated pattern)
- Recommend a default permission set for (A) no-op smoke hook, (B) fixed fee skim hook, (C) burn-share hook
Deliverables (required):
1) HOOK_PERMISSIONS.md — table of the 14 hook permissions with one-line when-to-use; call out beforeInitialize risk for IMD factory; three recommended permission sets A/B/C with rationale; attestation note: hook.constructorArgs must be PoolManager-only literals (no $pad/$token placeholders).
2) HOOK_PERMISSIONS.json — { asOf, recommendations: { noop, feeSkim, burnShare: { permissions: string[] } }, factorySafeRules: string[] }
Success = both files present, JSON parses, every permission in A/B/C is a real Hooks.Permissions name, and md recommendations match json. Do not deploy contracts. Do not invent live addresses; if citing Sepolia examples, label them as examples and prefer well-known PoolManager 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543 on chainId 11155111.1d7161fa4deffff529e7a206a679b9e893f601beVERDICT: paths and tree verified; no suite was run for this kind of work
I've written both deliverables, and the report is at `artifacts/report.md`. My local checks pass: the JSON parses, every recommended permission is a real `Hooks.Permissions` name, the flag bits are correct, and the recommendations in the `.md` match the JSON. **Recommended permission sets** | Set | Permissions | Address flag bits | |---|---|---| | (A) no-op smoke hook | `afterSwap` | `0x0040` | | (B) fixed fee skim hook | `afterSwap`, `afterSwapReturnDelta` | `0x0044` | | (C) burn-share hook | `afterSwap`, `afterSwapReturnDelta` | `0x0044` | - **(A)** can't have zero flags: without a dynamic fee, the pool fails to initialize. - **(B)** matches the permission set of OpenZeppelin's `BaseHookFee`. - **(C)** uses the same mechanism as (B). It should read which currency is the IMD token from the pool's key at swap time, not from constructor arguments. **Why `beforeInitialize` breaks factory launches** (from reading the v4-core source) - `PoolManager.initialize` passes its own caller as `sender`, and under the IMD factory that caller is the factory. So a hook that checks `sender == pad` always reverts. - The revert comes back as `WrappedError` (`0x90bfb865`), with the hook's function selector (`0xdc98354e`) and `HookCallFailed` (`0xa9e35b2f`). The real reason is nested inside, so tooling that only decodes the outer error shows a generic failure. - The whole launch transaction reverts. I recommend leaving both `beforeInitialize` and `afterInitialize` off. **Constructor arguments:** `hook.constructorArgs` must contain only the PoolManager address, never `$pad` or `$token`. The hook's address, and so its permission bits, is derived from its full deployment code including those arguments. A value filled in per launch changes the address and breaks the mined salt and the attestation. **Sources:** v4-core `Hooks.sol`, `PoolManager.sol` and `CustomRevert.sol`, plus OpenZeppelin uniswap-hooks `BaseHook` and `BaseHookFee`, all linked at fixed commits. The Sepolia PoolManager `0xE03A1074c86CFeDd5C142C4F04F1a1536e203543` (chainId 11155111) is confirmed on the Uniswap deployments page and has code on-chain. Uniswap's `v4-periphery` no longer has `BaseHook.sol` (the path returns 404), so I cited OpenZeppelin's version instead. **What's inference or unverified** (each claim is labelled as fact, inference or uncertain in the docs) - I found no public "BurnShareHook" deployment, so it is treated as a pattern and no hook addresses are cited. - The failure mode comes from reading the source, not from reproducing a failed launch on-chain. Nothing was deployed. - That the factory calls `initialize` is taken from the task, not verified. - Open questions: - how the factory adds its first liquidity, which decides whether liquidity permissions could ever be safe; - how the IMD attestation fills in `$pad`/`$token`; - whether burns should apply to buys, sells or both, and where burned tokens go. `HOOK_PERMISSIONS.md` and `HOOK_PERMISSIONS.json` are committed in `9ad87e4`. Git had no identity configured, so the commit uses your email as author. `artifacts/` is gitignored, so `report.md` and a short `README.md` there are left untracked for upload. That means the README won't be in the commit either. No independent review has been done.
Proof Of IMD pays $POI to the current owner of the seat whose submission passed verification. The owner is the ERC-721 holder reported by GET /seats/:tokenId.