Skip to content

Execution

Uniswap v4 Hook Permissions: Read the Address Flags

Decode the official C0 pattern, change one bit, and follow a swap through its callbacks. A review worksheet separates valid permissions from trusted behavior.

Two ivory ceramic ribbons pass through lime-lined openings in an ivory gate on a dark plinth.
Illustration for execution; examples in this article are educational.

Uniswap v4 hook permissions are flags encoded in the lowest 14 bits of a hook contract's address. They tell the PoolManager which callbacks to invoke and whether certain returned balance adjustments may apply. Decode those bits, then inspect the deployed code, configuration and authority behind the address. A valid flag combination does not establish that the hook's code is safe.

Read the address as a call schedule

A hook is an external contract that participates at selected points in a pool's operations. Hooks are optional. One pool identifies one hook, while a hook can serve several pools. The hook belongs to the pool's key; identifying only the token pair leaves part of the pool identity unresolved. Uniswap's hook concepts explain that relationship. The v4 pool-ID guide follows the complete key.

This article uses v4-core v4.0.0, pinned to commit e50237c. Verify the actual implementation when examining another deployment or fork. In its Hooks library, bit positions start at zero from the right. A set bit enables its corresponding permission.

OperationBefore callback: bit, valueAfter callback: bit, value
Initialize pool13, 0x200012, 0x1000
Add liquidity11, 0x080010, 0x0400
Remove liquidity9, 0x02008, 0x0100
Swap7, 0x00806, 0x0040
Donate5, 0x00204, 0x0010

Four remaining flags concern returned deltas: before-swap at bit 3, after-swap at bit 2, after-add-liquidity at bit 1 and after-remove-liquidity at bit 0. A delta is an accounting adjustment. Enabling one of these flags does not create another callback.

The mask covering all fourteen bits is 0x3fff, or 16,383. Apply a bitwise AND between the address interpreted as an integer and that mask: retain the bottom fourteen bits and discard the rest. The result isolates the permission pattern.

Decode the public C0 example

Uniswap's hook-deployment guide gives an explanatory address with leading zeros and an ending of 00C0. It selects beforeSwap and afterSwap. This is a public teaching example, not evidence that a usable contract exists at that address.

For an illustrative comparison, keep that documented pattern and construct three alternatives. The table shows only the last four hexadecimal digits; a deployment review must retain the complete address and network.

Address endingMasked valueSelected permissionsStructural observation
00c0192Before swap; after swapBoth callback bits present
40c0192Before swap; after swapThe extra high bit lies outside the mask
00c4196Both swap callbacks; after-swap return deltaReturn permission has its required callback
00044After-swap return delta onlyRequired after-swap callback is missing

The first calculation is 128 + 64 = 192. Adding the after-swap return flag gives 192 + 4 = 196. Applying the mask to 0x40c0 removes 0x4000 and leaves 0x00c0. These are integer operations, with no rounding or token-decimal conversion.

The final row matters even though its one set bit is recognizable. The pinned library's address-validity check rejects a return-delta permission without its associated callback permission. A nonzero mask is therefore insufficient to establish a valid combination. The valid 00c4 pattern still says nothing about what value its callback will return.

Use the same procedure for every permission, including ones unrelated to the operation you plan to perform. Recording only the swap bits could hide behavior that matters when liquidity is later removed.

Follow the two callbacks through one swap

Consider a hypothetical initialized, static-fee pool whose hook has the 00c0 pattern. Assume a router opens a valid unlocked session, calls the manager, and is a different address from the hook. Assume every callback returns a valid response and the session settles successfully. This is an illustrative control-flow check, not an observed transaction.

The ordinary path is:

  1. The manager reaches the before-swap dispatch point and calls beforeSwap.
  2. The pool performs its core swap calculation.
  3. The manager reaches the after-swap dispatch point and calls afterSwap.
  4. The manager accounts for the caller's resulting currency deltas.
  5. The surrounding unlocked session must finish with all currency deltas settled.

The pinned PoolManager implementation establishes that ordering. Initialization, liquidity changes and donations select none of this example's hook callbacks because their bits are unset.

The dispatch library also suppresses callbacks when the hook itself is the manager's caller. That exception is why this trace explicitly uses a separate router as caller.

