Polytraders Dev Guide
internal
v3 spine Phase 1 · Shared contracts 9 demo-wired · 0 shadow-ready · 0 production-live · 100 pending · 109 total 15/33 infra tasks the plan status board
HomeBy LayerSecurity5.3 SignaturePreviewer

5.3 SignaturePreviewer

Security Guardrail RejectPause PLANNED Spec started capital · Direct P5 · Execution rails pending stub

Render a plain-English summary of every EIP-712 signature before the wallet shows the modal.

v3 readiness

Docs27/27
donehow scored
Impl0/15
pendinghow scored
Backtest0/4
pendinghow scored
Runtime0/8
pendinghow scored

A bot is done when all four scores are. What does done mean?

1. Bot Identity

LayerSecurity  Security
Bot classGuardrail
AuthorityRejectPause
StatusPLANNED
ReadinessSpec started
Runs beforeWallet signing modal is shown to user
Runs afterStrategy OrderIntent and contract/permission checks
Applies toEvery EIP-712 signing request before user confirmation
Default modeshadow_only
User-visibleAdvanced details only
Developer ownerPolytraders core

Operational profile

Modes supportedquarantine

2. Purpose

Render a plain-English summary of every EIP-712 signature before the wallet shows the modal.

3. Why This Bot Matters

  • User signs a phishing or misrouted order without understanding it

    Funds transferred to an unintended address or contract under user signature.

  • Domain separator not validated before display

    A forged domain could cause a user to unknowingly sign for a different chain or contract.

  • Strategy deviates from declared envelope

    User signs an order outside the parameters they approved at strategy setup.

No worked examples on this bot yet. Worked examples are optional but strongly recommended — they turn an abstract failure mode into something a developer can verify in a fixture.

4. Required Polymarket Inputs

InputSourceRequired?Use
EIP-712 typed data for pending orderclob_authYesDecode and display the full typed data in plain English.
Market metadata (name, outcomes, expiry)gammaYesResolve token IDs to human-readable market names in the preview.

5. Required Internal Inputs

InputSourceRequired?Use
Strategy declared envelope (side, size, price range)StrategyConfigYesDiff the pending order against the declared envelope; flag deviations.
KillSwitch active flagKillSwitchYesBlock signing if kill switch is active.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
require_preview_for['all']Order fields deviate from strategy envelopeDomain separator mismatch or envelope breach > 20%List of order types that require a preview before signing.
block_on_envelope_mismatchTrueOrder deviates 10–20% from declared envelopeOrder deviates > 20% from declared envelopeBlock signing if the order exceeds the strategy's declared parameters.

7. Detailed Parameter Instructions

require_preview_for

What it means

List of order types that require a preview before signing.

Default

{ "require_preview_for": ["all"] }

Why this default matters

Default ['all'] ensures every signing request is previewed without exception.

Threshold logic

ConditionAction
order_type in require_preview_forGenerate preview; await user acknowledgement
domain separator mismatchREJECT — CONTRACT_GUARD_DOMAIN_MISMATCH

Developer check

if (p.require_preview_for.includes('all') || p.require_preview_for.includes(order.type)) generatePreview(order);

User-facing English

Please review this order before signing.

block_on_envelope_mismatch

What it means

Block signing if the order exceeds the strategy's declared parameters.

Default

{ "block_on_envelope_mismatch": true }

Why this default matters

Blocking on mismatch prevents a compromised strategy from signing orders the user did not intend.

Threshold logic

ConditionAction
deviation <= 10%APPROVE — no warning
deviation 10–20%WARN in preview
deviation > 20% AND block_on_envelope_mismatch=trueREJECT — SIGNATURE_ENVELOPE_BREACH

Developer check

if (p.block_on_envelope_mismatch && deviation > 0.20) return reject('SIGNATURE_ENVELOPE_BREACH');

User-facing English

This order is outside your strategy's declared parameters.

8. Default Configuration

{
  "bot_id": "sec.signature_previewer",
  "version": "0.1.0",
  "mode": "hard_guard",
  "defaults": {
    "require_preview_for": [
      "all"
    ],
    "min_detail_level": "standard",
    "block_on_envelope_mismatch": true,
    "retain_log_days": 30
  }
}

