OVERVIEW
Routes a bounded exact-input hook fee into configured PoolManager claim recipients using return-delta accounting.
accountingrevenue

Programmable revenue routing for every swap.
Routes a bounded exact-input hook fee into configured PoolManager claim recipients using return-delta accounting.
beforeInitialize○ INACTIVEafterInitialize○ INACTIVEbeforeAddLiquidity○ INACTIVEafterAddLiquidity○ INACTIVEbeforeRemoveLiquidity○ INACTIVEafterRemoveLiquidity○ INACTIVEbeforeSwap● ACTIVEafterSwap○ INACTIVEbeforeDonate○ INACTIVEafterDonate○ INACTIVEbeforeSwapReturnDelta● ACTIVEafterSwapReturnDelta○ INACTIVEafterAddLiquidityReturnDelta○ INACTIVEafterRemoveLiquidityReturnDelta○ INACTIVE001// SPDX-License-Identifier: MIT002pragma solidity ^0.8.26;003004import {BaseHook} from "@openzeppelin/uniswap-hooks/src/base/BaseHook.sol";005import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";006import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";007import {SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";008import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";009import {PoolId, PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";010import {Currency, CurrencyLibrary} from "@uniswap/v4-core/src/types/Currency.sol";011import {012BeforeSwapDelta,013BeforeSwapDeltaLibrary,014toBeforeSwapDelta015} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol";016017/// @title SplitStreamHook018/// @notice Exact-input custom accounting reference that routes a 10 bps fee into ERC-6909 claims.019contract SplitStreamHook is BaseHook {020using CurrencyLibrary for Currency;021using PoolIdLibrary for PoolKey;022error ZeroRecipient();023error UnsupportedExactOutput();024error InputTooLarge();025error InvalidShareTotal();026uint256 public constant BPS = 10_000;027uint256 public constant HOOK_FEE_BPS = 10;028uint256 public constant LP_SHARE = 6_000;029uint256 public constant CREATOR_SHARE = 2_000;030uint256 public constant REFERRAL_SHARE = 1_000;031uint256 public constant PUBLIC_GOODS_SHARE = 1_000;032address public immutable lpRewardsRecipient;033address public immutable creatorRecipient;034address public immutable referralRecipient;035address public immutable publicGoodsRecipient;036event StreamSplit(037PoolId indexed poolId,038Currency indexed currency,039uint256 totalFee,040uint256 lpAmount,041uint256 creatorAmount,042uint256 referralAmount,043uint256 publicGoodsAmount044);045046constructor(IPoolManager manager, address lp, address creator, address referral, address publicGoods)047BaseHook(manager)048{049if (lp == address(0) || creator == address(0) || referral == address(0) || publicGoods == address(0)) {050revert ZeroRecipient();051}052if (LP_SHARE + CREATOR_SHARE + REFERRAL_SHARE + PUBLIC_GOODS_SHARE != BPS) revert InvalidShareTotal();053lpRewardsRecipient = lp;054creatorRecipient = creator;055referralRecipient = referral;056publicGoodsRecipient = publicGoods;057}058059function getHookPermissions() public pure override returns (Hooks.Permissions memory) {060return Hooks.Permissions(061false, false, false, false, false, false, true, false, false, false, true, false, false, false062);063}064065function _beforeSwap(address, PoolKey calldata key, SwapParams calldata params, bytes calldata)066internal067override068returns (bytes4, BeforeSwapDelta, uint24)069{070if (params.amountSpecified >= 0) revert UnsupportedExactOutput();071uint256 input = _abs(params.amountSpecified);072(uint256 fee, uint256 lp, uint256 creator, uint256 referral, uint256 publicGoods) = previewSplit(input);073if (fee == 0) return (BaseHook.beforeSwap.selector, BeforeSwapDeltaLibrary.ZERO_DELTA, 0);074if (fee > uint256(uint128(type(int128).max))) revert InputTooLarge();075Currency currency = params.zeroForOne ? key.currency0 : key.currency1;076uint256 id = currency.toId();077if (lp > 0) poolManager.mint(lpRewardsRecipient, id, lp);078if (creator > 0) poolManager.mint(creatorRecipient, id, creator);079if (referral > 0) poolManager.mint(referralRecipient, id, referral);080if (publicGoods > 0) poolManager.mint(publicGoodsRecipient, id, publicGoods);081emit StreamSplit(key.toId(), currency, fee, lp, creator, referral, publicGoods);082return (BaseHook.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);083}084085function previewSplit(uint256 input)086public087pure088returns (uint256 fee, uint256 lp, uint256 creator, uint256 referral, uint256 publicGoods)089{090fee = input * HOOK_FEE_BPS / BPS;091lp = fee * LP_SHARE / BPS;092creator = fee * CREATOR_SHARE / BPS;093referral = fee * REFERRAL_SHARE / BPS;094publicGoods = fee - lp - creator - referral;095}096097function _abs(int256 value) internal pure returns (uint256) {098if (value >= 0) return uint256(value);099unchecked {100return uint256(-(value + 1)) + 1;101}102}103}104
A compiled contract is not necessarily safe. Tests are not an audit. A verified source can still contain exploitable logic.