ManifestHook
EthereumManifestHook is a Uniswap v4 hook on Ethereum. Runs code on every swap. Sets its own swap fee. Can block LP withdrawals. Gates who can add liquidity. Runs at pool creation. Handles donations to LPs.
- Contract name
- ManifestHook
How this hook works
Smart feesA smart fees hook. It changes its fee over time, trades on its own and burns tokens.
Fee changes over time
Instead of a fixed fee, it sets the fee itself and changes it over time, for example starting high at launch and coming down. The fee goes to liquidity providers; the hook keeps nothing for itself.
The code that does this (beforeSwap):function _beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata) internal override nonReentrant returns (bytes4, BeforeSwapDelta, uint24) { uint256 gasStart = gasleft(); // 1. Bypass all checks for protocol-initiated arbitrage swaps uint256 isSystemSwap; assembly { isSystemSwap := tload(SYSTEM_SWAP_SLOT) } if (isSystemSwap == 1) { // System swap - skip ManifestHook checks, zero fee for arb swaps return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, LPFeeLibrary.OVERRIDE_FEE_FLAG); } // 2. Pause check (user swaps only) if (paused) revert HookPaused(); // 3. Refresh targetPrice from the live NAV oracle so the flex constraint and dynamic // fee below level-set against the current NAV. uint256 nav = _refreshTargetPrice(); // 4. Circuit breaker check (user swaps only). Evaluate each cause once and carry the full // cause set in the revert data (an emit would be discarded when the frame reverts). NAV // is reused from the refresh above, not re-read: it cannot move within a single tx. if (!circuitBreakerOverride) { bool usdeDepegged = isUsdeDepeggedFromUsd(); bool navOutOfBand = _navOutOfBand(nav); // …Trades on its own
It makes trades itself, for example to convert the fees it collected.
The code that does this (arbSwap):function _arbSwap( IPoolManager poolManager, PoolKey calldata key, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimit ) private returns (int128 inputDelta, int128 outputDelta) { assembly { tstore(SYSTEM_SWAP_SLOT, 1) } BalanceDelta swapDelta = poolManager.swap( key, SwapParams({zeroForOne: zeroForOne, amountSpecified: amountSpecified, sqrtPriceLimitX96: sqrtPriceLimit}), "" ); assembly { tstore(SYSTEM_SWAP_SLOT, 0) } // Input delta is the negative side, output is the positive side int128 d0 = swapDelta.amount0(); int128 d1 = swapDelta.amount1(); inputDelta = d0 < 0 ? d0 : d1; outputDelta = d0 > 0 ? d0 : d1; }Burns tokens
It destroys some tokens (or sends them to an address nobody controls), so there are fewer of them.
The code that does this (afterSwap):function _afterSwap(address, PoolKey calldata key, SwapParams calldata, BalanceDelta delta, bytes calldata) internal override nonReentrant returns (bytes4, int128) { uint256 gasStart = gasleft(); // 1. Check system swap flag (arbitrage swaps from protocol) uint256 isSystemSwap; assembly { isSystemSwap := tload(SYSTEM_SWAP_SLOT) } if (isSystemSwap == 1) { // System swap - skip arbitrage/flex (prevents loop) return (BaseHook.afterSwap.selector, 0); } // targetPrice was already refreshed in _beforeSwap of this same swap tx (V4 fires afterSwap // only after beforeSwap for this permission set), and NAV cannot move mid-tx, so no refresh here. // 2. Accrue sell pressure from this swap's REALIZED USH leg, before any arb runs. // `delta` is the user's fill: system swaps returned above, so the hook's own arb legs are // never counted here. For an exact-input sell the USH leg is the fee-inclusive gross // input, which is the number the surcharge should see. Widening int128 to int256 before // the library negates it keeps int128.min unreachable, so the negation cannot overflow. { int256 ushDelta = int256(_isUshCurrency0(key) ? delta.amount0() : delta.amount1()); if (ushDelta != 0) { (uint24 feeLevel, uint256 reconciled) = DynamicFeeLib.reconcilePressure( DynamicFeeLib.ReconcileInput({ // …Uses an outside price feed
It reads a price from an outside source (an oracle) and uses it in its rules.
The code that does this (isUshNavBelowFloor):function isUshNavBelowFloor() public view returns (bool) { return ushPriceOracle.getCurrentPrice() < ushNavFloor; }Limits on trading
It limits trading, for example a max buy or wallet size or a wait between trades, to stop bots grabbing the supply before real traders.
The code that does this (afterSwap):function _afterSwap(address, PoolKey calldata key, SwapParams calldata, BalanceDelta delta, bytes calldata) internal override nonReentrant returns (bytes4, int128) { uint256 gasStart = gasleft(); // 1. Check system swap flag (arbitrage swaps from protocol) uint256 isSystemSwap; assembly { isSystemSwap := tload(SYSTEM_SWAP_SLOT) } if (isSystemSwap == 1) { // System swap - skip arbitrage/flex (prevents loop) return (BaseHook.afterSwap.selector, 0); } // targetPrice was already refreshed in _beforeSwap of this same swap tx (V4 fires afterSwap // only after beforeSwap for this permission set), and NAV cannot move mid-tx, so no refresh here. // 2. Accrue sell pressure from this swap's REALIZED USH leg, before any arb runs. // `delta` is the user's fill: system swaps returned above, so the hook's own arb legs are // never counted here. For an exact-input sell the USH leg is the fee-inclusive gross // input, which is the number the surcharge should see. Widening int128 to int256 before // the library negates it keeps int128.min unreachable, so the negation cannot overflow. { int256 ushDelta = int256(_isUshCurrency0(key) ? delta.amount0() : delta.amount1()); if (ushDelta != 0) { (uint24 feeLevel, uint256 reconciled) = DynamicFeeLib.reconcilePressure( DynamicFeeLib.ReconcileInput({ // …Locks tokens or liquidity
Some tokens or liquidity are locked for a set time and can’t be taken out before then.
The code that does this (getPendingAuthManager):function getPendingAuthManager() external view returns (address pendingManager, uint256 unlockTime) { pendingManager = _pendingAuthManager; if (_pendingAuthManager != address(0)) { unchecked { unlockTime = _setAuthManagerReqTimestamp + TIMELOCK_PERIOD; } } }
Who controls it: The hook developer can change who else can run it, the wallets that receive its fees, the price source, the fee and the settings of its pools. The hook developer can also change: circuit breaker override, upward arbitrage enabled, downward arbitrage enabled, external gas reserve, target arb price and 6 more. The hook developer can also call: cancel ownership transfer, cancel auth manager update, add trusted sender, remove trusted sender, reset hard breaker. The hook developer can pause trading. The code cannot be changed.
Based on the hook’s published code.
What it can do
Its code executes inside each trade in pools that use it.
Pools using it let the hook choose the swap fee instead of a fixed tier.
It runs before liquidity is removed, so it can delay or refuse LP exits.
It checks every deposit before it goes in.
It runs when a pool is created, often to lock in that pool’s settings.
It runs when someone donates tokens to the pool’s LPs.
Hook history
- Registered a new pool 1 time
- Removed a trusted sender 1 time
Settings changes: set aside a pool ush baseline (3 times), changed the ush nav ceiling (1 time), changed the min treasury balance (1 time), changed the pauser (1 time), changed the ush nav floor (1 time), changed the oracle staleness threshold (1 time).
Counted from what the hook recorded on-chain across all its pools, for every action in its published code, plus token transfers in and out of it.