9. Implementation Flow

  1. Receive EIP-712 typed data for pending signing request.
  2. Check KillSwitch; if active, REJECT(KILL_SWITCH_ACTIVE).
  3. Verify domain separator version == '2' and verifyingContract in V2 allow-list.
  4. Resolve token IDs to market names via Gamma API.
  5. Compute diff between order fields and strategy declared envelope.
  6. If deviation > 20% and block_on_envelope_mismatch: REJECT(SIGNATURE_ENVELOPE_BREACH).
  7. Render plain-English preview: market name, side, size in pUSD, price, expiry, contract.
  8. Emit preview to wallet UI; await user acknowledgement.
  9. Emit RiskVote(APPROVE) after acknowledgement; log to audit trail.

10. Reference Implementation

Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. IF/THEN/ELSE = decision. Translate directly to TypeScript, Python, Go, or Rust.

// SignaturePreviewer
FUNCTION previewSignature(typedData, strategyEnvelope):
  IF FETCH(internal.killswitch).active:
    EMIT RiskVote(DENY, KILL_SWITCH_ACTIVE); RETURN
  // Domain check
  IF typedData.domain.version != '2':
    EMIT RiskVote(DENY, CONTRACT_GUARD_DOMAIN_MISMATCH); RETURN
  // Resolve market name
  meta = FETCH(gamma.markets[typedData.message.tokenId])
  marketName = meta ? meta.question : '(unresolved)'
  // Envelope diff
  deviation = ABS(typedData.message.size_usd - strategyEnvelope.size_usd)
               / strategyEnvelope.size_usd
  IF deviation > 0.20 AND params.block_on_envelope_mismatch:
    EMIT RiskVote(DENY, SIGNATURE_ENVELOPE_BREACH); RETURN
  // Render preview
  preview = {
    market: marketName,
    side: typedData.message.side,
    size_pusd: typedData.message.size_usd,
    price: typedData.message.price,
    contract: typedData.domain.verifyingContract
  }
  EMIT preview TO wallet_ui
  AWAIT user.acknowledge()
  EMIT RiskVote(APPROVE)
  LOG(governance.audit, {preview, decision: 'APPROVE'})

SDK calls used

  • gamma.get_market(token_id)
  • clob_auth.get_typed_data(intent_id)
  • internal.killswitch.status()

Complexity: O(1) per signing request — single Gamma API call

11. Wire Examples

Input — what arrives on the wire

EIP-712 typed data for BUY orderclob_auth

{
  "intent_id": "int_3c4d5e6f7a8b9c0d",
  "domain": {
    "name": "CTFExchange",
    "version": "2",
    "chainId": 137,
    "verifyingContract": "0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E"
  },
  "message": {
    "side": "BUY",
    "tokenId": "0x5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e",
    "size_usd": 400,
    "price": 0.55,
    "timestamp_ms": 1746768672000
  }
}

Output — what the bot emits

RiskVote — APPROVE with preview

{
  "vote_id": "sec.signature_previewer.20260509T140000Z",
  "decision": "APPROVE",
  "reason_code": null,
  "preview": {
    "market": "US Election — Winner",
    "side": "BUY",
    "size_pusd": 400,
    "price": 0.55
  },
  "checked_at": "2026-05-09T14:00:00Z"
}

12. Decision Logic

APPROVE

Domain valid, order within envelope, user has acknowledged preview.

RESHAPE_REQUIRED

Not applicable — previewer approves or rejects; it does not modify orders.

REJECT

Domain separator mismatch, envelope breach > 20%, or KillSwitch active.

WARNING_ONLY

Warn in preview when envelope deviation is 10–20%.

13. Standard Decision Output

This bot returns a RiskVote object. See RiskVote schema.

