How it fits together
The SDK sits between your application and three independent parties: a set of contracts on an EVM chain, and three backend services that index and relay on the chain's behalf. None of them holds your keys, and none of them can move your funds.
This page maps who talks to whom, and — just as importantly — what the SDK deliberately never asks.
The whole system
Three things are worth reading off that picture.
Proving happens on your machine. The prover is inside the SDK box, not out at a service. No secret — note values, nsk, the notes being spent — ever leaves the process.
Only two parties write to the chain. Your own signer broadcasts deposits and cancellations. The relayer broadcasts everything else. There is no third writer.
The read path and the write path are different services. fmd-webserver is read-only and never sees a transaction; the relayer never serves note data.
Who the SDK talks to
| Party | SDK entry point | What it is asked for |
|---|---|---|
| fmd-webserver | FmdClient, NoteSource | encrypted notes, commitment chunks, nullifier chunks, sync watermarks |
| relayer | RelayerClient, Submitter, DepositStream | chain registry, fee estimates, spend submission, deposit-flush events |
| metaquoter | fetchSwapQuote | best swap route and minOut |
| EVM chain | ChainAdapter | asset registry, fee rate, deposit broadcast, escrow reads |
The deployment also runs an explorer indexer, a risk-screening API, and a price feed. The SDK contacts none of them — they are not on any wallet path.
What the SDK never asks
Several requests are absent by design, because making them would identify you to whoever answered.
| Never asked | Why | What happens instead |
|---|---|---|
"Is nullifier N spent?" | naming a nullifier names a note you own | the whole spent set is mirrored and filtered locally |
"Give me the Merkle path for leaf i" | the leaf index identifies the note you are spending | the tree is rebuilt locally from an append-only chunk feed |
| "Which notes are mine?" | that is the entire privacy property | every encrypted note is downloaded and trial-decrypted locally |
The third is negotiable and the other two are not. FMD lets you delegate detection to the server in exchange for far less bandwidth — a deliberate, permanent, and non-default trade.
Shielding: the path you broadcast
A deposit is the one operation your own signer sends. It is also the one with a settlement step after mining: escrowed funds are not in the tree until the relayer folds them in.
Steps 4 and 5 are why a freshly deposited note is not immediately spendable, and why DepositStream exists.
Spending: the path the relayer broadcasts
A transfer, withdraw, or swap is never sent by your signer. The spend proof binds the relayer's address, so the pool rejects a transaction that any other account submits.
The relayer sees a valid proof, its public inputs, and the fee note addressed to it. It does not learn which notes were spent, who the payee is, or the amount — those are the circuit's private inputs. What it does learn is your IP address and the timing of your submission.
The relayer is a pluggable, not a dependency
Submitter is an interface. Race several relayers, route through your own, or broadcast directly from an account you control — see Pluggable interfaces. The same is true of NoteSource: the FMD server is the default index, not the only possible one.
What each party can and cannot see
| Party | Learns | Cannot learn |
|---|---|---|
| fmd-webserver | that some client fetched a page of the public feed | which notes are yours — under the default full strategy |
| relayer | your IP, submission timing, the fee it is paid | spent notes, payee, amount |
| metaquoter | that someone wants a route for a token pair and size | who is asking, or whether the swap happens |
| the chain | a deposit's payer and amount; a withdraw's recipient and amount | anything about a shielded transfer beyond its existence |
Shielding and unshielding are the visible edges. What happens between them is not.
Next
- Concepts — notes, nullifiers, the tree, FMD
- Syncing — the read path in detail
- Deposit — the write path in detail
- Architecture — how the SDK's own code is layered