MindHook
EthereumMindHook is a Uniswap v4 hook on Ethereum. Runs code on every swap. Can take tokens from swaps. Sets its own swap fee. Runs at pool creation.
- Contract name
- MindHook
How this hook works
Trading taxA trading tax hook. It has a fee that can be changed, takes a tax on trades and charges more right after launch.
Fee can be changed
The trading fee is not fixed: the hook developer can change it at any time.
The code that does this (syncLpFee):function syncLpFee(PoolKey calldata key, uint24 lpFee) external { if (msg.sender != address(controller)) revert OnlyController(); PoolId poolId = key.toId(); PoolState memory state = _pools[poolId]; if (!state.registered) revert PoolNotRegistered(); if (lpFee > state.lpFeeMax) revert LpFeeAboveMax(lpFee, state.lpFeeMax); poolManager.updateDynamicLPFee(key, lpFee); emit LpFeeSynced(poolId, lpFee); }Takes a tax on trades
On top of the normal pool fee, it keeps part of each trade as a tax. Buys and sells can be charged differently. The tax can be changed, but never above 50%. The tax builds up in the hook until it is collected.
The code that does this (beforeSwap):function _beforeSwap(address sender, PoolKey calldata key, SwapParams calldata params, bytes calldata hookData) internal override returns (bytes4, BeforeSwapDelta, uint24) { PoolId poolId = key.toId(); (uint16 feeBps, uint8 moduleCount) = _prepareSwap(poolId, params.zeroForOne); if (moduleCount != 0 && params.zeroForOne) _runGuards(poolId, _swapCtx(sender, params, hookData, feeBps)); uint256 fee = _takeSpecifiedFee(poolId, params, feeBps); _storeSwap(feeBps, moduleCount, fee); return (IHooks.beforeSwap.selector, toBeforeSwapDelta(fee.toInt128(), 0), 0); }Higher at launch
The tax starts higher right after launch and comes down over time, which makes it costly for bots to buy first.
The code that does this (feeBps):function _feeBps(PoolId poolId, PoolState memory state, IHookController.PoolFees memory fees, bool isBuy) private view returns (uint16 feeBps, bool refreshed) { uint256 snipeBps = isBuy ? HookFeeLib.snipeExtraBps(state.snipeStartBps, state.snipeBlocks, block.number - state.launchBlock) : 0; uint256 volatilityBps; if (fees.volatilityOn) { if (block.number > state.refBlock) { int24 tick = _clampedTick(poolId, state.startTick); state.prevRefTick = block.number - state.refBlock > VOLATILITY_STALE_BLOCKS ? tick : state.refTick; state.refTick = tick; state.refBlock = uint48(block.number); refreshed = true; } int256 move = int256(state.refTick) - int256(state.prevRefTick); volatilityBps = HookFeeLib.volatilityExtraBps(uint256(move < 0 ? -move : move)); } feeBps = isBuy ? HookFeeLib.totalFeeBps( fees.buyFeeBps, _min(state.buyFeeMaxBps, fees.buyFeeMaxBps), snipeBps, volatilityBps ) : HookFeeLib.totalFeeBps(fees.sellFeeBps, _min(state.sellFeeMaxBps, fees.sellFeeMaxBps), 0, volatilityBps); }Keeps a price record
It records the pool’s price over time, so other apps can read a fair average price.
The code that does this (afterSwap):function _afterSwap( address sender, PoolKey calldata key, SwapParams calldata params, BalanceDelta delta, bytes calldata hookData ) internal override returns (bytes4, int128) { (uint16 feeBps, uint8 moduleCount, uint256 feeBefore) = _loadSwap(); _requireFullFill(params, delta.amount0(), feeBps, feeBefore); uint256 fee = _takeUnspecifiedFee(key.toId(), params, delta.amount0(), feeBps); if (moduleCount != 0) { bytes memory callData = _observeCall( key.toId(), _swapCtx(sender, params, hookData, feeBps), _swapResult(params.zeroForOne, delta, fee + feeBefore) ); _runObservers(key.toId(), sender, key.currency1, callData); } return (IHooks.afterSwap.selector, fee.toInt128()); }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 (validateVaultCaps):function _validateVaultCaps(VaultCaps memory c, VaultCaps memory g) private pure { if (c.perActionBps > g.perActionBps || c.perActionBps < MIN_CAP_BPS) revert InvalidBounds("perActionBps"); if (c.perHourBps > g.perHourBps || c.perHourBps < MIN_CAP_BPS) revert InvalidBounds("perHourBps"); if (c.perDayBps > g.perDayBps || c.perDayBps < MIN_CAP_BPS) revert InvalidBounds("perDayBps"); if (c.sellPerDayBps > g.sellPerDayBps || c.sellPerDayBps < MIN_SELL_PER_DAY_BPS) { revert InvalidBounds("sellPerDayBps"); } if (c.maxImpactBps > g.maxImpactBps || c.maxImpactBps < MIN_IMPACT_BPS) revert InvalidBounds("maxImpactBps"); if (c.cooldown < g.cooldown || c.cooldown > MAX_COOLDOWN) revert InvalidBounds("cooldown"); }Only approved callers create pools
New pools with this hook can only be created by approved callers, for example its own factory or launcher.
The code that does this (beforeInitialize):function _beforeInitialize(address sender, PoolKey calldata key, uint160) internal view override returns (bytes4) { if (sender != factory) revert OnlyFactory(); if ( !key.currency0.isAddressZero() || key.fee != LPFeeLibrary.DYNAMIC_FEE_FLAG || key.tickSpacing != TICK_SPACING ) revert InvalidPoolKey(); return IHooks.beforeInitialize.selector; }Anyone can trigger payouts
Anyone can collect the fees it has built up, so nobody has to wait on the hook developer.
The code that does this (collectFees):function collectFees(PoolId poolId) external nonReentrant returns (uint256 ethAmount) { FeeLedger memory ledger = _ledgers[poolId]; ethAmount = ledger.accrued - ledger.collected; if (ethAmount == 0) return 0; _ledgers[poolId].collected = ledger.accrued; emit HookFeesCollected(poolId, ethAmount); poolManager.unlock(abi.encode(ethAmount)); feeRouter.depositHookFees{value: ethAmount}(poolId); }
Who controls it: The hook developer can change the fee. The code never lets the fee go above 50%. 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.
It can take or add tokens during a swap. Fees, burns and custom curves work this way, and so do scams.
Pools using it let the hook choose the swap fee instead of a fixed tier.
It runs when a pool is created, often to lock in that pool’s settings.
Hook history
- Took its fee from a trade 100 times
Latest transactions
- 0x41f8df2d…d9bf2d block 26,394,327
- 0x800c8d1a…f87129 block 26,394,327
- 0xa53bfca3…754f03 block 26,394,327
- 0xbc0ba230…259b48 block 26,394,327
- 0x7f107f1f…90f47a block 26,394,327
- 0x19426d44…9f8e8a block 26,394,322
- 0x09c79b59…1f3334 block 26,394,322
- 0x0c0311d5…06af88 block 26,394,322
- 0x1f74e37f…d86c65 block 26,394,320
- 0xef17ee1c…1ed11b block 26,394,319
- Registered a new pool 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.