The fee stream
Why fees trickle out over a week instead of landing all at once — and why that choice protects you.
The problem it solves
Fees build up inside the Uniswap position continuously, but they are only collected when someone harvests. That makes a harvest a discrete jump: one moment the vault is worth X, the next it is worth X plus a week of trading fees.
Anything that jumps can be front-run. Without a stream, the attack is trivial:
block N attacker deposits a large amount
block N+1 attacker calls harvest() — a week of fees lands in NAV
block N+2 attacker withdraws, taking a share of fees earned
over a week they were exposed to for two blocksEvery honest staker who held through that week just had their yield diluted by someone who took none of the risk.
What the stream does
Harvested fees are not added to what shares are worth. They go into a seven-day linear payout, credited per second to whoever holds shares across that window.
The attacker above now earns two blocks of a seven-day stream — effectively nothing — while carrying full price exposure for as long as they hold. To capture a real share they have to hold for a real duration, at which point they are simply a liquidity provider like everyone else.
test_lateDepositor_cannotSnipeAccruedFees deposits a large position after all the trading is done, withdraws immediately, and asserts the round trip extracted nothing meaningful from roughly 600 USDC of fees sitting unharvested.Why every action harvests first
There is a subtler version of the same attack. In Uniswap v4, changing a position's liquidity sweeps its entire accrued fee balance, no matter how much liquidity you are adding or removing.
A deposit that did not harvest first would have those fees quietly netted against what the depositor owed — handing one person everyone else's yield through the settlement path rather than the reward path.
This was a real bug during development, worth 0.9 tokens per round trip on a pool with 1M of depth. The fix is that deposits, withdrawals and compounds all harvest before touching liquidity, so there is never a pending balance sitting there to sweep.
There is a second-order version too. When the vault swaps harvested fees through its own pool, that swap pays fees back to its own position — after the collection point. Those have to be swept as well, so internal swaps collect twice.
Overlapping harvests
If a harvest happens while a previous stream is still running, the two combine. Whatever is left of the old stream is folded into the new one and the seven-day clock restarts.
- Frequent harvests produce a smooth, near-continuous payout.
- Rare harvests produce visible steps in the APR.
- Either way nothing is lost — only the timing of the payout changes.
When nobody is staked
If a harvest lands while the vault has no shares outstanding, there is nobody to stream to. Rather than letting that value become permanently unclaimable, the whole harvest is routed into the compounding queue, where it becomes liquidity for whoever stakes next.