--- title: SBET V2 Grader Operator Guide description: How to run a grader on SBET V2 — stake, infrastructure, 6-tier AI escalation, cost ceilings, slashing, and VRF-selected rotation. canonical: https://sbettoken.org/docs/protocol-v2/grader-operator-guide version: 2.0.0 updated: 2026-04-05 --- # Grader Operator Guide Graders are the decentralized oracle for SBET V2. You stake $25k SBET, run infrastructure that grades match outcomes, and earn a share of auto-claim fees and slash distributions. Grade honestly and you collect rewards indefinitely. Grade dishonestly and you lose your stake. This guide covers the full lifecycle: registration, infrastructure, the 6-tier AI escalation pipeline, and slashing scenarios. --- ## Economic model | Quantity | Amount | |----------|-------:| | Minimum stake | **$25,000 SBET** | | Panel size (N) | **13** | | Quorum (M) | **9** | | Deregister cooldown | **7 days** | | Max slash per match | Up to 100% of grader's stake | ### Revenue - **20% of every slash** flows to SBET stakers. If you stake a portion of your rewards, you earn additional yield via `SBETStakerRewards`. - Auto-claim fees: **2% SBET / 3.5% other tokens** — 20% of every auto-claim fee flows to stakers as well. ### Cost - **$25k SBET** — locked as registry stake. - **Infrastructure** — see [Infrastructure](#infrastructure) below. - **AI inference costs** — see [Cost ceilings](#cost-ceilings) below. ### Break-even The break-even point depends on the slash flow and auto-claim fee flow your panel share captures. At scale, a single grader running reliably for a year should expect to recoup AI costs and earn net-positive returns as long as slash events remain below ~5% of active graders per year (i.e. as long as most graders are honest). --- ## Staking ### Register Approve `GraderRegistryV2` to pull your stake, then call `register`: ```javascript await sbet.approve(graderRegistryAddress, parseEther("25000")); await graderRegistry.register(parseEther("25000")); ``` **Reverts** if `amount < stakeFloor (25_000e18)` → `InsufficientStake(provided, required)`. On success, `GraderStatus` becomes `Active` and `GraderRegistered` fires. ### Increase stake ```javascript await sbet.approve(graderRegistryAddress, parseEther("10000")); await graderRegistry.increaseStake(parseEther("10000")); ``` Stacks on top of your existing stake. If you were previously `Slashed`, topping up back to the floor reactivates you. ### Deregister ```javascript await graderRegistry.requestDeregister(); // wait 7 days await graderRegistry.executeDeregister(); ``` The 7-day cooldown prevents a grader from exiting immediately after a questionable grade to escape potential slashing. You **cannot submit grades** while in `Cooldown`. ### Check your status ```javascript const info = await graderRegistry.getGraderInfo(graderAddress); // GraderInfo { stake, registeredAt, cooldownEndsAt, status, slashedTotal, gradesSubmitted } ``` --- ## Infrastructure ### Minimum requirements | Component | Recommendation | |-----------|----------------| | CPU / RAM | 4 vCPU / 8GB (light — mostly I/O bound) | | Storage | 50GB SSD (for match evidence cache) | | Network | 99.9% uptime, <200ms latency to L2 RPC | | RPC | Dedicated L2 RPC (Base/Arbitrum) + fallback | | HSM / signer | **Hardware-backed signer** — grader key controls $25k stake | ### Software stack Your grader runs a continuous loop: 1. **Listen** for `MatchRegistered` events on `SBETCoreV2`. 2. **Watch panel membership** — verify `verifyPanelMembership(matchId, yourAddress, proof)` for each match. 3. **Wait for match end** — monitor the sport's scheduled end-time. 4. **Grade** — run the 6-tier escalation pipeline (see below). 5. **Submit** — call `submitGrade(matchId, outcome, confidenceBps)` with your result. 6. **Monitor proposals** — if `aggregateConfidence` crosses quorum, the aggregator posts `proposeOutcome`. Reference implementation: see `sbet-indexer/src/graders/` (internal Python reference; a Go implementation is planned for public release). ### Key management The grader key signs `submitGrade` transactions. It does **not** hold custody of user funds, but it can be slashed if misused. Operational recommendations: - Use an HSM or a hardware wallet reachable via signer proxy (Fireblocks, Turnkey, etc.). - Keep the key on a dedicated machine with no inbound network access. - Rotate the key via governance if compromise is suspected (flagged as open question — per-grader key rotation is not yet wired). --- ## 6-tier AI escalation [Decision #10] Grading is not a single AI call. It's a **progressive escalation pipeline** that climbs in cost and confidence until one tier yields a result you're willing to stake your $25k on. | # | Tier | Typical cost | When to use | |---|------|-------------:|-------------| | 1 | **Cheap AI** (GPT-4o-mini / Claude Haiku) | ~$0.001 | First pass on public match metadata | | 2 | **Mid AI** (GPT-4o / Claude Sonnet) | ~$0.01 | If tier 1 confidence < 80% | | 3 | **Expensive AI** (GPT-4.5 / Claude Opus) | ~$0.10 | If tier 2 confidence < 90% | | 4 | **Multi-source fetch** | ~$0.20 | Pull 3+ independent sources (ESPN API, official league API, etc.), cross-check | | 5 | **Re-grade** | ~$0.20 | If multi-source disagrees, re-run tier 3 with fresh context | | 6 | **Human review / dispute** | operator time | If still uncertain, abstain and let another grader propose — optionally open a dispute if someone else proposes badly | ### Per-tier confidence scoring Each tier emits `confidenceBps` (0..10_000). You submit your highest-confidence tier's result. ### Confidence thresholds that matter downstream These thresholds feed `ChallengeWindowLib` and determine the challenge window: - `confidenceBps >= 9_500` → eligible for **90s Fast tier** (if unanimous + source agreement) - `confidenceBps < 6_000` → forces **4h Contentious tier** regardless of other signals **Implication**: submitting artificially high confidence when you're uncertain is dangerous — if the aggregator lumps your vote in and the match is disputed, you're potentially slashable. ### Source agreement The `sourceAgreement` parameter passed to `proposeOutcome` comes from tier 4 of your pipeline. Values 0..3 = number of independent sources that agreed: - `0` — no external sources consulted - `1` — one source - `2` — two sources agree ← threshold for Fast tier - `3` — three sources agree Submit high `sourceAgreement` **only** when you independently verified via distinct data feeds (e.g. ESPN API + official league API + Sportradar). --- ## Cost ceilings A rational grader sets a **max spend per match** to keep margins positive: ``` max_spend_per_match = expected_revenue_per_match × safety_factor ``` Example budget for a panel of 13 graders assuming: - 100 matches/month - $100k average TVL per match - 1% of matches disputed - 0.5% of matches slashed - 20% stakers share on all auto-claim fees Per-grader monthly expected AI cost: **~$30–50** at full 6-tier escalation on ~5% of matches (the rest settle at tier 1 or 2). ### When to abstain **Do not submit a grade** if: - You can't reach quorum confidence (≥60%) even after tier 6 - The match has conflicting data feeds and you don't have independent verification - The match is paused at any tier (`pauseTierOf(matchId) != None`) Abstaining does not slash you. Submitting a wrong grade does. --- ## Slashing scenarios ### What gets you slashed 1. **Signing a proposal that the arbiter overrules** — if the 5-of-9 arbiter rules the proposed outcome wrong, the graders who signed it are candidates for slashing (wired via `DisputeManager.arbiterResolve` → `GraderRegistryV2.slashWithChallenger`). 2. **Submitting outside your panel** — currently prevented off-chain by the aggregator, but future versions may enforce on-chain via Merkle proof in `submitGrade`. 3. **Governance-level violations** — e.g. manipulating confidence scores to mint false Fast-tier finalizations (caught post-hoc by the challenger system). ### What does NOT get you slashed - Abstaining from a match (not submitting a grade) - Submitting a grade that ends up in a successful dispute **as a dissenter** (dissenters are not slashed; only signers of the losing proposal are) - Deregistering after a grade you feel unsure about (7-day cooldown provides the window) ### Slash distribution (you are the victim) If you're slashed $25k: | Recipient | Amount | |-----------|-------:| | Challenger | $12,500 (50%) | | Treasury retained | $5,000 (20%) | | SBET stakers | $5,000 (20%) | | Community bounty | $10,000 (10%) | | **Total** | **$25,000** | No burn. Your stake goes to your counterparties. ### How to recover after a slash If your stake falls below the floor, your status becomes `Slashed` and you cannot submit grades. To reactivate: ```javascript await sbet.approve(graderRegistryAddress, parseEther("25000")); await graderRegistry.increaseStake(parseEther("25000")); // status automatically transitions back to Active ``` --- ## VRF panel rotation [Decision #12] You are not on every match. For each match, **13 graders are randomly drawn** from the active pool using Chainlink VRF v2.5. Quorum is 9. ### How to know you're on a panel When a match is registered, `MatchRegistered` is emitted with a `panelRoot`. The aggregator publishes the full panel list off-chain. Your grader checks: ```javascript const proof = /* Merkle proof from off-chain index */; const onPanel = await sbetCore.verifyPanelMembership(matchId, graderAddress, proof); ``` If `onPanel` is `true`, you should grade this match. If `false`, skip it. ### Drop-out tolerance Panel size 13, quorum 9 → the panel tolerates **4 dropouts**. If you're briefly offline, the remaining graders can still reach quorum. If 5 or more panel members go offline, the match cannot be proposed and may eventually be voided. ### VRF fulfillment delays If VRF is slow, `seedFromBlockhashFallback` is available after 24h. Your panel membership is still determined by the sealed `panelRoot`, but the draw used `blockhash(matchCreationBlock)` instead of VRF. You should still grade these matches; flag them for extra scrutiny in your monitoring. --- ## Submitting a grade ### Canonical submission ```javascript await graderRegistry.submitGrade( matchId, outcome, // uint8 confidenceBps // uint16, 0..10_000 ); ``` **Reverts**: - `GraderNotActive(you)` — you're in Cooldown, Slashed, or Inactive - `InvalidConfidence(bps)` — `confidenceBps > 10_000` - `AlreadySubmittedGrade(matchId, you)` — you already graded this match Each grader may submit **at most once per matchId**. ### Batching There is no on-chain batch entry today. Operators can bundle multiple `submitGrade` calls using a multi-call wrapper off-chain, but each submission is an independent on-chain transaction. ### Watching the aggregator The off-chain aggregator reads `aggregateConfidence(matchId)` to decide when to call `proposeOutcome`: ```javascript const [avgConfidenceBps, dissents, signers] = await graderRegistry.aggregateConfidence(matchId); ``` `signers` is the total grader count; `dissents` is the number of signers whose outcome disagrees with the modal outcome. The aggregator waits until `signers >= 9` before proposing. --- ## Operational checklist Before going live: - [ ] SBET in wallet ≥ $25k + gas buffer - [ ] HSM / hardware signer configured - [ ] Dedicated L2 RPC + fallback RPC - [ ] Event subscription to `MatchRegistered`, `VRFSeedFulfilled`, `OutcomeProposed`, `MatchFinalized`, `GraderSlashed` - [ ] 6-tier AI pipeline tested on historical matches - [ ] Cost budget set per match (cap tier 6 escalation) - [ ] Monitoring dashboard for your own `gradesSubmitted`, `slashedTotal` - [ ] Alerting on pause events (`MatchLocked`, `CategoryPauseActivated`, `GlobalPauseActivated`) - [ ] Runbook for abstain-and-investigate when confidence < 60% --- ## Common failure modes | Symptom | Cause | Fix | |---------|-------|-----| | `submitGrade` reverts with `GraderNotActive` | Stake fell below floor after slashing | Call `increaseStake` | | Grade never propagates to `OutcomeProposed` | Quorum not reached (fewer than 9 signers) | Check panel liveness — other graders offline | | Your grade contradicts the modal outcome | You're a dissenter | Not slashed as a dissenter; may be slashed if the modal outcome is ruled wrong | | Merkle proof verification fails off-chain | Wrong `panelRoot` lookup | Rebuild proof from the `MatchRegistered` event's `panelRoot` | | Missed matches where you were on panel | Event subscription stale | Resubscribe; audit missed matches for potential reputation damage | --- ## Getting help - **Grader discord channel** — invite-only, one per active grader - **On-chain**: watch `GraderRegistryV2` for role grants and `GraderStakeFloorUpdated` events - **Emergency pause**: if you observe suspicious activity, contact a `CATEGORY_GUARDIAN_ROLE` holder to trigger a 3-of-5 category pause, or a `GLOBAL_GUARDIAN_ROLE` holder for 5-of-9 global pause --- ## Next - [Security model](./security-model.md) — the economic story behind the $25k stake - [Contracts reference: GraderRegistryV2](./contracts-reference.md#graderregistryv2) — full function signatures - [State machine](./state-machine.md) — where your grade fits into the match lifecycle