CrownHook
RobinhoodCrownHook is a Uniswap v4 hook on Robinhood Chain. Runs code on every swap. Can take tokens from swaps. Runs at pool creation.
- Contract name
- CrownHook
How this hook works
Buyback and burnA buyback and burn hook. It takes a tax on trades, buys back the token and burns tokens.
Takes a tax on trades
On top of the normal pool fee, it keeps part of each trade as a tax. The tax builds up in the hook until it is collected.
The code that does this (beforeSwap):function beforeSwap(address sender, PoolKey calldata, SwapParams calldata p, bytes calldata) external override onlyPoolManager returns (bytes4, BeforeSwapDelta, uint24) { if (_inSwap) revert ReentrantSwap(); if (p.amountSpecified == 0) revert NoTrade(); if (address(module).codehash != moduleCodeHash) revert ModuleCodeChanged(); _inSwap = true; // Launch protection, by the module: for 5 minutes a buy must come through the canonical router (its // msgSender() is the buyer) or the initializer (the developer); for 30 seconds buys pay the opening tax, // except the developer and the fixed exempt wallets. if (p.zeroForOne && block.timestamp < token.startedAt() + PROTECTION_SECONDS) { _activeBuyer = module.protectedBuyer(sender); } uint256 taxPpm = p.zeroForOne ? module.openingTaxPpmForSender(sender) : 0; _taxPpm = taxPpm; // Transitions run before fees, so a trade pays under the reign in force when it starts. _tick(); bool nativeSpecified = p.zeroForOne == (p.amountSpecified < 0); _nativeSpecified = nativeSpecified; if (!nativeSpecified) return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); uint256 amount = _abs256(p.amountSpecified); bool exactOut = p.amountSpecified > 0; // an exact amount of ETH out: a sell (uint256 gross, uint256 fee, uint256 tax) = _grossAndFee(amount, exactOut, taxPpm); uint256 poolNative = exactOut ? gross : amount - fee; // …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) { (uint8 op, address to, uint256 amount) = abi.decode(data, (uint8, address, uint256)); if (op == OP_PAY) { poolManager.burn(address(this), ETH_ID, amount); poolManager.take(Currency.wrap(address(0)), to, amount); return ""; } // Buyback (Programmable's share already moved to the fee vault): no callback fee and no bid. uint256 swapIn = amount; BalanceDelta d = poolManager.swap( poolKey, SwapParams({ zeroForOne: true, amountSpecified: -int256(swapIn), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1 }), "" ); uint256 spent = _abs(d.amount0()); uint256 bought = _abs(d.amount1()); poolManager.burn(address(this), ETH_ID, spent); // pays for the swap from the pot's claim poolManager.take(poolKey.currency1, address(this), bought); token.burn(bought); if (spent < swapIn) buybackPot += swapIn - spent; // a partial fill stays in the pot emit Buyback(spent, bought); return ""; }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) { (uint8 op, address to, uint256 amount) = abi.decode(data, (uint8, address, uint256)); if (op == OP_PAY) { poolManager.burn(address(this), ETH_ID, amount); poolManager.take(Currency.wrap(address(0)), to, amount); return ""; } // Buyback (Programmable's share already moved to the fee vault): no callback fee and no bid. uint256 swapIn = amount; BalanceDelta d = poolManager.swap( poolKey, SwapParams({ zeroForOne: true, amountSpecified: -int256(swapIn), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1 }), "" ); uint256 spent = _abs(d.amount0()); uint256 bought = _abs(d.amount1()); poolManager.burn(address(this), ETH_ID, spent); // pays for the swap from the pot's claim poolManager.take(poolKey.currency1, address(this), bought); token.burn(bought); if (spent < swapIn) buybackPot += swapIn - spent; // a partial fill stays in the pot emit Buyback(spent, bought); return ""; }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 (checkCaps):function _checkCaps(bool isBuy, uint256 ethAmt, uint256 tokAmt, address w) private view { if (isBuy && block.timestamp < launchTime + CROWN_OPENS_AFTER && ethAmt > LAUNCH_MAX_BUY) { revert OverLaunchCap(); } (Rule rule, uint256 value) = _ruleLive(); if (rule == Rule.VelvetRope) { if (isBuy && ethAmt > value) revert OverVelvetRope(); } else if (rule == Rule.SellCap) { if (!isBuy && tokAmt > token.totalSupply() * value / 10_000) revert OverSellCap(); } else if (rule == Rule.Cooldown) { if (!isBuy && lastSell[w] != 0 && block.timestamp < lastSell[w] + value * 1 minutes) revert OnCooldown(); } }Rewards to claim
It keeps track of what people have earned, and they claim it themselves.
The code that does this (claim):function _claim(address recipient, uint256 amount, bool platform) private { if (claimActive) revert ReentrantClaim(); if (amount == 0) revert NothingToClaim(); claimActive = true; activeClaim = keccak256(abi.encode(recipient, amount)); if (platform) platformAccrued = 0; else creatorAccrued = 0; bytes memory result = poolManager.unlock(abi.encode(recipient, amount)); if (result.length != 0 || activeClaim != bytes32(0)) revert InvalidClaim(); claimActive = false; emit FeesClaimed(recipient, amount, platform); }Anyone can trigger payouts
Anyone can send the collected fees on to where they go, so nobody has to wait on the hook developer.
The code that does this (sweep):function sweep(uint256 id) external { if (!_sweepIfBreached(id)) revert NothingToDo(); }
Who controls it: The hook developer can call: revoke, veto name. 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
- Took its fee from a trade 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.