> ## Documentation Index
> Fetch the complete documentation index at: https://docs.privacyboost.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Earn (ERC-4626 Vaults)

> Move a shielded balance into an ERC-4626 yield vault and back without leaving the pool. The external call gateway spends a private note, runs the vault call, and credits the measured output as a new private note — the depositor's account stays hidden.

# Earn (ERC-4626 Vaults)

**Earn** lets a user put a shielded balance to work in an ERC-4626 yield vault
(for example a Morpho vault) and pull it back out — without the funds ever
leaving the shielded pool. A deposit spends a private asset note, routes it
through the vault via the **external call gateway**, and credits the vault
shares back as a new private note; a withdraw redeems those share notes the
same way. The vault call itself is public on-chain, but the account that owns
the deposited note is not.

<Warning>
  **Preview feature — pending external audit.** The External Call Gateway (Earn)
  moves user funds through operator-approved external targets and ships to
  mainnet only after a gateway-specific security audit (see the *External Call
  Gateway Audit Gate* in `AUDIT_SCOPE.md`). Treat the API as
  stable-but-unaudited and gate production use on your own review. Earn is
  enabled per deployment: the server advertises the executor as
  `externalGatewayAddress` in [`/info`](/sdk/concepts/configuration) only when
  the gateway is enabled. Detect support at runtime with
  `sdk.vault.getGatewayConfig()`, which returns the executor address, request
  constraints, and availability. Deployments without a gateway return
  `enabled: false`; feature-gate on that field and the relevant availability.
</Warning>

## How it works

An earn deposit is a **gateway unshield**. Instead of paying a private note out
to a public address, the SDK signs an authorization that lets the gateway run
the vault call and credit the result straight back into the pool:

```text theme={null}
 asset note (private) ──spend──▶  gateway executes vault deposit  ──▶  share note (private)
                                          │
                        failure / expiry / below minOut
                                          ▼
                                  input refunded as a private note
```

1. **Preview & authorize.** The SDK reads the vault on-chain (`asset()`,
   `previewDeposit` / `previewRedeem`), derives a `minOutputAmount` floor from
   your slippage bound, and signs the `PB:WITHDRAW:GATEWAY` authorization for
   exactly that request.
2. **Execute.** The gateway spends the input note and calls the vault. The
   output is **measured on-chain**, not trusted from the preview.
3. **Credit.** The measured vault output is credited as a new gateway-origin
   private note. The owner's identifying keys never appear in the transaction.
4. **Refund on failure.** If the vault reverts, the request expires, or the fill
   would land below the signed `minOutputAmount`, the input is refunded as a
   private note via the fallback receipt. Funds never leave the pool uncredited.

A withdraw is the mirror image: it redeems share notes and credits the asset
output.

## SDK API shape

The earn surface lives on the **vault resource** (`sdk.vault`). Reads are
public; the two write operations require an authenticated session.

| Method                                        | Auth    | Purpose                                                                                                       |
| --------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------- |
| `getGatewayConfig()`                          | public  | Gateway discovery: executor address, request constraints, rescue delay, availability. Use it to feature-gate. |
| `listEarnVaults()`                            | public  | Vault catalog: APY, TVL, warnings, and per-operation availability.                                            |
| `getEarnVaultHistory(vault, metric, period?)` | public  | One cached chart series (TVL or net APY) for a vault.                                                         |
| `getEarnVaultCharts(vault, period?)`          | public  | Both chart series for a vault, fetched concurrently.                                                          |
| `earnDeposit(params)`                         | session | Deposit assets into a vault; credits a share note.                                                            |
| `earnRedeem(params)`                          | session | Redeem share notes; credits an asset note.                                                                    |

`earnDeposit` / `earnRedeem` take an `EarnParams`. **`amount` is net** — exactly
what enters the vault call (assets for a deposit, shares for a redeem); the
unshield fee is charged on top, so your notes must cover `amount + fee`.
`maxSlippageBps` bounds the fill below the on-chain preview (default `100` =
1%).

```typescript theme={null}
// Detect gateway support and browse vaults — no session required
const gateway = await sdk.vault.getGatewayConfig();
const vaults = await sdk.vault.listEarnVaults();

// Deposit 100 USDC (net) into a vault; the fee is charged on top
const result = await sdk.vault.earnDeposit({
  vaultAddress: '0xVault…',
  amount: 100_000000n,   // net assets entering the vault (6-decimal USDC)
  maxSlippageBps: 100,   // reject a fill more than 1% below the preview
});

// Later: redeem shares back to the underlying asset
await sdk.vault.earnRedeem({ vaultAddress: '0xVault…', amount: shares });
```

```bash theme={null}
privacy-boost earn vaults
privacy-boost earn deposit  --vault 0xVault… --amount 100 --human --decimals 6
privacy-boost earn withdraw --vault 0xVault… --amount 50  --human
```

