Slice
Docs

How it works

The full path a trading fee takes before it becomes something you can claim.

The vault

Each pool gets one vault. The vault owns a single full-range Uniswap v4 position — liquidity spread across every possible price, so it never falls out of range and never needs rebalancing.

When you deposit, the vault adds your tokens to that position and mints you ERC-20 shares. Your shares are a pro-rata claim on the whole position. If the position holds 100 USDC and 100 tokens and you own 10% of the shares, you can withdraw 10 USDC and 10 tokens.

Why shares instead of the position NFT
Uniswap v4 positions are NFTs, and each one has its own price range. If the vault escrowed your NFT, working out your share of the fees would depend on the range you happened to pick. One vault-owned position with fungible shares keeps the maths simple and makes your stake transferable.

Harvesting

Trading fees accrue inside the Uniswap position. harvest() collects them. It is permissionless — anyone can call it, and it also runs automatically at the start of every deposit, withdrawal and compound.

Fees arrive in both tokens. The vault splits them three ways:

PortionWhere it goes
Protocol fee1% of the harvest, accrued for the treasury to collect separately.
Stream shareConverted to USDC and paid out to stakers over seven days.
Compound shareQueued, then added back as liquidity — raising every share's backing.

The split between streaming and compounding is a per-vault setting. At the default of 100% streaming, everything goes to stakers as claimable USDC.

Converting the token side

Half the fees arrive as the pool's token rather than USDC. The vault swaps that side into USDC through the same pool, so everything it pays out is denominated in one asset.

That swap is bounded. The vault keeps its own time-weighted average price and refuses to trade if the current price has drifted too far from it. If the price looks manipulated — or if the vault has not gathered enough price history yet — it simply defers the conversion and tries again next time.

Why deferring matters
Harvest sits on the deposit and withdrawal path. If it reverted whenever the price looked wrong, a volatile hour would freeze the vault and you could not get your money out. Deferring keeps withdrawals working no matter what the market does.

Streaming, not dumping

A harvest is a lump of value arriving at one instant. If it were added straight to what your shares are worth, anyone could deposit in the block before a harvest, withdraw in the block after, and walk off with fees earned during a week they were not there for.

So the proceeds are paid out linearly over seven days instead. To capture a meaningful amount you have to actually hold the position for a meaningful time — which is the same thing honest liquidity providers are doing.

deposit ──► shares ──► rewards accrue every second ──► claim()
                        │
                        └─ transferring shares carries your
                           unclaimed rewards with you, not them

Compounding

Whatever is not streamed goes into a queue. Calling compound() takes that USDC, swaps half into the token, and adds both sides back into the position as new liquidity.

No new shares are minted, so the extra liquidity is spread across existing holders. Your share count stays the same; what each share is worth goes up.

Who runs it

Anyone can. poke(), harvest(), compound() and collectProtocolFees() are all callable by anyone, and none of them let the caller redirect funds: the conditions decide whether a call is valid, not who made it. They exist as buttons on every vault page.

Permissionless is a statement about who may run it, not about whether anything needs running. Two things genuinely depend on somebody calling in:

  • poke() records a price observation. The vault keeps its own thirty-minute average, built from the last 32 observations, and will only swap while the current price sits close to that average. Single-sided deposits and the conversion of token-side fees both need it. A pool with nobody trading through the vault does not refresh it by itself, so a keeper pokes every vault roughly every ninety seconds. Slice runs one; it covers every vault the factory knows about, including pools listed by strangers, and it skips empty ones.
  • harvest() collects fees and starts the stream. Deposits and withdrawals call it on the way through, so a busy pool keeps itself current; a quiet one is harvested by the same keeper.
  • compound() turns the queue into liquidity, once the oracle can price it.

What never depends on any of this: two-sided deposits, withdrawals and claims. None of them swap, so none of them consult the price. If every keeper in the world stopped, you could still get in with both sides and out with everything.

On a token that moves several percent a minute, the price may sit outside the band most of the time however often it is poked. The vault page says so when that is the case, and single-sided deposits pause rather than execute at a price the vault cannot verify. More on the stream →