# Yield deposit

> Shield a yield-bearing position: wrap the underlying into an exact number of ppUSDC shares and deposit them in one router transaction.

A yield deposit puts USDC to work on Aave while it sits in the pool. You pay USDC; you hold a [note](/concepts/notes) denominated in ppUSDC shares whose value in USDC rises as interest accrues. The pool sees an ordinary ERC-20 deposit of the share token. [PPRouter](/protocol/contracts/pp-router) does the wrapping in the same transaction, and the proof you generate is a standard [deposit](/operations/deposit) proof whose `tokenId` is the wrapper.

## How a yield deposit works

```mermaid
sequenceDiagram
    participant You
    participant Router as PPRouter
    participant Zap as PPYieldTokenZap
    participant Entrypoint
    participant PoolVault

    Note over You: deposit proof for exactly N ppUSDC shares
    You->>Router: approve USDC, then depositExactShares(proof, noteData, aspCiphertext, maxUnderlyingIn)
    Router->>Zap: buyFixedYieldToken(N + vetting fee)
    Note over Zap: supply USDC to Aave, mint exact shares
    Router->>Entrypoint: deposit(proof, noteData, aspCiphertext)
    Entrypoint->>PoolVault: deposit
```

1.  **Size in shares.** The note commits to a share count, so that is the fixed leg. Choose it from a mint principal (`sharesForUnderlying`, which is `previewDeposit` exactly) or from an all-in wallet budget (`sizeSharesForBudget`, which reserves a small rate headroom and fits the router's full price inside the rest).
2.  **Prove.** The SDK builds the normal deposit proof with `tokenId = ppUSDC` and `value = shares`, then re-targets the same `(proof, noteData, aspCiphertext)` at `PPRouter.depositExactShares`. The proof's `context` is `keccak(noteData)`, which the router forwards unchanged, so it stays valid.
3.  **Cap the spend.** `maxUnderlyingIn` bounds the USDC the router may pull: the caller's figure, or the router's `quote` plus `DEPOSIT_RATE_HEADROOM_PPM` (1 ppm, roughly seventeen minutes of accrual at 3% APR). A deposit that outlives its cap reverts `PPRouter_SlippageExceeded` and is retried with a fresh quote; it never pays more than the cap.
4.  **Submit.** Approve the router for the cap, then send the deposit. The router buys exactly `shares + vettingFee` shares through the zap, hands them to the Entrypoint with your proof, and asserts its own share balance is back to baseline.

## How to deposit

Yield methods live on the namespace `session.yieldFor(token)` returns for a configured wrapper or its underlying:

```ts
const toHex = (value: bigint): `0x${string}` => `0x${value.toString(16)}`;

const yieldSessions = session.yieldFor(ppUsdcAddress);
if (!yieldSessions) throw new Error("no yield deployment configured for this chain");

// Size from what the wallet is willing to spend all-in: 100 USDC (6 decimals).
const sized = await yieldSessions.deposit.sizeSharesForBudget(100_000_000n);
console.log("shares:", sized.shares, "cap:", sized.maxUnderlyingIn, "headroom:", sized.headroom);

const prepared = await yieldSessions.deposit.prepareYieldDeposit({
    value: toHex(sized.shares),
    maxUnderlyingIn: sized.maxUnderlyingIn,
});

// Submit prepared.approvalTxs in order (a zero-reset precedes the approval when a
// residual allowance exists; mainnet USDT reverts otherwise), then prepared.depositTx.
// Persist prepared.pendingNote once the deposit is mined.
```

`prepareYieldDeposit` returns `pendingNote`, `approvalTxs`, `depositTx`, and the sizing it settled on (`sharesValue`, `underlyingNeeded`, `maxUnderlyingIn`). A caller cap below the router's quoted `underlyingNeeded` is refused with `InvalidYieldAmount` rather than producing a transaction that will revert.

The headroom starts being consumed the moment you size: it has to cover proof generation, the wallet prompts, the approval confirmation, and inclusion. If the budget no longer fits by the time you prepare, size and prepare again.

## Constraints

-   **Shares, not USDC, are what you hold.** Show a user `previewRedeem(shares)` as the position's value and never the raw share count; the SDK's yield `token` interactor exposes the ERC-4626 reads.
-   **Bucket in shares for privacy.** Round USDC amounts produce rate-dependent share counts, which partition the anonymity set by deposit time. Offer standard share amounts; see [Yield shares](/concepts/yield-shares).
-   **The vetting fee is charged in shares.** The router mints `value + vettingFee` shares and the [Entrypoint](/protocol/contracts/entrypoint) keeps the fee, so the underlying you spend covers both. Because ppUSDC has 18 decimals, its Entrypoint `minAmount` and `maxRelayFee` are share-denominated.
-   **Native mode.** A ppETH router accepts ETH directly through `depositExactSharesNative`, where `msg.value` is both the funding and the cap and the unspent remainder is refunded in the same transaction. The SDK encodes that call on the swap-shaped deposit path when the target router is native-mode.
-   **Aave is a counterparty.** Your underlying is supplied to Aave. A failure there is a loss there; the pool cannot insulate you from it, and unwrapping later depends on Aave liquidity.

## Availability

Yield is live in production: [Ethereum mainnet](/deployments/mainnet) carries ppUSDC, ppUSDT, and ppETH (the last through a native-mode router, so users deposit ETH directly), and [BNB Chain](/deployments/bnb) carries ppUSDT. There is none on Citrea or Sepolia.

The SDK's canonical `YIELD_DEPLOYMENTS` table is nonetheless empty, and `LOCAL_FORK_YIELD_DEPLOYMENT` exists for the anvil Sepolia fork only. A session learns about the live deployments either explicitly (`withYieldDeployment` / `withYieldDeployments` with the addresses from the chain pages) or from the production relayer's `/v1/details`, whose `yield[]` entries carry the wrapper, underlying, router, zap, and decimals per deployment; the builder derives deployments from that feed when none were set explicitly. Check `session.yieldFor(token)` for `undefined` before offering the option.

## Behind the scenes

SDK call `session.yieldFor(token).deposit.sizeSharesForBudget(...)` or `.sharesForUnderlying(...)`, then `.prepareYieldDeposit(...)`.
Contract method [`PPRouter`](/protocol/contracts/pp-router)`.depositExactShares(proof, noteData, aspCiphertext, maxUnderlyingIn)`, which calls `PPYieldTokenZap.buyFixedYieldToken` and then `Entrypoint.deposit`.
Sizing constants `DEPOSIT_RATE_HEADROOM_PPM = 1`, `ZAP_SUPPLY_BUFFER_UNITS = 2` (the zap's rounding buffer, already inside the router's quote), `CROSS_CHAIN_DEPOSIT_RATE_HEADROOM_PPM = 500` for a share deposit that lands after a bridge.
Circuit [`deposit`](/protocol/circuits/deposit) with `tokenId = ppUSDC` and `value` in shares.

## What's next

-   [Yield withdraw](yield-withdraw): unwrap back to USDC for a recipient.
-   [Manage notes](manage-notes): the yield note is attested and discovered like any deposit.
-   [Reshield](reshield): land a swap as a yield share instead of a plain token.
