QSimpleLaunch
RobinhoodQSimpleLaunch is a Uniswap v4 hook on Robinhood Chain. Runs code on every swap. Can take tokens from swaps. Runs at pool creation.
- Contract name
- QSimpleLaunch
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. Buys and sells can be charged differently.
The code that does this (beforeSwap):function beforeSwap(address sender, PoolKey calldata key, IPoolManager.SwapParams calldata p, bytes calldata data) external onlyManager returns (bytes4, BeforeSwapDelta, uint24) { Pool storage pool = pools[PoolId.unwrap(key.toId())]; if (pool.token == address(0)) revert NotLaunchPool(); address b = pool.blocks; if (b == address(0)) return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); (uint256 charge, bool burn) = QSimpleBlocks(b).beforeSwap(sender, p, data); if (charge == 0) return (IHooks.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0); _route((p.amountSpecified < 0) == p.zeroForOne ? key.currency0 : key.currency1, charge, burn, b); return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(charge)), 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 (seed):function _seed(IPoolManager m, PoolKey memory key, address buyer, uint256 buyQuote, uint256 minOut) internal { bytes32 id = PoolId.unwrap(key.toId()); Pool storage pool = pools[id]; bool tokenIs0 = pool.tokenIs0; (int24 lower, int24 upper) = _range(pool); uint160 sa = TickMath.getSqrtPriceAtTick(lower); uint160 sb = TickMath.getSqrtPriceAtTick(upper); // Everything still held (the supply less any reserve), less a hair so rounding up in the pool can never // ask for more than we hold. uint256 held = ISimpleToken(pool.token).balanceOf(address(this)); uint256 amount = held - held / 1e12; uint256 liquidity = tokenIs0 ? FullMath.mulDiv(FullMath.mulDiv(amount, sa, 1 << 96), sb, sb - sa) : FullMath.mulDiv(amount, 1 << 96, sb - sa); (BalanceDelta d,) = m.modifyLiquidity(key, IPoolManager.ModifyLiquidityParams(lower, upper, int256(liquidity), 0), ""); Currency tc = Currency.wrap(pool.token); int128 owed = tokenIs0 ? d.amount0() : d.amount1(); require((tokenIs0 ? d.amount1() : d.amount0()) == 0 && owed < 0); m.sync(tc); ISimpleToken(pool.token).transfer(address(m), uint256(uint128(-owed))); m.settle(); if (buyQuote == 0) return; bool zeroForOne = !tokenIs0; // pay the quote, receive the token BalanceDelta s = m.swap(key, IPoolManager.SwapParams(zeroForOne, -int256(buyQuote), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1), ""); int128 q = zeroForOne ? s.amount0() : s.amount1(); int128 t = zeroForOne ? s.amount1() : s.amount0(); require(q < 0 && t > 0); uint256 spent = uint256(uint128(-q)); // …Buys back the token
It uses what it collects to buy the token back.
The code that does this (seed):function _seed(IPoolManager m, PoolKey memory key, address buyer, uint256 buyQuote, uint256 minOut) internal { bytes32 id = PoolId.unwrap(key.toId()); Pool storage pool = pools[id]; bool tokenIs0 = pool.tokenIs0; (int24 lower, int24 upper) = _range(pool); uint160 sa = TickMath.getSqrtPriceAtTick(lower); uint160 sb = TickMath.getSqrtPriceAtTick(upper); // Everything still held (the supply less any reserve), less a hair so rounding up in the pool can never // ask for more than we hold. uint256 held = ISimpleToken(pool.token).balanceOf(address(this)); uint256 amount = held - held / 1e12; uint256 liquidity = tokenIs0 ? FullMath.mulDiv(FullMath.mulDiv(amount, sa, 1 << 96), sb, sb - sa) : FullMath.mulDiv(amount, 1 << 96, sb - sa); (BalanceDelta d,) = m.modifyLiquidity(key, IPoolManager.ModifyLiquidityParams(lower, upper, int256(liquidity), 0), ""); Currency tc = Currency.wrap(pool.token); int128 owed = tokenIs0 ? d.amount0() : d.amount1(); require((tokenIs0 ? d.amount1() : d.amount0()) == 0 && owed < 0); m.sync(tc); ISimpleToken(pool.token).transfer(address(m), uint256(uint128(-owed))); m.settle(); if (buyQuote == 0) return; bool zeroForOne = !tokenIs0; // pay the quote, receive the token BalanceDelta s = m.swap(key, IPoolManager.SwapParams(zeroForOne, -int256(buyQuote), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1), ""); int128 q = zeroForOne ? s.amount0() : s.amount1(); int128 t = zeroForOne ? s.amount1() : s.amount0(); require(q < 0 && t > 0); uint256 spent = uint256(uint128(-q)); // …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 returns (bytes memory) { IPoolManager m = chain().manager; require(msg.sender == address(m) && _tload(T_LOCK) == 1); uint8 action = uint8(bytes1(data[31])); if (action == 0) { (, PoolKey memory key, address buyer, uint256 buyQuote, uint256 minOut) = abi.decode(data, (uint8, PoolKey, address, uint256, uint256)); _seed(m, key, buyer, buyQuote, minOut); } else if (action == 1) { (, PoolKey memory key) = abi.decode(data, (uint8, PoolKey)); _collect(m, key); } else { (, Currency cur, uint256 amount, address to) = abi.decode(data, (uint8, Currency, uint256, address)); m.burn(address(this), cur.toId(), amount); m.take(cur, to, amount); } 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 (preset):function _preset(uint8 style, uint32 snipe, address token, Chain memory ch) internal pure returns (QSimpleBlocks.Config memory c) { c.token = token; c.quote = ch.quote; uint256 cap = ch.startCap; if (style == FAIR || style == FLYWHEEL_STYLE) { // Anti-snipe 50% falling to 0 over `snipe` seconds (default 15 s; the launch blocks cap a charge at 50%); // 1% wallet cap and 30 s between buys for ten minutes (or the anti-snipe length, if longer); sandwich charge. c.mask = (1 << 1) | (1 << 2) | (1 << 4) | (1 << 7); c.guardSeconds = snipe > 600 ? snipe : 600; c.snipeBps = SNIPE_BPS; c.snipeDecay = snipe; c.maxBuyPerBlock = uint128(cap / 100); c.walletCapBps = 100; c.cooldownSeconds = 30; c.sandwichMaxBps = 800; if (style == FLYWHEEL_STYLE) { // 2% tax: 37.5% of it buys back and burns every 15 minutes, 12.5% to referrers, the rest to the payees. c.mask |= (1 << 11) | (1 << 16) | (1 << 17) | (1 << 18); c.buyTaxBps = 200; c.sellTaxBps = 200; c.taxDecay = 1800; c.buybackShareBps = 3750; c.buybackCooldown = 900; c.buybackSlipBps = 100; c.buybackMax = uint128(cap / 100); c.referralShareBps = 1250; } } else if (style == DEGEN) { // Anti-snipe as in Fair; launch tax 15% falling to 3% over 30 minutes, 1% burn per buy, king pot, // last-buyer clock, referrals. // …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 onlyManager returns (bytes4) { // Only pools this contract is creating, inside launch(). if (sender != address(this) || _tload(T_INIT) != 1) revert NotLaunchPool(); return IHooks.beforeInitialize.selector; }Anyone can trigger payouts
Anyone can collect the fees it has built up and send the collected fees on to where they go, so nobody has to wait on the hook developer.
The code that does this (collect):function _collect(IPoolManager m, PoolKey memory key) internal { bytes32 id = PoolId.unwrap(key.toId()); Pool storage pool = pools[id]; (int24 lower, int24 upper) = _range(pool); (BalanceDelta d,) = m.modifyLiquidity(key, IPoolManager.ModifyLiquidityParams(lower, upper, 0, 0), ""); int128 a0 = d.amount0(); int128 a1 = d.amount1(); require(a0 >= 0 && a1 >= 0); address to = pool.payout; if (a0 > 0) m.take(key.currency0, to, uint256(uint128(a0))); if (a1 > 0) m.take(key.currency1, to, uint256(uint128(a1))); (uint256 tokenAmt, uint256 quoteAmt) = pool.tokenIs0 ? (uint256(uint128(a0)), uint256(uint128(a1))) : (uint256(uint128(a1)), uint256(uint128(a0))); emit FeesCollected(id, to, quoteAmt, tokenAmt); }
Who controls it: The pool creator can change the wallets that receive its fees. The pool creator can also change: creator. 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.
It runs when a pool is created, often to lock in that pool’s settings.
Hook history
Across all its pools, the hook took its fee from a trade: $0.437.
- Took its fee from a trade 23 times, worth $0.437
Latest transactions
- 0xb086fa16…933a8d block 111,242,788 · 1.76e-7 WETH
- 0x54700bac…751344 block 111,215,594 · 0.0000324 WETH
- 0xa6452f4f…17ed85 block 111,215,594 · 0.0000324 WETH
- 0x1360bc16…c19418 block 111,215,594 · 0.0000331 WETH
- 0xff989aba…9d4c80 block 111,215,593 · 0.0000344 WETH
- 0x717a6f06…87814a block 111,215,592 · 0.0000337 WETH
- 0xc2a4c745…01ea59 block 111,154,395 · 52.31 HOOKER
- 0x89b6b3bf…615504 block 111,153,518 · 107.20 HOOKER
- 0x62578f85…f44440 block 111,152,017 · 5.31e-7 WETH
- 0x32f1959b…60640d block 111,152,001 · 0.00000758 WETH
- Launched a new token 1 time
Latest transactions
- 0xdf4d2a7b…a66595 block 111,148,635 · 1.00e-14 HOOKER
- Burned tokens 1 time
Latest transactions
- 0xdf4d2a7b…a66595 block 111,148,635 · 0.00100 HOOKER
Settings changes: changed the creator (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. Dollar values count the amounts paid in ETH, stablecoins, BNB and BTC, at today’s prices.