Revenue sharing belongs in the pool, not in the token
A token that wants to share its revenue normally writes a tax into transfer. That breaks aggregators, gets the contract flagged by scanners, forces an owner onto a token that should not need one, and can never be added to something already deployed. Every project rewrites the same plumbing, badly.
Stackr moves capture out of the token and into the pool. StackrHook takes 1% of the ETH leg of a swap in afterSwap and forwards it in the same call. The token stays a bare ERC-20 — no tax, no owner, no hook in transfer — which is what lets any ERC-20 that already shipped adopt revenue sharing by opening a v4 pool with the hook. Nothing to redeploy, nothing to migrate.
The split is a thermostat, not a vote
Every protocol replays the same argument: how much goes to LPs, how much to stakers? Stackr deletes the argument. The hook is the only place that sees both sides at once, so it derives the answer from their imbalance on every swap:
stakerShare = poolTokens / (poolTokens + staked)
clamped to [20%, 80%] so neither side can be starved. Thin staking against deep liquidity pushes the staker share up to 80% and staking becomes worth doing. Everyone staking against a drained pool pushes it down to 20% and providing liquidity becomes worth doing. Both sides are measured in STACKR — what the pool holds versus what is staked — so the ratio means something and costs a single balanceOf.
The LP half is paid with poolManager.donate(), which credits in-range LPs natively. No distributor contract, no merkle drop, no claim step.
The standard: IStackrSink
The hook knows nothing about staking. It talks to three functions:
function weight() external view returns (uint256); function fund() external payable; function asset() external view returns (address);
Anything that implements them can receive a share: plain staking, ve-locks, senior/junior tranches, an insurance backstop. One hook, as many sharing designs as anyone cares to write — that is the part worth calling a standard. The reference sink ships with the protocol, but it is one implementation, not the interface.
The reference sink: StackrStaking
Stake STACKR, earn ETH. Not emissions, not a second token: the actual revenue the hook skimmed, redistributed pro-rata through a standard accRewardPerShare accumulator. Nothing is minted, so nothing is diluted, and the only reason to stay staked is that the yield is real. No lock and no vesting — unstaking never forfeits accrued ETH.
One edge case worth naming: revenue that lands while nobody is staked cannot be credited to anyone. Rather than strand it, the sink parks it in orphaned and folds it into the next fund() that arrives with a live stake behind it.
Which swaps actually pay
Uniswap v4 return-deltas can only adjust the unspecified currency of a swap, so the skim lands on ETH only when ETH is the unspecified leg — in practice exact-input sells and exact-output buys. An exact-input buy pays nothing. That is by construction, not an oversight: it keeps the hook from ever holding a token the sink cannot price. Anyone claiming “1% on every trade” has not read the contract.
What can never change
The skim, the clamp bounds, the sink address and the pool the hook serves are all fixed at deployment. There is no owner, no role, no pause and no upgrade path on any contract. The only way to change any of it is to launch a different pool.