# Ragequit

> Recover a note's full value through a public exit, even when the ASP refuses to cooperate.

Ragequit publicly recovers a note's full value to its owner via on-chain transfer, regardless of [ASP](/concepts/asp-attestation) approval. It exists to guarantee self-custody at all times, even when the ASP refuses to attest a deposit.

## When to use it

Ragequit is for a note you cannot spend normally, or cannot wait to spend. It always recovers the full value, but it exposes the note on-chain and the recovered value has no anonymity set, so use it only when there is no private alternative.

| Note status | Private alternative | Ragequit's role |
| --- | --- | --- |
| `ACTIVE` | [Withdraw](withdraw) or [transfer](transfer), both keep the `label` private | Unnecessary, and would expose the `label` for no reason |
| `PENDING` | None yet, the note is awaiting [attestation](/concepts/asp-attestation) | Get the value out before attestation, or wait |
| `REJECTED` | None, the ASP refused the deposit | The designed recovery path |

## How it works

- **Owner-only:** the call must come from the note's current owner, the keystore-registered address the note is bound to. The proof requires keystore membership, so the owner must be [registered](register-viewing-key) first. Because notes are transferable, the owner is whoever holds the note now, not necessarily the original depositor.
- **Destination binding:** the funds always return to the owner's address. The circuit forces this, so the recovery cannot be redirected to any other address.
- **Exposes the note on-chain:** the `Ragequit` event always publishes the note's [commitment](/concepts/commitments-and-nullifiers), its `label`, and the owner's address, whatever the note's status. A deposit note links straight back to its `Deposited` event through the matching commitment. A note received through transfers has no such match, but its `label` is the deposit's lineage identifier, so anyone holding the label-to-deposit mapping (the ASP) can still tie it to the origin deposit. Either way, the recovered value has no anonymity set.
- **Any unspent status:** ragequit works on any unspent commitment, whatever its status.
- **Gas and value:** the caller pays gas in ETH, and the note's full value moves in one step. There is no partial ragequit.
- **Works while paused:** `ragequit` is the one `PoolVault` entry point without a pause guard, so a paused pool cannot trap funds.
- **A yield note pays shares:** ragequit pays the note's `tokenId`, so a [yield-share](/concepts/yield-shares) note lands as ppUSDC in your wallet, not USDC. Unwrap it afterwards with [`PPYieldTokenZap`](/protocol/contracts/pp-yield-token-zap)`.redeemToUnderlying`, or exit to the aToken with `PPYieldToken.redeem`, which never touches Aave.

## How to use it

```ts
const allNotes = (await session.exportAccount()).notes;
const stuckNote = allNotes.find(
    (n) => n.status === "PENDING" || n.status === "REJECTED",
);
if (!stuckNote) throw new Error("no PENDING or REJECTED note to recover");

const result = await session.rageQuit({
    commitment: stuckNote.commitment,
});

console.log("ragequit tx:", result.txHash);
```

**Ragequit is a last-resort path.** It exists so the protocol remains self-custodial and never freezes funds, regardless of ASP approval.

## Behind the scenes

SDK call `session.rageQuit({commitment})`
Contract method [`PoolVault`](/protocol/contracts/pool-vault)`.ragequit(...)`
Contract gate `require(msg.sender == _proof.ownerAddress())`: caller must be the keystore-registered owner of the note
Circuit `ragequit` Groth16: proves keystore membership and knowledge of the `noteSecret` and the nullifying / revocable keys. It does **not** use `depositSecret`, which lives only in the deposit circuit.
On-chain effect Note commitment marked as exited and value transferred to the note's recorded `ownerAddress`. The `Ragequit` event emits the `ragequitter` (current owner), not the original depositor.
