BananaHook
EthereumBananaHook is a Uniswap v4 hook on Ethereum. Brand new: its first pool went live today. Still in use: 6 trades in the last week, and 1 new pool this week.
- Contract name
- BananaHook
How this hook works
Buyback and burnA buyback and burn hook. It takes a tax on trades, charges more right after launch and buys back the token.
Takes a tax on trades
On top of the normal pool fee, it keeps part of each trade as a tax. The tax can be changed, but never above 3%.
The code that does this (beforeSwap):function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata) external onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) { if (PoolId.unwrap(key.toId()) != PoolId.unwrap(poolId)) revert WrongPool(); _observe(); uint256 bps = _bps(params.zeroForOne); uint256 tax; if (params.zeroForOne && params.amountSpecified < 0) { tax = uint256(-params.amountSpecified) * bps / 10_000; // a buy with exact ETH in: off the ETH in } else if (!params.zeroForOne && params.amountSpecified > 0) { tax = uint256(params.amountSpecified) * bps / (10_000 - bps); // a sale for exact ETH out: on top } if (tax == 0) return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); assembly ("memory-safe") { tstore(PENDING_TAX, tax) } return (IHooks.beforeSwap.selector, toBeforeSwapDelta(tax.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 (taxNow):function taxNow() public view returns (uint256 buyBps, uint256 sellBps) { uint256 o = openedAt; if (o != 0 && block.timestamp < o + LAUNCH) { return (MAX_TAX_BPS, LAUNCH_SELL_BPS - (LAUNCH_SELL_BPS - MAX_TAX_BPS) * (block.timestamp - o) / LAUNCH); } return (taxBps, taxBps); }Buys back the token
It uses what it collects to buy the token back.
The code that does this (route):function _route(PoolKey calldata key, bool buy, uint256 bps, uint256 tax) private { bool launch = _inLaunch(); uint256 toDev; uint256 toFarm; if (!launch || buy) { if (launch) toDev = tax / 2; else toFarm = tax / 2; } if (toDev != 0) poolManager.take(key.currency0, dev, toDev); if (toFarm != 0) { poolManager.take(key.currency0, address(this), toFarm); try IFarmTax(farm).taxIn{value: toFarm}() {} catch { (bool ok,) = dev.call{value: toFarm}(""); ok; } } uint256 burned; uint256 rest = tax - toDev - toFarm; if (rest != 0) { // this hook's own swap does not call the hook again BalanceDelta d = poolManager.swap(key, IPoolManager.SwapParams({ zeroForOne: true, amountSpecified: -rest.toInt256(), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1 }), ""); burned = uint256(uint128(d.amount1())); if (burned != 0) { poolManager.take(key.currency1, address(this), burned); IBurnable(banana).burn(burned); } } emit Taxed(buy, bps, tax, toDev, toFarm, burned); }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 (route):function _route(PoolKey calldata key, bool buy, uint256 bps, uint256 tax) private { bool launch = _inLaunch(); uint256 toDev; uint256 toFarm; if (!launch || buy) { if (launch) toDev = tax / 2; else toFarm = tax / 2; } if (toDev != 0) poolManager.take(key.currency0, dev, toDev); if (toFarm != 0) { poolManager.take(key.currency0, address(this), toFarm); try IFarmTax(farm).taxIn{value: toFarm}() {} catch { (bool ok,) = dev.call{value: toFarm}(""); ok; } } uint256 burned; uint256 rest = tax - toDev - toFarm; if (rest != 0) { // this hook's own swap does not call the hook again BalanceDelta d = poolManager.swap(key, IPoolManager.SwapParams({ zeroForOne: true, amountSpecified: -rest.toInt256(), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1 }), ""); burned = uint256(uint128(d.amount1())); if (burned != 0) { poolManager.take(key.currency1, address(this), burned); IBurnable(banana).burn(burned); } } emit Taxed(buy, bps, tax, toDev, toFarm, burned); }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 (beforeSwap):function beforeSwap(address, PoolKey calldata key, IPoolManager.SwapParams calldata params, bytes calldata) external onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) { if (PoolId.unwrap(key.toId()) != PoolId.unwrap(poolId)) revert WrongPool(); _observe(); uint256 bps = _bps(params.zeroForOne); uint256 tax; if (params.zeroForOne && params.amountSpecified < 0) { tax = uint256(-params.amountSpecified) * bps / 10_000; // a buy with exact ETH in: off the ETH in } else if (!params.zeroForOne && params.amountSpecified > 0) { tax = uint256(params.amountSpecified) * bps / (10_000 - bps); // a sale for exact ETH out: on top } if (tax == 0) return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); assembly ("memory-safe") { tstore(PENDING_TAX, tax) } return (IHooks.beforeSwap.selector, toBeforeSwapDelta(tax.toInt128(), 0), 0); }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 sqrtPriceX96) external onlyPoolManager returns (bytes4) { if (sender != dev) revert NotDev(); if (PoolId.unwrap(poolId) != 0) revert AlreadySet(); if (!key.currency0.isAddressZero() || Currency.unwrap(key.currency1) != banana || address(key.hooks) != address(this)) revert WrongPool(); _key = key; poolId = key.toId(); floorSqrtPriceX96 = sqrtPriceX96; lastObserved = uint32(block.timestamp); openedAt = block.timestamp; emit Opened(PoolId.unwrap(poolId), sqrtPriceX96); return IHooks.beforeInitialize.selector; }
Who controls it: The hook developer can change the tax. The code never lets the fee go above 3%. Control can be passed on or given up. The code cannot be changed.
Based on the hook’s published code.What do these labels mean?
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.
It runs when a pool is created, often to lock in that pool’s settings.
Hook history
- Took its tax from a trade 3 times
Latest transactions
- 0x5ce1ae7d…8f135d block 26,407,901
- 0x17f49375…089b54 block 26,407,869
- 0xe4e375af…299d0d block 26,407,745
- Burned tokens 3 times
Latest transactions
- 0x5ce1ae7d…8f135d block 26,407,901 · 0.547 BANANA
- 0x17f49375…089b54 block 26,407,869 · 0.284 BANANA
- 0xe4e375af…299d0d block 26,407,745 · 0.0868 BANANA
- Handled trade money (this hook holds the pool’s funds) 3 times
Latest transactions
- 0x5ce1ae7d…8f135d block 26,407,901 · 0.547 BANANA
- 0x17f49375…089b54 block 26,407,869 · 0.284 BANANA
- 0xe4e375af…299d0d block 26,407,745 · 0.0868 BANANA
- Opened a 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.