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

# Nullifiers and spent state

Learn how one public nullifier marks a child note as spent and how to handle an unclear transaction result.

A nullifier is the public marker for one spent child note. It lets the pool stop a double spend without publishing the child secret or Merkle path.

## Lifecycle

```mermaid
flowchart TB
  subgraph Spend["Spend one child note"]
    direction TB
    START["Choose one child note"]
    SPENT{"Nullifier already recorded?"}
    STOP["Stop: note was already spent"]
    PROVE["Build and submit withdrawal proof"]
    VALID{"Proof valid and nullifier still unused?"}
    DONE["Record nullifier and send ETH"]
    REJECT["Reject withdrawal"]

    START -->|check spent state| SPENT
    SPENT -->|yes| STOP
    SPENT -->|no| PROVE
    PROVE -->|pool verifies| VALID
    VALID -->|yes| DONE
    VALID -->|no| REJECT
  end
```

## What becomes public

Observers can see the nullifier, pool, transaction, destination, gross amount, fee, sender, and timing. The nullifier reveals no child secret; the surrounding transaction remains public.

## Double-spend rule

The client checks spent state before proving to avoid wasted work, then checks again after proving. The contract performs the final check and records the nullifier atomically with payment.

The sibling child has a different nullifier and remains available until that nullifier is spent.

## When submission is unclear

Do not assume a timeout means the child is still available. Check the original transaction, its receipt, the pool's nullifier mapping, and the matching `Withdrawal` event. Retry only after a definite failure and an unspent pool result.

See [Choose a submission path](/use/relayer-or-wallet.md) for the user flow.