### Tracking settlement

An earn operation returns an `EarnResult` with a `requestId`. Because the credit
lands in a later epoch than the unshield, **poll the unshield status and read
the `gateway` projection** — `gateway.creditStatus === 'completed'` is the
terminal state. The top-level `completed` flag only covers the unshield epoch,
not the vault credit.

```bash theme={null}
privacy-boost earn status <REQUEST_ID>
```

## Vault history charts

`getEarnVaultHistory` and `getEarnVaultCharts` return cached TVL and net-APY
series suitable for rendering a chart. Both are **public** — usable before
login.

* `metric` is `'tvlUsd'` or `'netApy'`; `period` is `'1w' | '1m' | '3m' | 'all'`
  (server default `1w`). `getEarnVaultCharts` fetches both metrics at once and
  rejects if either fails — call `getEarnVaultHistory` twice if you want partial
  results.
* **APY values are decimal ratios** (`0.05` = 5%); TVL values are USD. Points
  ascend by timestamp.
* A `null` `points[].value` is an **upstream gap** — render it as a gap rather
  than interpolate across it.
* Check `metadata.state` before presenting the data as current. Today it is one
  of `fresh`, `stale` (upstream unavailable — this is the last good series),
  `synthetic` (deterministic testnet data), or `unsupported`. It is an **open
  set**: treat an unrecognized state as renderable data of unknown freshness,
  not an error.
* While the server's cache warms, a cold request may answer `503` with a
  retry-after hint; the SDK's standard retry policy already honors it.

```typescript theme={null}
const { tvlUsd, netApy } = await sdk.vault.getEarnVaultCharts('0xVault…', '1m');
for (const point of netApy.points) {
  if (point.value === null) continue; // upstream gap
  plot(point.timestamp, point.value); // decimal ratio: 0.05 = 5%
}
```

```bash theme={null}
# One series
privacy-boost earn history --vault 0xVault… --metric netApy --period 1m
# Both series (omit --metric)
privacy-boost earn history --vault 0xVault…
```

## Trust and privacy

Earn is a **deposit-and-credit** extension: the gateway can only spend the note
you authorize and credit the measured result back into the pool. It grants no
new authority to move or seize funds.

| Actor                           | Can                                                                    | Cannot                                                                      |
| ------------------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| **Owner**                       | authorize a deposit/redeem of their own notes; spend the credited note | —                                                                           |
| **Gateway executor / operator** | run the approved vault call; measure and settle the exact output       | learn which account owns the note; redirect the credit; spend from the pool |
| **Relay / prover**              | submit the gateway epoch from non-spend values                         | change the amount, target vault, or recipient; credit a different account   |
| **TEE**                         | observe the request; derive the owner's credited note via the registry | sign anything; custody, move, or seize funds                                |

A gateway-origin note joins the **same shared note tree** as every other deposit
and transfer, so its anonymity set is the full mixed pool. Earn hides *who*
deposited into the vault, **not** *that the vault was used* — the vault call
(target and amount) is public on-chain. The `minOutputAmount` floor is signed,
so no operator can settle a deposit into a worse-than-authorized fill; the worst
case is a refund of the input.

## Launch limitations

These are known constraints of the current (pre-audit) preview; design around
them:

* **Pre-audit.** The gateway contracts are outside the completed audit scope
  until the *External Call Gateway Audit Gate* clears. Do not custody
  significant value on it in production before then.
* **Enabled per deployment.** A deployment without a gateway has no
  `externalGatewayAddress` in `/info`, and `getGatewayConfig()` returns
  `enabled: false`. Always feature-gate on `enabled` and the operation's
  `availability.canSubmit` value.
* **Operator-curated catalog.** Only vaults the operator approves are usable.
  `listEarnVaults()` reports per-operation availability and any warnings — honor
  them before offering a deposit.
* **Slippage is a floor, not a guarantee of execution.** A fill below the signed
  `minOutputAmount` refunds the input via the fallback receipt instead of
  executing; surface that outcome rather than treating the request as failed.
* **History is a best-effort cached read.** Freshness is reported by
  `metadata.state`, and a cold cache can briefly `503`. Do not treat a `stale`
  or unknown state as an error.
* **Single-chain.** Earn works on a chain that hosts both a pool and a deployed
  gateway.

## Using it from the SDK

* **TypeScript / WASM** — the earn methods on `sdk.vault`. See the
  [TypeScript API reference](/sdk/typescript/api-reference).
* **CLI** — the `earn` subcommands. See the
  [Commands reference](/sdk/cli/guides/commands#earn-commands).
* **iOS / Android / React Native** — the earn deposit/redeem and catalog methods
  plus JSON-passthrough history reads (`getEarnVaultHistoryJson`,
  `getEarnVaultChartsJson`), matching the existing earn read surface.
