Register, run, manage, and exit a zkScatter relayer.
A relayer matches orders off-chain, generates the counterparty
half-proof, and submits settleAuth transactions on-chain in exchange
for a basis-point fee. This page covers the SDK side — operator
on-chain actions and HTTP-API expectations. The matcher logic itself
lives in zk-relayer/ of the monorepo.
Operator lifecycle
- Register
Post a bond, set a fee, publish your URL.
- Operate
Run a matcher service, expose the standard HTTP endpoints, settle matched orders.
- Update
Change URL, fee, profile metadata as needed.
- Exit
Request exit, wait the cool-down, withdraw the bond.
Register
import {
registerRelayer,
MAX_RELAYER_FEE_BPS,
} from "@zkscatter/sdk/relayer";
await registerRelayer(
network.contracts.relayerRegistry,
{
url: "https://relayer.example.com",
feeBps: 30, // 0.30 %, must be ≤ MAX_RELAYER_FEE_BPS
bondEth: "1.0", // sent as msg.value
},
signer,
);The contract enforces a minimum bond — read registry.minBond()
first. The bond is returned in full on exit (after the 7-day
cool-down); the protocol does not slash it. Apps still favour
higher-bonded operators because bond size signals operational
commitment. (SDK note: the parameter is named bondEth for legacy
reasons. The SDK supports both bond modes: in native mode
(bondToken == address(0)) it forwards the bond via msg.value;
in ERC20 mode (non-zero bondToken) the operator must approve
the registry for the bond amount first, and the SDK then calls
register / addRelayerBond with msg.value = 0. The TON-bond
migration is a separate PR; until it lands, the deployed bond
asset is whatever the contract was deployed with.)
Update info
import { updateRelayerInfo } from "@zkscatter/sdk/relayer";
await updateRelayerInfo(
network.contracts.relayerRegistry,
{ url: "https://new.example.com", feeBps: 25 },
signer,
);
// Both `registerRelayer` and `updateRelayerInfo` validate `feeBps`
// against `MAX_RELAYER_FEE_BPS` (500 = 5%) before prompting the wallet.URL changes propagate to apps on their next loadRelayersWithApiInfo
refresh — typically within 60s.
Add bond
import { addRelayerBond } from "@zkscatter/sdk/relayer";
await addRelayerBond(network.contracts.relayerRegistry, "0.5", signer);More bond = stronger commitment signal, which apps factor into their own ranking. There’s no on-chain ranking and no on-chain slashing; the bond is locked collateral that returns in full on exit (after the 7-day cool-down). Reputation enforcement happens off-chain via the planned Dispute Registry — see whitepaper §10.2.
Read your status
import { loadOperatorRow } from "@zkscatter/sdk/relayer";
const row = await loadOperatorRow(
network.contracts.relayerRegistry,
account,
readProvider,
);
// row.status: "active" | "cooldown" | "offline" | "unregistered"
// row.exitRequestedAt + EXIT_COOLDOWN_SECONDS = unix seconds when bond becomes withdrawableSurface this in your operator dashboard.
Exit
import {
requestRelayerExit,
executeRelayerExit,
EXIT_COOLDOWN_SECONDS,
} from "@zkscatter/sdk/relayer";
await requestRelayerExit(network.contracts.relayerRegistry, signer);
// wait EXIT_COOLDOWN_SECONDS (7 days) for any outstanding obligations
await executeRelayerExit(network.contracts.relayerRegistry, signer);
// bond returned to operator addressWhile in cool-down your relayer should stop accepting new orders but continue honoring matched ones — otherwise the bond is at risk.
HTTP API contract
Apps using the SDK’s RelayerClient expect these endpoints:
| Endpoint | Purpose | Notes |
|---|---|---|
GET /api/info | Identity + fee + capabilities | Used for liveness probe |
POST /api/orders | Submit signed order | Body: { order, signature, feeMode? } |
GET /api/orders/:address | Orders for a maker (active + history) | ?status=&limit=&offset= for filtering |
GET /api/orders/:address/:nonce | Single order detail | — |
DELETE /api/orders/:address/:nonce | Cancel an active order | x-cancel-signature header |
Claims are surfaced as the claims field on RelayerOrder. There is
no separate /api/claims endpoint — apps derive claim entries from
their pending order list.
Reference implementation: zk-relayer/src/http/ in the monorepo.
/api/info shape
{
name: string,
version: string,
address: string, // operator address
fee: number, // bps, must match on-chain
orderCount: number,
settlement: string, // contract address, must match the chain it's serving
profile?: RelayerProfile,
}profile will be sanitized client-side by sanitizeProfile — keep it
honest, no HTML, https URLs only.
Operating posture
- Run two replicas behind a load balancer — health checks should return non-200 the moment matching is impaired so apps switch to another relayer.
- Surface latency in
/api/info— apps can use this for routing. - Pre-warm Poseidon and the prover zkey on boot — cold-start match latency tanks user experience.
- Watch reorg depth — submit settlements only after enough confirmations on the leaves you’re proving against, otherwise your proofs go stale.
Common errors
`Bond too low` on register
Below the contract minimum. Read registry.minBond() first.
`Already registered`
The signer’s address is already in the registry. Use updateRelayerInfo
or exit first.
`executeExit` reverts with `Cooldown`
Less than 7 days since requestExit. Wait or query loadOperatorRow
for exitAvailableAt.