PearHook
EthereumPearHook is a Uniswap v4 hook on Ethereum. Brand new: its first pool went live today. Still in use: 1 trade in the last week, and 1 new pool this week.
- Contract name
- PearHook
How this hook works
Trading taxA trading tax hook. It takes 2% of every trade, manages its own liquidity and keeps a price record.
Takes 2% of every trade
On top of the normal pool fee, it takes 2% of each trade. The rate is written into the code. 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 onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) { // no swaps on PEAR pools while listSpoke / sweep / compound / bootstrap are mid-flight if (_reentrancyGuardEntered()) revert Reentrancy(); if (Currency.unwrap(key.currency0) == address(0)) { // HUB: record the oracle on the first swap of the block, using the pre-swap tick. (, int24 tick,,) = poolManager.getSlot0(hubId); if (oracle.write(uint32(block.timestamp), tick)) { hubBlockTime = uint32(block.timestamp); hubBlockOpenTick = tick; hubBlockMaxDev = 0; } // swap tax, always in ETH. When ETH is the specified currency it is charged here (the swap is // resized by the tax); otherwise it is charged on the ETH leg in afterSwap. int256 specified = params.amountSpecified; if (params.zeroForOne == (specified < 0)) { uint256 tax = uint256(specified < 0 ? -specified : specified) * TAX_BPS / BPS; _accrueEthTax(tax); return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(uint128(tax)), 0), 0); } return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); } return (IHooks.beforeSwap.selector, _fillSpoke(key.toId(), sender, params), 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 (touchPositions):function _touchPositions(uint256 start, uint256 count, bool remove) internal returns (uint256 eth, uint256 pearAmt) { uint256 n = _hubPositions.length; if (start > n) start = n; uint256 end = count > n - start ? n : start + count; PoolKey memory key = _hubKey(pear); for (uint256 i = start; i < end; ++i) { HubPosition memory pos = _hubPositions[i]; bytes32 salt = bytes32(uint256(pos.salt)); int256 liquidityDelta; if (remove) { liquidityDelta = -int256( uint256( poolManager.getPositionLiquidity( hubId, keccak256(abi.encodePacked(address(this), pos.tickLower, pos.tickUpper, salt)) ) ) ); if (liquidityDelta == 0) continue; } (BalanceDelta d,) = poolManager.modifyLiquidity( key, ModifyLiquidityParams(pos.tickLower, pos.tickUpper, liquidityDelta, salt), "" ); eth += uint256(uint128(d.amount0())); pearAmt += uint256(uint128(d.amount1())); } }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 sender, PoolKey calldata key, SwapParams calldata params, bytes calldata) external onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) { // no swaps on PEAR pools while listSpoke / sweep / compound / bootstrap are mid-flight if (_reentrancyGuardEntered()) revert Reentrancy(); if (Currency.unwrap(key.currency0) == address(0)) { // HUB: record the oracle on the first swap of the block, using the pre-swap tick. (, int24 tick,,) = poolManager.getSlot0(hubId); if (oracle.write(uint32(block.timestamp), tick)) { hubBlockTime = uint32(block.timestamp); hubBlockOpenTick = tick; hubBlockMaxDev = 0; } // swap tax, always in ETH. When ETH is the specified currency it is charged here (the swap is // resized by the tax); otherwise it is charged on the ETH leg in afterSwap. int256 specified = params.amountSpecified; if (params.zeroForOne == (specified < 0)) { uint256 tax = uint256(specified < 0 ? -specified : specified) * TAX_BPS / BPS; _accrueEthTax(tax); return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(uint128(tax)), 0), 0); } return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); } return (IHooks.beforeSwap.selector, _fillSpoke(key.toId(), sender, params), 0); }Liquidity can’t be added or removed directly
Nobody can add or remove liquidity through Uniswap directly. Liquidity only moves through the hook’s own code.
Only the hook creates its pools
Nobody can create a pool with this hook directly. Pools are only created through the hook’s own code.
Anyone can trigger payouts
Anyone can send the collected fees on to where they go, reinvest what it has collected and collect the fees it has built up, so nobody has to wait on the hook developer.
The code that does this (sweep):function sweep(PoolId spokeId, uint256 minWethOut) external nonReentrant returns (uint256 xIn, uint256 ethToHub) { Spoke storage s = _spokes[spokeId]; if (s.token == address(0)) revert UnknownPool(); Pricing memory p = _pricing(s); if (!p.priced) revert PriceUnavailable(); if (p.reason != FROZEN_NONE) revert SpokeFrozen(p.reason); uint256 xInv = s.xInventory; if (_xToWeth(xInv, p.refTwapTick, s.xIsToken0Ref) < SWEEP_MIN_WETH) revert BelowSweepMinimum(); uint256 sweepAmount = xInv * SWEEP_BPS / BPS; s.xInventory = uint128(xInv - sweepAmount); // effects first; the unsold remainder is added back below bytes memory res = poolManager.unlock( abi.encode(Action.Sweep, abi.encode(spokeId, sweepAmount, minWethOut, p.refTwapTick, msg.sender)) ); uint256 bounty; (xIn, ethToHub, bounty) = abi.decode(res, (uint256, uint256, uint256)); emit Swept(spokeId, xIn, ethToHub, msg.sender); _sendEth(msg.sender, bounty); }
Who controls it: The hook developer can call: bootstrap. 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 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.
Hook history
- Listed the spoke 13 times
Latest transactions
- 0x00b4cbdf…4f7ae3 block 26,422,024
- 0x37eb40e5…e9debb block 26,422,023
- Added starting liquidity 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.
Pools
14 totalOnly pools with trades in the last 7 days are listed here: 1 of 14. See all, newest first.