InterpoolHook
BaseInterpoolHook is a Uniswap v4 hook on Base. Runs code on every swap. Can take tokens from swaps. Runs at pool creation.
- Contract name
- InterpoolHook
How this hook works
Buyback and burnA buyback and burn hook. It takes a tax on trades, manages its own liquidity 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. It can never be above 10%. 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) external override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) { if (sender == address(this)) return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); bool specifiedIsEth = (params.amountSpecified < 0) == params.zeroForOne; if (!specifiedIsEth) return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); uint256 base = params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified); (uint256 fee,) = _split(base, Currency.unwrap(key.currency1)); if (fee != 0) poolManager.mint(address(this), CurrencyLibrary.ADDRESS_ZERO.toId(), fee); return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(uint128(fee)), 0), 0); }Manages its own liquidity
It adds and removes liquidity itself, so it can own part or all of the pool.
The code that does this (unlockCallback):function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) { (Op op, bytes memory args) = abi.decode(data, (Op, bytes)); uint256 id = CurrencyLibrary.ADDRESS_ZERO.toId(); if (op == Op.Pay) { (address to, uint256 amount) = abi.decode(args, (address, uint256)); poolManager.burn(address(this), id, amount); poolManager.take(CurrencyLibrary.ADDRESS_ZERO, to, amount); return ""; } (address token, uint256 eth) = abi.decode(args, (address, uint256)); PoolKey memory key = IFactoryKeys(factory).poolKey(token); if (op == Op.Floor) { (, int24 tick,,) = poolManager.getSlot0(key.toId()); int24 lower = _ceil(tick + 1, key.tickSpacing); int24 upper = lower + FLOOR_BAND * key.tickSpacing; int24 maxTick = TickMath.maxUsableTick(key.tickSpacing); if (upper > maxTick) upper = maxTick; if (lower >= upper) revert Nothing(); uint128 liq = LiquidityAmounts.getLiquidityForAmount0( TickMath.getSqrtPriceAtTick(lower), TickMath.getSqrtPriceAtTick(upper), eth ); if (liq == 0) revert Nothing(); (BalanceDelta d,) = poolManager.modifyLiquidity( key, ModifyLiquidityParams({tickLower: lower, tickUpper: upper, liquidityDelta: int256(uint256(liq)), salt: bytes32(0)}), "" ); uint256 owed = uint256(uint128(-d.amount0())); poolManager.burn(address(this), id, owed); // …Buys back the token
It uses what it collects to buy the token back.
The code that does this (unlockCallback):function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) { (Op op, bytes memory args) = abi.decode(data, (Op, bytes)); uint256 id = CurrencyLibrary.ADDRESS_ZERO.toId(); if (op == Op.Pay) { (address to, uint256 amount) = abi.decode(args, (address, uint256)); poolManager.burn(address(this), id, amount); poolManager.take(CurrencyLibrary.ADDRESS_ZERO, to, amount); return ""; } (address token, uint256 eth) = abi.decode(args, (address, uint256)); PoolKey memory key = IFactoryKeys(factory).poolKey(token); if (op == Op.Floor) { (, int24 tick,,) = poolManager.getSlot0(key.toId()); int24 lower = _ceil(tick + 1, key.tickSpacing); int24 upper = lower + FLOOR_BAND * key.tickSpacing; int24 maxTick = TickMath.maxUsableTick(key.tickSpacing); if (upper > maxTick) upper = maxTick; if (lower >= upper) revert Nothing(); uint128 liq = LiquidityAmounts.getLiquidityForAmount0( TickMath.getSqrtPriceAtTick(lower), TickMath.getSqrtPriceAtTick(upper), eth ); if (liq == 0) revert Nothing(); (BalanceDelta d,) = poolManager.modifyLiquidity( key, ModifyLiquidityParams({tickLower: lower, tickUpper: upper, liquidityDelta: int256(uint256(liq)), salt: bytes32(0)}), "" ); uint256 owed = uint256(uint128(-d.amount0())); poolManager.burn(address(this), id, owed); // …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 (unlockCallback):function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) { (Op op, bytes memory args) = abi.decode(data, (Op, bytes)); uint256 id = CurrencyLibrary.ADDRESS_ZERO.toId(); if (op == Op.Pay) { (address to, uint256 amount) = abi.decode(args, (address, uint256)); poolManager.burn(address(this), id, amount); poolManager.take(CurrencyLibrary.ADDRESS_ZERO, to, amount); return ""; } (address token, uint256 eth) = abi.decode(args, (address, uint256)); PoolKey memory key = IFactoryKeys(factory).poolKey(token); if (op == Op.Floor) { (, int24 tick,,) = poolManager.getSlot0(key.toId()); int24 lower = _ceil(tick + 1, key.tickSpacing); int24 upper = lower + FLOOR_BAND * key.tickSpacing; int24 maxTick = TickMath.maxUsableTick(key.tickSpacing); if (upper > maxTick) upper = maxTick; if (lower >= upper) revert Nothing(); uint128 liq = LiquidityAmounts.getLiquidityForAmount0( TickMath.getSqrtPriceAtTick(lower), TickMath.getSqrtPriceAtTick(upper), eth ); if (liq == 0) revert Nothing(); (BalanceDelta d,) = poolManager.modifyLiquidity( key, ModifyLiquidityParams({tickLower: lower, tickUpper: upper, liquidityDelta: int256(uint256(liq)), salt: bytes32(0)}), "" ); uint256 owed = uint256(uint128(-d.amount0())); poolManager.burn(address(this), id, owed); // …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, uint160) external view override onlyPoolManager returns (bytes4) { if (sender != factory) revert NotFactory(); return IHooks.beforeInitialize.selector; }Anyone can trigger payouts
Anyone can trigger a buyback, so nobody has to wait on the hook developer.
The code that does this (buyback):function buyback(address token) external { uint256 eth = IRulesFee(rules).takeBuybackBudget(token); if (eth < BUYBACK_MIN) revert Nothing(); poolManager.unlock(abi.encode(Op.Buyback, abi.encode(token, eth))); }
Who controls it: The hook developer can change which launcher it works with. The hook developer can withdraw funds held by the hook. Control can be passed on or given up. The code cannot be changed.
What do these labels mean? 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.
It runs when a pool is created, often to lock in that pool’s settings.
Hook history
- Handled a trade 17 times
Latest transactions
- 0x0d966d6d…10c198 block 56,706,867 · 0.000842 INTERPOOL
- 0x68607293…370121 block 56,705,595 · 0.000891 INTERPOOL
- 0x2fd98c74…6fa0b6 block 56,705,526 · 0.000742 INTERPOOL
- 0xa9a5de83…799fd4 block 56,705,248 · 0.0000247 INTERPOOL
- 0xc027191c…e9221e block 56,705,248 · 0.0000247 INTERPOOL
- 0xe4d751db…296637 block 56,705,126 · 0.000272 INTERPOOL
- 0xb9420270…ec5c11 block 56,704,934 · 0.000643 INTERPOOL
- 0x09e57f65…1ab6e9 block 56,704,905 · 0.000594 INTERPOOL
- 0x71822ea0…24b1e1 block 56,704,886 · 0.0000990 INTERPOOL
- 0x57024553…9cb3e0 block 56,704,749 · 0.000495 INTERPOOL
- Sent on a treasury 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. Still reading older history, so totals will grow.