{
  "vote_id": "sec.signature_previewer.20260509T140000Z",
  "decision": "APPROVE",
  "reason_code": null,
  "evidence": {
    "market": "US Election \u2014 Winner",
    "side": "BUY",
    "size_pusd": 400,
    "price": 0.55,
    "domain_ok": true,
    "envelope_deviation_pct": 2.0,
    "user_acknowledged": true
  },
  "checked_at": "2026-05-09T14:00:00Z"
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
KILL_SWITCH_ACTIVEHARD_REJECTGlobal kill switch is active.Immediately return DENY.Trading is currently paused.
CONTRACT_GUARD_DOMAIN_MISMATCHHARD_REJECTEIP-712 domain version is not '2' or verifyingContract is not in V2 allow-list.Return DENY; emit security alert.The security parameters in this order do not match the current exchange.
SIGNATURE_ENVELOPE_BREACHHARD_REJECTOrder parameters deviate more than 20% from strategy declared envelope.Return DENY; display explanation to user.This order is outside your strategy's declared parameters.
SIGNATURE_ENVELOPE_WARNWARNOrder deviates 10–20% from envelope.Display warning in preview; allow user to proceed.This order is slightly outside your typical parameters. Review before signing.
MARKET_UNRESOLVEDINFOGamma API unavailable; market name could not be resolved.Show raw token ID with warning banner.Market name could not be loaded. Showing raw order details.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_sec_signaturepreviewer_decisions_totalcountercountdecisionTotal preview decisions.
polytraders_sec_signaturepreviewer_envelope_breaches_totalcountercountOrders rejected for envelope breach.
polytraders_sec_signaturepreviewer_domain_mismatches_totalcountercountDomain separator mismatches detected.
polytraders_sec_signaturepreviewer_preview_latency_mshistogrammsTime to render and display preview.

Alerts

AlertConditionSeverityRunbook
SignaturePreviewerDomainMismatchrate(polytraders_sec_signaturepreviewer_domain_mismatches_total[5m]) > 0P0#runbook-signaturepreviewer-domain
SignaturePreviewerEnvelopeBreachrate(polytraders_sec_signaturepreviewer_envelope_breaches_total[5m]) > 0P1#runbook-signaturepreviewer-envelope

16. Developer Reporting

{
  "bot_id": "sec.signature_previewer",
  "decision": "APPROVE",
  "inputs_used": [
    "eip712.typed_data",
    "gamma.market_metadata",
    "strategy.envelope"
  ],
  "checked_at": "2026-05-09T14:00:00Z"
}

17. Plain-English Reporting

SituationUser-facing explanation
Preview shown before signingYou are about to sign an order. Please review the details and confirm.
Order blocked — envelope breachThis order is outside the parameters you set for this strategy and has been blocked.
Order blocked — domain mismatchThe security parameters in this order do not match the current exchange. It has been blocked.

18. Failure-Mode Block

main_failure_modePreview not shown because Gamma API is unreachable, leaving user to sign a raw typed blob.
false_positive_riskLegitimate strategy update triggers envelope mismatch alert before envelope cache refreshes.
false_negative_riskMarket name resolves stale, showing an outdated description while order targets a different market.
safe_fallbackIf Gamma API is unreachable, display raw typed data with a warning that market names could not be resolved. Block if domain check also fails.
required_dependenciesGamma API for market metadata, StrategyConfig for envelope, KillSwitch

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
DOMAIN_VERSION_V1Set typedData.domain.version='1'Automatic on next order with correct domain.
ENVELOPE_BREACHSet order.size_usd to 5x strategy envelope maxStrategy must re-declare envelope or reduce order size.
GAMMA_API_DOWNBlock Gamma API endpointAutomatic when Gamma API recovers.

20. State & Persistence

Cold-start recovery

Preview log rebuilt from audit trail on restart.

21. Concurrency & Idempotency

AspectSpecification
Execution modelasync per signing request
Max in-flight50
Idempotency keyintent_id
Per-call timeout (ms)2000
Backpressure strategyqueue; reject if user does not acknowledge within timeout
Locking / mutual exclusionnone — each preview is independent

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchKillSwitch gate.DENY(KILL_SWITCH_ACTIVE) short-circuits.
sec.contract_address_guardDomain separator pre-validated.Contract guard runs before previewer in pipeline.

Emits to (downstream consumers)

BotWhyContract
gov.builder_attributionLog every signed preview and decision.GovernanceLog entry on each APPROVE or DENY.

Sibling bots (same OrderIntent)

External services

ServiceEndpointSLA assumedOn failure
Gamma APIhttps://gamma-api.polymarket.com99.9%Show raw typed data with warning; domain check still enforced.

23. Security Surfaces

Abuse vectors considered

  • Phishing payload with misleading market name in typed data
  • Forged domain separator to redirect signing to different chain

Mitigations

  • Market names resolved from trusted Gamma API, not from order payload
  • Domain separator validated against V2 allow-list before display

24. Polymarket V2 Compatibility

AspectValue
CLOB versionv2
Collateral assetpUSD
EIP-712 Exchange domain version2
Aware of builderCode fieldyes
Aware of negative-risk marketsyes
Multi-chain readyno
SDK usedpy-clob-client-v2
Settlement contractCTFExchangeV2
NotesValidates EIP-712 domain version '2' in every signing request; displays pUSD amounts and builder code in preview.

API surfaces declared

clob_authgammainternal

Networks supported

polygon

25. Versioning & Migration

FieldValue
spec2.0.0
implementation0.1.0
schema2
releasedNone
planned_releaseQ3-2026

Migration history

DateFromToReasonAction taken
2026-04-28n/av2-specSpec drafted post-CLOB-V2 cutover; bot not yet implementedDesigned against V2 schema (pUSD, builder codes, V2 EIP-712 domain)

26. Acceptance Tests

Unit Tests

TestSetupExpected result
Preview renders market name from Gamma APItyped_data with token_id=0xABC, Gamma returns 'US Election'Preview shows 'US Election'
Reject when domain version is '1'eip712_domain_version='1'DENY(CONTRACT_GUARD_DOMAIN_MISMATCH)
Reject when envelope deviation > 20%order.size_usd=2000, envelope.max_size_usd=1000DENY(SIGNATURE_ENVELOPE_BREACH)

Integration Tests

TestExpected result
Gamma API unreachable — raw typed data shown with warningPreview displayed with raw fields and warning banner; domain check still runs
User acknowledgement required before APPROVERiskVote APPROVE not emitted until user confirms

Property Tests

PropertyRequired behaviour
Domain separator mismatch always produces DENYAlways true
Every signing request generates at least a minimal previewAlways true

27. Operational Runbook

Domain mismatch and envelope breach alerts are P0/P1 security events requiring immediate investigation.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
SignaturePreviewerDomainMismatch
SignaturePreviewerEnvelopeBreach

Manual overrides

Healthcheck

GET /internal/health/signaturepreviewer → green if Gamma API reachable; last preview rendered within 30s.; red if Gamma API unreachable for > 60s or domain mismatch alert firing.

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

GateHow measuredThreshold
Preview renders correctly for BUY and SELL orders with known market IDsCI integration test100% pass

Promote to Limited live

GateHow measuredThreshold
Domain mismatch injection test fires DENY correctlyFailure injection testPass

Promote to General live

GateHow measuredThreshold
Zero false-positive envelope breaches in 48h shadowGrafana EnvelopeBreach alert history0 false positives

29. Developer Checklist

Ready-to-ship score: 27/27 sections complete · 100%

RequirementStatus
Purpose defined✓ done
Required inputs listed✓ done
Parameters defined✓ done
Defaults defined✓ done
Warning thresholds defined✓ done
Hard thresholds defined✓ done
Safe fallback defined✓ done
Structured output defined✓ done
Developer log defined✓ done
Plain-English explanation✓ done
Unit tests defined✓ done
Integration tests defined✓ done
Property tests defined✓ done
Failure-mode block complete✓ done
Reference implementation pseudocode✓ done
Wire examples (input + output)✓ done
Reason codes listed✓ done
Metrics & logs defined✓ done
State & persistence defined✓ done
Concurrency & idempotency defined✓ done
Dependencies declared✓ done
Security surfaces declared✓ done
Polymarket V2 compatibility declared✓ done
Version & migration history declared✓ done
Operational runbook defined✓ done
Promotion gates defined✓ done
Failure-injection recipes defined✓ done