QSimpleLaunch
BaseQSimpleLaunch is a Uniswap v4 hook on Base. Runs code on every swap. Can take tokens from swaps. Runs at pool creation.
- Contract name
- QSimpleLaunch
How this hook works
Trading taxA trading tax 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 taxed 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 ""; }Launch protection
It limits trading, for example a max buy or wallet size or a cooldown, 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. // …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 code itself can’t be changed. The pool creator can change: creator, payout. The manager can call: before swap, after swap.
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
- Launched 1 time
Latest transactions
- 0x98f49b34…471f26 block 55,351,364 · 1.00e-14 CLEAN
- Burned tokens 1 time
Latest transactions
- 0x98f49b34…471f26 block 55,351,364 · 0.00100 CLEAN
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.