AfterSwap runs inside the transaction. It is not a notification delivered after a completed settlement. A callback failure that propagates through this manager path, or an invalid callback response, prevents the swap call from completing. If the enclosing transaction reverts, its earlier state changes do not become a successful swap merely because execution reached the core calculation.

The 00c0 hook has no return-delta permission. The official swap-hook tutorial illustrates this callback pair with counters and zero returned adjustments. That example demonstrates a possible implementation; the same flags do not establish that another hook only counts swaps.

Separate dispatch, declarations and validity

Three checks answer different questions. Address decoding tells you which permissions the manager sees. A hook's declared permissions describe what its developer intended. Pool initialization checks whether the address and fee configuration satisfy the core's structural rules.

The first-hook tutorial uses getHookPermissions to declare expected callbacks and validate the address during deployment. Check that the deployed design actually performs that comparison. A getter returning a reassuring set of booleans is not an independent guarantee.

In the pinned core, PoolManager.initialize calls isValidHookAddress; it does not ask the hook's getHookPermissions getter to choose callbacks. Initialization also does not prove that every selected function will respond correctly later. Keep deployment-time matching, initialization acceptance and successful execution as separate worksheet entries.

Changing an address to obtain the desired ending is not an arbitrary edit to a deployed contract. With CREATE2, the predicted address depends on the deploying address, salt and initialization-code hash. Developers search salts for the required pattern, then deploy using the matching inputs. EIP-1014 specifies these dependencies. A mined address still needs a verified deployment and an inspection of the resulting runtime code.

Ask what a returned delta can change

Compare the illustrative 00c0 and 00c4 patterns. Both select the same two callbacks. The additional flag in 00c4 enables the after-swap returned delta to participate in the manager's accounting path.

In the pinned IHooks interface, afterSwap returns an adjustment in the unspecified currency. That is the currency whose amount was not fixed by the swap request. A positive hook delta means currency is owed to, or taken by, the hook; a negative one means the hook owes or sends currency. Preserve that sign convention when reviewing a balance preview.

Absence of the return flag limits this particular return-value mechanism. It does not certify low gas use, unrestricted access or harmless external calls. Nor does it determine the LP fee. For a pool configured for dynamic fees, beforeSwap can supply an eligible fee override through a separate return field. Uniswap's dynamic-fee explanation distinguishes those fee controls. Review the pool's fee mode alongside its address flags.

Inspect the program behind the pattern

Two hooks can share a permission pattern while enforcing different rules. One might maintain a counter; another might require an authorized caller or depend on a separate contract. Ask which branch runs for the exact pool, sender, direction and hook data in the proposed operation.

Identity is particularly easy to misread. Inside a callback invoked by the manager, msg.sender is the PoolManager. The callback's sender argument identifies the manager's caller, often a router. An access rule or reward calculation can therefore act on the router when its author intended to identify the user's wallet. Uniswap's sender guide describes the trusted-router boundary.

Retain the deployed code and relevant state at one block. The address versus code-hash guide explains why matching bytes still leave configuration and dependencies unresolved. If the hook forwards to replaceable logic, or an administrator can change material rules, use the upgrade-authority worksheet to trace that control. Fixed address bits can coexist with changing behavior behind them.

An interface warning is another evidence item. Uniswap Labs states that its hook warnings may lack data and are not endorsements. An empty warning display cannot complete the code and authority review.

Keep a review record for the exact action

Use this worksheet before relying on a hook's displayed name or decoded label:

RecordEvidence to retain
Pool identityChain, PoolManager, complete pool key and hook address
Permission patternMasked value, all callback flags and return-delta dependencies
ImplementationRuntime code, matching source revision, forwarding paths and block identity
Material stateFees, access rules, dependencies, administrators and change powers
Proposed callRouter, sender handling, parameters, hook data and expected callback sequence
Failure behaviorCallback rejection, invalid response, unavailable dependency and settlement failure
Outcome boundsFinal asset changes, acceptable amounts and the router's enforced limits

Wallet spending authority needs its own entry. A hook flag does not describe the token approval or signed permission used to fund the action. The Permit2 versus ERC-2612 guide follows that separate authorization layer.

Save the decoded mask beside a plain-language account of each enabled callback's behavior. If the bits are understood but the code, caller identity or change authority remains unresolved, preserve that gap in the record before funding the action.

Keep the context with the example.

Each worked example is illustrative, with its sources and assumptions in the article. Read our editorial standards for authorship, verification and corrections.