Sanity-check that the source listed in a market's rule actually publishes what's needed for resolution.
3. Why This Bot Matters
Cited source does not actually publish what's needed
A market that cites 'team-website.com' as its source-of-truth resolves on whatever that site publishes. If the site stops publishing, publishes only behind a paywall, or publishes too coarsely (winner only, no margin), the market cannot be resolved cleanly. Catching that at listing prevents the trade.
Rule change to a worse source goes undetected
A post-listing edit can swap the cited source for a less reliable one. Pairing this verifier with RuleChangeMonitor catches the moment the cited source becomes unsuitable.
Operators have no defensible exclusion reason
MarketScanner needs a structured reason to exclude a market with a broken source-of-truth. The verifier produces that reason in a form Discovery and Governance can both consume.
4. Required Polymarket Inputs
Input
Source
Required?
Use
Market status and metadata from Gamma API
gamma
Yes
First source in three-way verification.
Market status and metadata from Data API
data
Yes
Second source in three-way verification.
On-chain condition state from CTFExchangeV2
onchain
Yes
Third source — authoritative on-chain resolution status.
5. Required Internal Inputs
Input
Source
Required?
Use
KillSwitch active flag
KillSwitch
Yes
Suppress all emissions when KillSwitch is active.
6. Parameter Guide
Parameter
Default
Warning
Hard
What it controls
poll_interval_s
120
600
1800
Seconds between three-source verification cycles per active market.
staleness_threshold_s
300
600
3600
Maximum age of any source response before verification is considered stale.
7. Detailed Parameter Instructions
poll_interval_s
What it means
Seconds between three-source verification cycles per active market.
Default
{ "poll_interval_s": 120 }
Why this default matters
120 s provides timely detection of source divergence without overloading Gamma, Data, and RPC endpoints.
Threshold logic
Condition
Action
interval <= 120 s
Normal
120–600 s
WARN — reduced divergence detection speed
> 1800 s
Reject — PARAMETER_CHANGE_REQUIRES_APPROVAL
Developer check
if (p.poll_interval_s > p.hard) throw ConfigError('PARAMETER_CHANGE_REQUIRES_APPROVAL');
User-facing English
Market state is verified across multiple sources regularly to catch inconsistencies.
staleness_threshold_s
What it means
Maximum age of any source response before verification is considered stale.
Default
{ "staleness_threshold_s": 300 }
Why this default matters
300 s ensures all three sources are fresh before comparison.
Threshold logic
Condition
Action
age <= 300 s
Normal
300–600 s
WARN — approaching stale threshold
> 3600 s
Reject — emit STALE_DATA and halt verification
Developer check
if (source_age_s > p.staleness_threshold_s.hard) emit('STALE_DATA');
User-facing English
Source data must be fresh for verification to be reliable.
Before entering a position, the system checks that all data sources agree on the market's current state. This ensures positions are not opened on inconsistent data.
Mismatch detected between on-chain state and API
The on-chain market state differs from the API. This is usually a temporary propagation delay. The system paused new entries until sources agree.
18. Failure-Mode Block
main_failure_mode
On-chain RPC outage during a market resolution event causes the verifier to miss the chain-resolved status, potentially leaving strategies in positions on an already-resolved market.
false_positive_risk
Propagation lag between on-chain resolution and Gamma/Data API updates causes a transient CHAIN_API_MISMATCH that resolves within minutes.
false_negative_risk
All three sources share the same cached CDN layer during a large-scale outage, causing the verifier to report agreement when all three are returning stale data.
safe_fallback
If any source is unavailable for > staleness_threshold_s, emit STALE_DATA WARN and suppress new ObservationReports until all three sources return fresh data.
required_dependencies
Polymarket Gamma API, Polymarket Data API, Polygon RPC, KillSwitch active flag, Postgres for verification audit log
19. Failure-Injection Recipes
Scenario
How to inject
Expected behaviour
Recovery
GAMMA_API_DOWN
Block Gamma API for 10 min
Automatic on Gamma recovery; re-verify all active markets
CHAIN_API_MISMATCH
Inject resolved=true in mock chain state while Data API shows resolved=false
Automatic once both sources agree
KILL_SWITCH_ON
Set killswitch.active=true during active verification polling
Emissions resume on KillSwitch reset
20. State & Persistence
Cold-start recovery
On cold start, reload from Postgres; re-verify all active markets on first cycle.
21. Concurrency & Idempotency
Aspect
Specification
Execution model
async per-market verification triggered by strategy entry or periodic poll
Max in-flight
15
Idempotency key
condition_id + verification_cycle_id
Per-call timeout (ms)
15000
Backpressure strategy
drop-after-buffer — max 50 pending verifications queued
Locking / mutual exclusion
Postgres advisory lock on condition_id during verification update
Attacker causes deliberate Gamma/chain mismatch to trigger strategy pauses via alert flooding
Stale on-chain state during fast resolution causes spurious CHAIN_API_MISMATCH alerts
Mitigations
Alert thresholds require multiple mismatches before paging to reduce noise
ObservationReports informational only — strategies verify independently before pausing
24. Polymarket V2 Compatibility
Aspect
Value
CLOB version
v2
Collateral asset
pUSD
EIP-712 Exchange domain version
2
Aware of builderCode field
no
Aware of negative-risk markets
yes
Multi-chain ready
no
SDK used
py-clob-client-v2
Settlement contract
CTFExchangeV2
Notes
Cross-validates Gamma metadata against on-chain CTFExchangeV2 state and data API. All pUSD-denominated. No order signing.
API surfaces declared
gammadataonchaininternal
Networks supported
polygon
25. Versioning & Migration
Field
Value
spec
2.0.0
implementation
0.1.0
schema
2
released
None
planned_release
Q3-2026
Migration history
Date
From
To
Reason
Action taken
2026-04-28
n/a
v2-spec
Spec drafted post-CLOB-V2 cutover; bot not yet implemented
Designed against V2 schema (pUSD, builder codes, V2 EIP-712 domain)
26. Acceptance Tests
Unit Tests
Test
Setup
Expected result
All three sources agree: verified=true ObservationReport emitted
Gamma, Data, and chain all return consistent resolved=false for active market
ObservationReport emitted with verified=true, discrepancies=[]
Chain/API mismatch detected and reported
Chain resolved=true; Data API resolved=false
ObservationReport emitted with verified=false, discrepancies=['chain_api_mismatch']
KillSwitch suppresses emission
killswitch.active=true; all sources available
No ObservationReport; KILL_SWITCH_ACTIVE logged
Integration Tests
Test
Expected result
Full lifecycle: verification triggers on strategy entry request; result consumed by strategy
Strategy receives verified=true ObservationReport before entry
Gamma API down: STALE_DATA emitted; verification halted
STALE_DATA WARN; no ObservationReports until Gamma recovers
Property Tests
Property
Required behaviour
SourceOfTruthVerifier never submits or signs orders
Always true
No ObservationReport emitted when KillSwitch is active
Always true
27. Operational Runbook
SourceOfTruthVerifier incidents are most critical when chain/API mismatch is detected — this may indicate a resolution propagation delay. Page immediately on chain mismatch.
On-call actions
Alert
First step
Diagnosis
Mitigation
Escalate to
SourceOfTruthChainAPIMismatch
Check on-chain condition state via Polygon explorer. Compare with Data API response. If chain shows resolved, notify strategy team immediately.
Intelligence pod lead immediately; escalate to market operations if > 5 min
SourceOfTruthGammaDataMismatch
Check last_updated_at for both Gamma and Data API responses. If one is stale, wait for propagation.
Intelligence pod lead if mismatch persists > 10 min
Manual overrides
force_reverify_all — POST /internal/sourceoftruthverifier/reverify-all to force fresh three-source check on all active markets — After known Gamma or Data API outage recovery
Healthcheck
Endpoint: /internal/health/sourceoftruthverifier | Green: Last verification < 5 min ago AND Postgres reachable AND Gamma + RPC returning 200 | Red: No verification for > 15 min OR Postgres unreachable OR chain/API mismatch unresolved > 10 min
28. Promotion Gates
A bot does not advance to the next readiness state until every gate below is green. Gates are observable from production data — no subjective sign-off.
Promote to Shadow
Gate
How measured
Threshold
Unit tests pass for all three discrepancy types and KillSwitch suppression
CI test run
100% pass
Promote to Limited live
Gate
How measured
Threshold
Verification latency p99 < 3 s over 24 h on staging