Skip to Content
DocumentContractsRelayerRegistry
Protocol reference

On-chain directory of bonded relayer operators.

RelayerRegistry is the source of truth for who’s allowed to submit settleAuth transactions. Apps read it for relayer discovery; the settlement contract reads it on every match to gate caller eligibility.

Storage

struct Relayer { string url; // /api/info endpoint uint256 fee; // basis points (capped at MAX_FEE = 500 bps) uint256 bond; // staked native amount, in wei uint256 registeredAt; // unix seconds, 0 = never registered uint256 exitRequestedAt; // unix seconds, 0 = not in cool-down bool active; } mapping(address => Relayer) public relayers; address[] public relayerList; // every address that's ever been added uint256 public minBond; // governance-settable minimum uint256 public constant EXIT_COOLDOWN = 7 days; uint256 public constant MAX_FEE = 500; // 5% absolute cap IIdentityRegistry public immutable identityRegistry; address public treasury;

Lifecycle

  1. Register

    register(url, fee) with msg.valueminBond(). Operator must hold a valid IdentityGate attestation. Sets registeredAt, active = true.

  2. Operate

    updateInfo(url, fee) to change endpoint or fee. addBond() with msg.value to top up bond.

  3. Exit

    requestExit() — sets exitRequestedAt = now. Operator stays active = true during the cool-down so in-flight matches still settle.

  4. Withdraw

    After EXIT_COOLDOWN (7 days), executeExit() returns the bond and sets active = false.

External entry points

function register(string calldata url, uint256 fee) external payable; function updateInfo(string calldata url, uint256 fee) external; function addBond() external payable; function requestExit() external; function executeExit() external; function relayers(address operator) external view returns (Relayer memory); function getRelayerCount() external view returns (uint256); function getActiveRelayers() external view returns (address[] memory); function isActiveRelayer(address operator) external view returns (bool); function getFee(address operator) external view returns (uint256); function getSettlementInfo(address operator) external view returns (bool isActive, uint256 fee, address treasury_);

Events

event RelayerRegistered( address indexed relayer, string url, uint256 fee, uint256 bond ); event RelayerUpdated( address indexed relayer, string url, uint256 fee ); event BondAdded( address indexed relayer, uint256 amount ); event ExitRequested( address indexed relayer, uint256 exitAfter ); event RelayerExited( address indexed relayer, uint256 bondReturned ); // Governance event TreasuryUpdated(address oldTreasury, address newTreasury); event MinBondUpdated(uint256 oldMinBond, uint256 newMinBond);

Errors

ErrorWhen
AlreadyRegisteredregister called by an already-active operator
NotRegisteredAction requires an existing entry
RelayerNotActiveOperator was deactivated (e.g. completed exit)
NotVerifiedIdentityRegistry.isVerified(msg.sender) is false
InsufficientBondmsg.value < minBond on register
FeeTooHighFee > MAX_FEE (500 bps)
AlreadyExitingrequestExit while already in cool-down
ExitNotRequestedexecuteExit before requestExit
CooldownNotPassedexecuteExit before EXIT_COOLDOWN elapsed
BondTransferFailedOperator EOA can’t receive ETH on exit
ZeroAddressRequired address arg is address(0)

Accountability — reputation, not slashing

The registry does not implement bond slashing, by design. Bonds are posted on registration and returned in full on exit (after the 7-day cool-down) — there is no slash() entry point and no governance hook to forfeit bond.

Misbehaving relayers are accountable through the planned DisputeRegistry: anyone with cryptographic evidence (e.g., a conflicting commit-reveal pair) submits it on-chain, the contract verifies and emits an event, and an off-chain reputation indexer aggregates events into a per-relayer rating that frontends display in the relayer picker. Users avoid low-rated relayers; lost fees do the economic work a slashed bond would do, without the dispute-game complexity slashing systems impose. See whitepaper §10.2 for the rationale.

SDK helpers

import { loadActiveRelayers, loadRelayersWithApiInfo, registerRelayer, updateRelayerInfo, addRelayerBond, requestRelayerExit, executeRelayerExit, loadOperatorRow, } from "@zkscatter/sdk/relayer";

See SDK reference / relayer for signatures.

Address by network

NetworkConfig.contracts.relayerRegistry. ABI ships as RELAYER_REGISTRY_ABI / RELAYER_REGISTRY_IFACE.

Last updated on