SinjohPreferredHook
RobinhoodSinjohPreferredHook is a Uniswap v4 hook on Robinhood Chain. Runs code on every swap. Can take tokens from swaps. Sets its own swap fee. Can block LP withdrawals. Gates who can add liquidity. Runs at pool creation. Handles donations to LPs.
- Contract name
- SinjohPreferredHook
How this hook works
Trading taxA trading tax hook. It sets its own trading fee, takes a tax on trades and charges more right after launch.
Sets its own trading fee
It sets the pool’s trading fee itself instead of using a fixed one.
The code that does this (completeGraduation):function completeGraduation(PoolId poolId, uint256 tokenId, uint128 liquidity) external onlyFactory { Graduation storage g = _graduations[poolId]; Phase phase = phaseOf(poolId); if (phase != Phase.Swept) revert WrongPhase(phase); (g.graduated, g.graduatedAt, g.liquidity, g.tokenId) = (true, uint64(block.timestamp), liquidity, tokenId); FutureMarket storage f = _future[poolId]; if (f.kind != LaunchKind.Curve) { // Owner decision (27 October 2026): a slot pool's tax model starts at actual graduation. f.modelStart = uint64(block.timestamp); emit ModelStarted(poolId, uint64(block.timestamp)); } if (_isPoolCurve(poolId)) poolManager.updateDynamicLPFee(_poolKey(_markets[poolId]), LP_FEE); SinjohTraditionalToken(_markets[poolId].token).notifyGraduation(); if (_pairedBacking[poolId]) { ISinjohPairedToken(_markets[poolId].backing).notifyPairedGraduation(poolId); } emit Graduated(poolId, tokenId, liquidity, uint64(block.timestamp)); }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 tax can be changed. 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) internal override returns (bytes4, BeforeSwapDelta, uint24) { PoolId poolId = key.toId(); // A module's own swap in the window the root opened for it on this pool (trusted transient state, never hook // data): uncharged here, its flows are charged per direction through `chargeModule` and reconciled at close. address window = WINDOW_MODULE_SLOT.asAddress().tload(); if (window != address(0)) { if (sender != window || WINDOW_POOL_SLOT.asBytes32().tload() != PoolId.unwrap(poolId)) { revert SwapReentered(); } if (MODULE_SWAP_SLOT.asBoolean().tload()) revert SwapReentered(); MODULE_SWAP_SLOT.asBoolean().tstore(true); return (IHooks.beforeSwap.selector, toBeforeSwapDelta(0, 0), 0); } if (SWAP_LOCK_SLOT.asBoolean().tload() || _reentrancyGuardEntered()) revert SwapReentered(); if (!_graduations[poolId].graduated) { if (!_isPoolCurve(poolId) || phaseOf(poolId) != Phase.Curve) revert WrongPhase(phaseOf(poolId)); SWAP_LOCK_SLOT.asBoolean().tstore(true); CURVE_SWAP_SLOT.asBoolean().tstore(true); return _beforeCurveSwap(sender, key, params); } // Pending module flow settles before the trade's pre-trade state is read. _runModules(key, Hooks.BEFORE_SWAP_FLAG); SWAP_LOCK_SLOT.asBoolean().tstore(true); ISinjohHook.Market storage m = _markets[poolId]; bool buy = params.zeroForOne != m.tokenIsCurrency0; bool exactInput = params.amountSpecified < 0; // …Higher at launch
The tax starts higher right after launch and comes down over time, which makes it costly for bots to buy first.
The code that does this (curveRates):function curveRates(PoolId poolId, bool buy, uint160 sqrtPriceX96, bool exempt) external view returns (uint256 creatorBps, uint256 launchBps, bool sellSurcharge) { ISinjohHook.Market storage m = _market(poolId); creatorBps = _creatorRate(poolId, m, buy, sqrtPriceX96); launchBps = exempt ? 0 : TaxMath.launchSurchargeBps(block.timestamp - m.launchTime); sellSurcharge = !buy && _surchargeBefore(m, sqrtPriceX96, _advancedAverage(m, sqrtPriceX96)); // Pool-backed curves have no native LP fee before graduation. If core enables its input protocol fee, // trim only the launch component as for the graduated pool, retaining the same 99% combined bound. if (launchBps != 0 && _isPoolCurve(poolId)) { // This branch needs only the native protocol-fee word. // slither-disable-next-line unused-return (,, uint24 protocol,) = poolManager.getSlot0(poolId); uint16 nativeFee = buy != m.tokenIsCurrency0 ? ProtocolFeeLibrary.getZeroForOneFee(protocol) : ProtocolFeeLibrary.getOneForZeroFee(protocol); if (nativeFee != 0) { // Return the applied launch component; the curve reconstructs the combined total. // slither-disable-next-line unused-return (, launchBps) = TaxMath.poolCombinedBps(creatorBps, launchBps, sellSurcharge); } } }Rewards to claim
It keeps track of what people have earned, and they claim it themselves.
The code that does this (redeemVaultClaims):function redeemVaultClaims(PoolId poolId, uint256 amount) external { ISinjohHook.Market storage m = _market(poolId); if (msg.sender != m.vault) revert NotVault(msg.sender); // The callback returns nothing. // slither-disable-next-line unused-return poolManager.unlock(abi.encode(OP_REDEEM, abi.encode(poolId, amount))); }Rules for adding or removing liquidity
It decides who can add or remove liquidity, or when.
The code that does this (beforeAddLiquidity):function _beforeAddLiquidity(address sender, PoolKey calldata key, ModifyLiquidityParams calldata, bytes calldata) internal override returns (bytes4) { _beforeLiquidity(sender, key, Hooks.BEFORE_ADD_LIQUIDITY_FLAG); return IHooks.beforeAddLiquidity.selector; }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 key, uint160) internal view override returns (bytes4) { PoolId poolId = key.toId(); _market(poolId); Phase phase = phaseOf(poolId); if (_isPoolCurve(poolId) && phase == Phase.Curve) { if (sender != curveOf[poolId]) revert NotCurve(); } else { if (sender != factory) revert NotFactory(); if (phase != Phase.Swept) revert WrongPhase(phase); } return IHooks.beforeInitialize.selector; }Anyone can trigger payouts
Anyone can collect the fees it has built up, so nobody has to wait on the hook developer.
The code that does this (collectPlatform):function collectPlatform(PoolId[] calldata poolIds) external nonReentrant { address recipient = ISinjohSettings(settings).feeRecipient(); Payment[] memory payments = new Payment[](poolIds.length); for (uint256 i; i < poolIds.length; ++i) { ISinjohHook.Market storage m = _market(poolIds[i]); uint256 curvePaid = _curvePlatform(poolIds[i]); uint256 amount = m.platformBucket; m.platformBucket = 0; payments[i] = Payment(poolIds[i], _backingOf(m), recipient, amount, m.backingIsSinjohToken); emit PlatformCollected(poolIds[i], recipient, amount + curvePaid); } _pay(payments); }
Who controls it: The hook developer can call: execute takeover. The pool creator can change the fee. The pool creator can also change: sell surcharge. The pool creator can also call: adopt module, adopt decision module, remove module, lower static rates, switch to static and 5 more. 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.
Pools using it let the hook choose the swap fee instead of a fixed tier.
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.
It runs when someone donates tokens to the pool’s LPs.
Hook history
- Received tokens 1 time
Latest transactions
- 0xf10477cc…958582 block 111,083,137 · 1000.00M SOLAR
- Sent tokens out 1 time
Latest transactions
- 0xf10477cc…958582 block 111,083,137 · 1000.00M SOLAR
Counted from token transfers in and out of the hook. Its published code records no actions of its own.