# Yield shares

> How a shielded note can earn Aave interest without leaking how long it has been held: the note commits to a fixed share count in a non-rebasing wrapper.

A yield note is an ordinary [note](/concepts/notes) whose `tokenId` is a share token rather than the asset a user thinks in. The user deposits USDC and holds a note denominated in ppUSDC shares; the share count never changes, and each share is redeemable for more USDC as Aave interest accrues. The pool learns nothing new to support this. It shields ppUSDC exactly as it shields any other ERC-20, and all of the yield machinery sits outside the pool as a wrapper, a zap, and a `processor` router.

## Why shares, not a rebasing balance

If the pool shielded a token whose balance rebased, every note's value would drift with time, and the drift would be a clock. A note created when 1 aUSDC was worth 1.00 USDC and spent when it was worth 1.05 would reveal roughly how long it had been held, because the circuit enforces [value conservation](/protocol/circuits/helpers) on the committed values and any change would have to be accounted for publicly.

A fixed share count avoids that entirely. The number committed in the note is the same at deposit and at spend, so conservation holds with no time-dependent term, and yield is realised only at the moment shares are unwrapped back to the underlying. Nothing inside the pool distinguishes a share note held for a day from one held for a year.

## Properties

-   **Shares are always 18 decimals.** [PPYieldToken](/protocol/contracts/pp-yield-token) fixes the ERC-4626 decimals offset to `18 - assetDecimals`, so ppUSDC over 6-decimal aUSDC carries a 10^12 virtual-share buffer against first-depositor inflation, on top of the dead shares the factory locks at creation.
-   **The rate comes from live backing.** A share's value is `totalAssets() / totalSupply()`, and `totalAssets()` is the wrapper's current aToken balance net of protocol fees. It is never stored, so it cannot go stale.
-   **The protocol fee is on yield only.** The wrapper charges `feeBps` (at most 20%) of the balance *increase* since its last checkpoint. Principal is never charged, and a fee change is never retroactive.
-   **Asset configuration is share-denominated.** Because ppUSDC has 18 decimals, its `minAmount` and `maxRelayFee` on the [Entrypoint](/protocol/contracts/entrypoint) are 10^12 times the plain-USDC figures for the same dollar value, and a share-denominated floor drifts upward in dollar terms as the rate rises.
-   **A yield note is still a pool note.** It can be transferred privately, withdrawn as ppUSDC through the plain [PrivacyPoolRelay](/protocol/contracts/privacy-pool-relay), or [ragequit](/operations/ragequit). Only the unwrap back to USDC needs the router, and only the unwrap depends on Aave liquidity.

## What the share count leaks, and what to do about it

The fixed share count protects the *time* dimension, but it moves the anonymity-set question to the *amount* dimension. Two users who each deposit 100 USDC at different exchange rates receive different share counts, and notes of different value are distinguishable. If deposits are sized in round USDC amounts, the share count records the rate at deposit time.

The mitigation is to size deposits in shares rather than in the underlying. [PPRouter](/protocol/contracts/pp-router)`.depositExactShares` exists for exactly this: the note commits to a fixed share value and the underlying actually spent floats up to a `maxUnderlyingIn` cap. An application that offers a small menu of standard share amounts keeps yield notes bucketed the way plain notes are; one that lets users type arbitrary USDC amounts does not. This is an application-layer obligation, not something the contracts can enforce.

## Implications for SDK callers

-   Size from shares. Use `sharesForUnderlying` when the input is a mint principal, or `sizeSharesForBudget` when the input is what the wallet is willing to spend all-in; both live on the yield session returned by `session.yieldFor(token)`. See [Yield deposit](/operations/yield-deposit).
-   Read value through `previewRedeem`. The UI-facing value of a yield note is `PPYieldToken.previewRedeem(shares)`, which the SDK wraps on the yield session's `token` interactor. Never show a user the raw share count as a balance.
-   A withdrawal to USDC is a normal transact whose `processor` is the router, so it cannot be an item in a [batch withdrawal](/operations/batch-withdraw): the batch relay is itself the processor. Withdrawing the shares as shares through the plain relay is the way to move many yield notes at once.
-   After a ragequit the owner holds loose ppUSDC in their wallet, not USDC. [PPYieldTokenZap](/protocol/contracts/pp-yield-token-zap)`.redeemToUnderlying` converts it in one call, and `PPYieldToken.redeem` exits to the aToken without touching Aave at all.
