# Deposit

> Move ETH or an ERC20 into the pool and get a private note back once the ASP approves the deposit.

A deposit moves public funds into the pool and creates a [note](/concepts/notes), the private value you spend from then on. It is also where the protocol's compliance screening happens: the [ASP](/concepts/asp-attestation) reviews every deposit before the resulting note becomes spendable, so everything circulating inside the pool has passed that review.

## How a deposit becomes spendable

```mermaid
sequenceDiagram
    participant Depositor
    participant Entrypoint
    participant PoolVault
    participant ASP as ASP service
    participant ASPRegistry

    Depositor->>Entrypoint: deposit (proof, noteData, aspCiphertext)
    Note over Entrypoint: keeps the vetting fee
    Entrypoint->>PoolVault: deposit
    Note over PoolVault: inserts the note's commitmentinto the state tree
    PoolVault-->>Depositor: Deposited + Note events
    Note over Depositor: note is PENDING
    Entrypoint->>ASPRegistry: registerLabel(aspCiphertext)
    ASPRegistry-->>ASP: LabelRegistered event
    Note over ASP: decrypts the opening,screens the deposit's label
    ASP->>ASPRegistry: updateASPRoot(root, ipfsCID)
    Note over Depositor: label is in the latest root:note is ACTIVE
```

The deposit moves through a few stages before the note is spendable:

