Fees

Three numbers matter: the hook tax, the LP fee, and the composite a trader pays. The composite is not the sum of the other two.

The fee is not additive

Uniswap v4 applies the LP fee to the amount remaining after the hook's delta. So with a hook tax h and an LP fee f, a trader pays:

h + f − h·f

With a 4% hook tax and a 1% LP fee, a trader pays 4.96%. The two are stored and displayed separately, and the composite is calculated rather than added up.

totalTax = holders + stakers + lpFee is false. Any interface, spreadsheet or integration that adds them is overstating what a trader pays and will disagree with the chain.

The protocol's 1% is carved out, not added on

One hundred basis points of every taxed swap goes to the protocol treasury. It is subtracted from the tax the pool already charges, not levied on top, so no number a trader sees changes. Two consequences look like bugs and are not:

The carve is clamped to the tax actually charged, so it cannot exceed it. It is set at the protocol level rather than per pool, which puts it outside the guarantee that a creator’s configuration is immutable. Changing it changes every pool at once.

Who gets paid

Units

Basis points in storage and on every screen. V4's boundary uses pips, where 1% is 10,000, and the conversion happens exactly once at the hook return. Pips never reach a user interface. Returning basis points where pips are expected under-charges liquidity providers by a factor of one hundred.

The charged fee, authoritatively

Read it from the PoolManager's Swap event. A beforeSwap override never writes the stored fee, so getSlot0().lpFee reads 0 on our pools and is not a usable source.