1.  **Funds enter the pool.** You send ETH or an ERC20 token to the [Entrypoint](/protocol/contracts/entrypoint), which forwards it to the PoolVault. The vault inserts the new note's [commitment](/concepts/commitments-and-nullifiers) into its [state tree](/concepts/state-tree) and emits two events: `Deposited` (the public deposit details) and `Note` (the encrypted note payload). Your note now exists in `PENDING` state, and because the deposit transaction is public, your wallet is still visibly linked to it.
2.  **The ASP opening is registered.** In the same transaction, the Entrypoint registers the deposit's encrypted ASP opening (`aspCiphertext`) with the ASPRegistry, which emits a `LabelRegistered` event carrying that ciphertext.
3.  **The ASP screens the deposit.** The ASP service consumes the `LabelRegistered` event, decrypts the opening with its own key, then recomputes and screens the deposit's label, the identifying value that every note descended from it will carry.
4.  **Attestation makes the note spendable.** When the deposit is approved, the ASP includes the label in its association-set Merkle root, and an account holding the `POSTMAN_ROLE` publishes that root to the ASPRegistry contract. That published approval is the deposit's **attestation**. Spending proofs are checked against the latest published root, so your note turns `ACTIVE` only once its label is in it. Approval is often complete within an hour, though it can take up to 7 days. You can [check a specific deposit's status](#check-a-deposits-attestation-status) while you wait.

A deposit the ASP refuses moves to `REJECTED` instead, and the note's owner can still recover the funds through [ragequit](ragequit), a public exit that works without the ASP's cooperation.

## Register before you spend

The deposit path never touches the keystore, so a wallet that has not registered can still deposit successfully. Spending and ragequit both prove keystore membership, though, which means a note owned by an unregistered wallet can be created but neither spent nor recovered until the owner registers. For that reason, register before you spend rather than before you deposit. Depositing first is fine, but the note stays unspendable until registration, so frontends typically handle [registration](../operations/register-viewing-key) as part of account setup.

## How to deposit

```ts
const NATIVE_ETH = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";

const receipt = await session.deposit({
    tokenId: NATIVE_ETH,
    value:   "0x2386f26fc10000",        // 0.01 ETH in wei
});

await session.discoverNotes();
```

Native ETH uses the placeholder asset address `0xeeee…eeee`. The SDK's `AddressSchema` validates the hex format only, and the deposit path compares the asset case-insensitively, so either casing is accepted.

```ts
const USDC_SEPOLIA = "0x1c7d4b196cb0c7b01d743fbc6116a902379c7238";

// session.deposit() checks your ERC20 allowance and, when it is too low,
// submits an approve for the deposit value plus the vetting fee before
// sending the deposit transaction itself.
await session.deposit({
    tokenId: USDC_SEPOLIA,
    value:   "0x4c4b40",   // 5 USDC (6 decimals)
});

await session.discoverNotes();
```

The ERC20-specific constraints are:

-   The token must be whitelisted on the `Entrypoint` (Sepolia USDC and USDT are pre-configured).
-   `value` uses the token's smallest unit.
-   The `tokenId` is the ERC-20 contract address; it may be checksummed or lowercase, so use consistent casing when filtering local notes.

When your current allowance is too low, the deposit takes two transactions: the SDK first submits an `ERC20.approve` granting the `Entrypoint` the deposit value plus the vetting fee, then the deposit itself. With a sufficient allowance already in place it sends only one.

`deposit()` returns a transaction receipt, not note material. For an owned deposit, the SDK persists the note locally, where it appears in `exportAccount().notes` as `PENDING` right away. After ASP attestation, `discoverNotes()` promotes it to `ACTIVE`. Use `prepareDeposit(...)` first if you need the pending note before submitting.

## Constraints

-   Min amount, vetting fee, and max relay fee are set per asset on the `Entrypoint`. Read them live with `Entrypoint.assets(asset)`, which returns `AssetConfig{ enabled, minAmount, vettingFeeBPS, maxRelayFee }`, rather than assuming fixed values. There is no max deposit amount.
-   **Pre-attestation visibility:** until the ASP attests the deposit label, the note exists on-chain in `PENDING` state and the depositor's wallet is linked to it. Privacy is realized only after attestation and a subsequent spend.

## Check a deposit's attestation status

Each note carries its deposit's label, and the ASP answers status queries per label:

```ts
const labelHash = hashService.hash([note.label]);
const decimalLabelHash = BigInt(labelHash).toString();
const attestation = await fetch(
    `${aspUrl}/labels/${decimalLabelHash}/status`,
).then(r => r.json());

switch (attestation.status) {
    case "approved":
        console.log("Note is spendable. ACTIVE.");
        break;
    case "pending":
        console.log("ASP still reviewing. Wait or check back later.");
        break;
    case "rejected":
        console.log("ASP refused this deposit. Use ragequit to recover.");
        break;
    case "unknown":
        console.log("ASP has no record of this label. Keep waiting or re-check.");
        break;
}
```

A rejection is a different state from a pending review: if the ASP explicitly rejects a label (a sanctions hit or compliance flag), the note moves to `REJECTED` rather than back to `PENDING`, and [ragequit](ragequit) is its recovery path.

**Most apps don't call this directly.** `discoverNotes()` integrates the attestation check into its sync, so your local NoteManager status updates from `PENDING` to `ACTIVE` automatically when the ASP catches up. Use this query when you need to check one specific deposit. The ASP indexes the Poseidon hash of the label, encoded as a decimal integer (`GET {aspUrl}/labels/{decimalLabelHash}/status`).

## Behind the scenes

SDK call `session.deposit({tokenId, value})` (same signature for ETH and ERC20)
Contract method `Entrypoint.deposit(...)`, which calls `PoolVault.deposit(...)` and then registers the encrypted ASP opening via `aspRegistry().registerLabel(aspCiphertext)`
ERC20 prerequisite An `Entrypoint` allowance covering the deposit value plus the vetting fee. `session.deposit()` submits the `approve` automatically when the current allowance is too low.
Circuit `deposit` Groth16
On-chain events `PoolVault` emits `Deposited(outputCommitment, asset, depositedValue, caller)` plus a `Note(hint, data)` carrying the encrypted payload (the `Note` event itself does not include the commitment). `Entrypoint` additionally emits its own `Deposited(asset, depositor, value, fee)` capturing the depositor and vetting fee, and `ASPRegistry` emits `LabelRegistered(ciphertext)` carrying the encrypted opening the ASP decrypts to screen the label.

## Variants

-   [Yield deposit](yield-deposit): shield the value as ppUSDC shares that earn Aave interest.
-   [Swap and deposit](swap-and-deposit): enter from any wallet token, with the swap and the deposit in one transaction.

## What's next

-   [Manage notes](manage-notes): discovery promotes the attested note into your spendable set.
-   [Transfer](transfer): spend the note privately inside the pool.
-   [Withdraw](withdraw): exit the value to a public address.
-   [Ragequit](ragequit): the recovery path if the ASP rejects the deposit.
