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 LayerRisk1.8 ComplianceGate

1.8 ComplianceGate

Risk Guardrail RejectReshape LIVE General live capital · Critical P4 · Core risk pending stub

ComplianceGate enforces Polymarket's terms-of-service access policy on every OrderIntent before it reaches execution. It validates (1) that the originating wallet has completed Polymarket onboarding, (2) that the user's jurisdiction is not on the blocked list, (3) that the wallet address does not appear on OFAC or other configured sanctions lists, and (4) that the target market is eligible for trading (not restricted by category policy). When any check fails the order is rejected outright; for geopolitical markets the bot applies NegRisk-aware category rules before passing. ComplianceGate is fail-closed: any inability to verify policy results in a hard reject.

v3 readiness

Docs27/27
donehow scored
Impl11/15
in progresshow scored
Backtest3/4
in progresshow scored
Runtime0/8
pendinghow scored

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

v3.5 · wired registry id risk.compliancegate

Validates builderCode length, signer authority. SEARCH_SPACE declared. Fixture pack pending.

Source: @polytraders/bots · src/risk/compliancegate.js · Impl 11/15 · Backtest 3/4

1. Bot Identity

LayerRisk  Risk
Bot classGuardrail
AuthorityRejectReshape
StatusLIVE
ReadinessGeneral live
Runs beforeExecutionPlan emit
Runs afterStrategy OrderIntent
Applies toEvery OrderIntent — checks the originating user wallet and target market for jurisdiction, KYC, sanctions, and market-eligibility policy
Default modegeneral_live
User-visiblesummary-only
Developer ownerPolytraders core — Risk pod

Operational profile

Modes supportedquarantine

2. Purpose

ComplianceGate enforces Polymarket's terms-of-service access policy on every OrderIntent before it reaches execution. It validates (1) that the originating wallet has completed Polymarket onboarding, (2) that the user's jurisdiction is not on the blocked list, (3) that the wallet address does not appear on OFAC or other configured sanctions lists, and (4) that the target market is eligible for trading (not restricted by category policy). When any check fails the order is rejected outright; for geopolitical markets the bot applies NegRisk-aware category rules before passing. ComplianceGate is fail-closed: any inability to verify policy results in a hard reject.

3. Why This Bot Matters

  • Sanctioned-address order submitted

    Allowing a wallet on an OFAC or other sanctions list to trade exposes the operator to regulatory and legal liability, and may result in asset freezing or platform shutdown.

  • Blocked-jurisdiction user bypasses geo check

    Trading from a restricted jurisdiction violates Polymarket's terms and applicable law; unchecked this can lead to regulatory action against the platform.

  • Non-onboarded wallet places order

    Polymarket's KYC/AML process has not been completed; the user has not accepted terms and the platform has no identity verification for the account.

  • Market category restriction not enforced

    Certain market categories (e.g. restricted financial instruments, region-specific political events) may not be available to all users; ignoring category eligibility creates unequal treatment and compliance gaps.

  • Fail-open on policy-service outage

    If compliance checks succeed silently during a data outage, ineligible users can trade undetected. ComplianceGate must fail-closed to prevent this.

4. Required Polymarket Inputs

InputSourceRequired?Use
Gamma API market metadata — negRisk flag and categorygammaYesDetermine whether the market is a NegRisk category (e.g. geopolitical) and whether category-level trading restrictions apply.
Polymarket onboarding status for the linked walletinternalYesConfirm the wallet has completed Polymarket KYC/AML onboarding before any order is allowed.

5. Required Internal Inputs

InputSourceRequired?Use
User jurisdiction metadata (from account profile or IP-geolocation)internalYesCheck the user's detected jurisdiction against the blocked_jurisdictions list.
Sanctions list snapshot (OFAC SDN + configured providers)internalYesScreen the originating wallet address against current sanctions lists; hard-reject on any match.
KillSwitch active flagKillSwitchYesIf KillSwitch is active, reject all orders immediately without running policy checks.
Market eligibility override mapAdmin UINoAllow compliance team to whitelist or blacklist specific markets outside of automated category rules.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
require_polymarket_onboardedTrueNoneTrueWhether the originating wallet must have completed Polymarket's onboarding flow before any order is permitted.
blocked_jurisdictions['US', 'GB', 'IR', 'KP', 'SY', 'CU']['US', 'GB', 'IR', 'KP', 'SY', 'CU', 'UA']['US', 'GB', 'IR', 'KP', 'SY', 'CU']ISO-3166 country codes that are prohibited from trading on Polymarket per platform policy. Warning threshold: list narrower than the recommended 7-country minimum triggers a WARN annotation.
sanctions_list_sourceOFAC_SDNNoneOFAC_SDNWhich sanctions screening provider(s) to use. Options: OFAC_SDN, CHAINALYSIS, ELLIPTIC, COMBINED.
close_only_on_violationFalseNoneFalseIf true, a user who fails a non-sanctions policy check (e.g. jurisdiction change post-onboarding) may still submit close/reduce-only orders to exit existing positions rather than being fully hard-rejected.

7. Detailed Parameter Instructions

require_polymarket_onboarded

What it means

Whether the originating wallet must have completed Polymarket's onboarding flow before any order is permitted.

Default

{ "require_polymarket_onboarded": true }

Why this default matters

Polymarket's terms require onboarding acceptance before trading. Disabling this check would allow unauthenticated wallets to trade, violating ToS and AML obligations.

Threshold logic

ConditionAction
onboarded = trueAPPROVE (this check)
onboarded = falseREJECT — COMPLIANCE_GATE_NOT_ONBOARDED

Developer check

if (require_polymarket_onboarded && !user.polymarket_onboarded) return reject('COMPLIANCE_GATE_NOT_ONBOARDED');

User-facing English

Your account must complete Polymarket onboarding before placing orders.

blocked_jurisdictions

What it means

ISO-3166 country codes that are prohibited from trading on Polymarket per platform policy. Warning threshold: list narrower than the recommended 7-country minimum triggers a WARN annotation.

Default

{ "blocked_jurisdictions": ["US", "GB", "IR", "KP", "SY", "CU"] }

Why this default matters

Polymarket does not permit trading from these jurisdictions due to regulatory restrictions. The list is reviewed by the compliance team; hardcoded minimum set cannot be removed via config.

Threshold logic

ConditionAction
configured list has >= 7 entriesNo warning — list meets minimum recommended coverage
configured list has 4–6 entriesWARN — COMPLIANCE_GATE_JURISDICTION_LIST_NARROW: list is below recommended minimum
user jurisdiction not in blocked_jurisdictionsAPPROVE (this check)
user jurisdiction in blocked_jurisdictionsREJECT — COMPLIANCE_GATE_JURISDICTION_BLOCKED

Developer check

if (blocked_jurisdictions.includes(user.country_code)) return reject('COMPLIANCE_GATE_JURISDICTION_BLOCKED');

User-facing English

Trading is not available in your region due to regulatory restrictions.

sanctions_list_source

What it means

Which sanctions screening provider(s) to use. Options: OFAC_SDN, CHAINALYSIS, ELLIPTIC, COMBINED.

Default

{ "sanctions_list_source": "OFAC_SDN" }

Why this default matters

OFAC SDN is the minimum legal requirement for US-linked operators. Additional providers increase coverage.

Threshold logic

ConditionAction
wallet not on any screened listAPPROVE (this check)
wallet matches any entryREJECT — COMPLIANCE_GATE_SANCTIONS_HIT

Developer check

const hit = sanctionsScreener.check(intent.wallet, params.sanctions_list_source); if (hit) return reject('COMPLIANCE_GATE_SANCTIONS_HIT');

User-facing English

This wallet cannot be used for trading on this platform.

close_only_on_violation

What it means

If true, a user who fails a non-sanctions policy check (e.g. jurisdiction change post-onboarding) may still submit close/reduce-only orders to exit existing positions rather than being fully hard-rejected.

Default

{ "close_only_on_violation": false }

Why this default matters

Defaulting to false (full reject) is the conservative compliance posture. Enable only with legal review, as it may be required to allow position exits in some jurisdictions.

Threshold logic

ConditionAction
close_only_on_violation = false AND check failsREJECT — COMPLIANCE_GATE_JURISDICTION_BLOCKED
close_only_on_violation = true AND check fails AND order is close/reduceRESHAPE — set constraints.close_only = true

Developer check

if (policyFailed && params.close_only_on_violation && isCloseOrder(intent)) return reshape({ close_only: true });

User-facing English

You may only close existing positions in this market.

8. Default Configuration

{
  "bot_id": "risk.compliance_gate",
  "version": "2.0.0",
  "mode": "hard_guard",
  "defaults": {
    "require_polymarket_onboarded": true,
    "blocked_jurisdictions": [
      "US",
      "GB",
      "IR",
      "KP",
      "SY",
      "CU"
    ],
    "sanctions_list_source": "OFAC_SDN",
    "close_only_on_violation": false
  },
  "locked": {
    "require_polymarket_onboarded": {
      "min": true
    },
    "sanctions_list_source": {
      "allowed": [
        "OFAC_SDN",
        "CHAINALYSIS",
        "ELLIPTIC",
        "COMBINED"
      ]
    }
  }
}

9. Implementation Flow

  1. Receive OrderIntent from Strategy layer including market_id, wallet address, and user metadata.
  2. Check KillSwitch active flag; if active, return REJECT with KILL_SWITCH_ACTIVE immediately.
  3. Screen the originating wallet address against the configured sanctions list (OFAC SDN + additional providers). On any match, return HARD_REJECT with COMPLIANCE_GATE_SANCTIONS_HIT — this check cannot be bypassed by close_only_on_violation.
  4. Check user jurisdiction against blocked_jurisdictions. If blocked and close_only_on_violation=false, return HARD_REJECT with COMPLIANCE_GATE_JURISDICTION_BLOCKED. If blocked and close_only_on_violation=true and this is a close/reduce order, return RESHAPE with constraints.close_only=true.
  5. If require_polymarket_onboarded=true, verify the wallet's onboarding status from the internal onboarding cache. If not onboarded, return HARD_REJECT with COMPLIANCE_GATE_NOT_ONBOARDED.
  6. Fetch target market metadata from Gamma API to determine category and negRisk flag. If the market category is restricted for this user (e.g. geopolitical events in certain jurisdictions), return HARD_REJECT with COMPLIANCE_GATE_MARKET_INELIGIBLE.
  7. Check the admin-configurable market eligibility override map. If market is explicitly blacklisted, return HARD_REJECT with COMPLIANCE_GATE_MARKET_INELIGIBLE.
  8. All checks passed — return APPROVE with inputs_used list and checked_at timestamp.

10. Reference Implementation

Runs four sequential policy gates — KillSwitch, sanctions screening, jurisdiction check, and market eligibility — before emitting a RiskVote. All gates are fail-closed: any data-source error produces HARD_REJECT.

Pseudocode is language-agnostic. FETCH = read input. EMIT = produce output. Translate to TS/Python/Go/Rust.

FUNCTION evaluateCompliance(intent):
  // --- 0. KillSwitch gate ---
  ks = FETCH internal.killswitch.status
  IF ks.active:
    EMIT RiskVote(decision=HARD_REJECT, reason=KILL_SWITCH_ACTIVE)
    RETURN

  // --- 1. Sanctions screening ---
  sanctionsResult = FETCH internal.sanctions.check(intent.wallet, params.sanctions_list_source)
  IF sanctionsResult IS NULL:
    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_DATA_UNAVAILABLE)
    RETURN
  IF sanctionsResult.hit:
    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_SANCTIONS_HIT)
    RETURN

  // --- 2. Jurisdiction check ---
  user = FETCH internal.user.profile(intent.user_id)
  IF user IS NULL:
    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_DATA_UNAVAILABLE)
    RETURN
  IF params.blocked_jurisdictions.contains(user.country_code):
    IF params.close_only_on_violation AND isCloseOrder(intent):
      EMIT RiskVote(decision=RESHAPE_REQUIRED,
                    reason=COMPLIANCE_GATE_JURISDICTION_CLOSE_ONLY,
                    constraints={close_only: true})
    ELSE:
      EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_JURISDICTION_BLOCKED)
    RETURN

  // --- 3. Onboarding check ---
  IF params.require_polymarket_onboarded:
    onboarding = FETCH internal.onboarding.status(intent.wallet)
    IF onboarding IS NULL:
      EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_DATA_UNAVAILABLE)
      RETURN
    IF NOT onboarding.completed:
      EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_NOT_ONBOARDED)
      RETURN

  // --- 4. Market eligibility check ---
  market = FETCH gamma.getMarketByConditionId(intent.market_id)
  IF market IS NULL:
    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_DATA_UNAVAILABLE)
    RETURN
  override = FETCH internal.market_eligibility_overrides.get(intent.market_id)
  IF override == 'BLOCKED':
    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_MARKET_INELIGIBLE)
    RETURN
  IF isCategoryRestrictedForUser(market.category, market.negRisk, user):
    EMIT RiskVote(decision=HARD_REJECT, reason=COMPLIANCE_GATE_MARKET_INELIGIBLE)
    RETURN

  // --- 5. All checks passed ---
  EMIT RiskVote(decision=APPROVE,
                inputs_used=['internal.sanctions', 'internal.user.profile',
                             'internal.onboarding', 'gamma.market'],
                checked_at=now_iso())

SDK calls used

  • gamma.getMarketByConditionId(market_id)
  • internal.sanctions.check(wallet, provider)
  • internal.onboarding.status(wallet)
  • internal.user.profile(user_id)
  • internal.market_eligibility_overrides.get(market_id)
  • internal.killswitch.status()

Complexity: O(1) — constant number of policy checks per intent, each backed by in-memory cache with periodic refresh

11. Wire Examples

Input — what arrives on the wire

OrderIntent from strategy — user in blocked jurisdictioninternal

{
  "intent_id": "int_c1d2e3f4a5b6c7d8",
  "market_id": "0x7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a",
  "side": "BUY",
  "outcome": "YES",
  "size_usd": 500,
  "price": 0.55,
  "wallet": "0xDeAdBeEf1234567890AbCdEf1234567890AbCdEf",
  "user_id": "usr_9a8b7c6d",
  "generated_at_ms": 1746780000000
}

User profile snapshotinternal

{
  "user_id": "usr_9a8b7c6d",
  "country_code": "GB",
  "polymarket_onboarded": true,
  "profile_fetched_at_ms": 1746779950000
}

Output — what the bot emits

RiskVote — HARD_REJECT (jurisdiction blocked)

{
  "guard_id": "risk.compliance_gate",
  "decision": "HARD_REJECT",
  "severity": "HARD",
  "reason_code": "COMPLIANCE_GATE_JURISDICTION_BLOCKED",
  "message": "Wallet jurisdiction GB is on the blocked list. Order rejected per platform compliance policy.",
  "constraints": {},
  "inputs_used": [
    "internal.sanctions.OFAC_SDN",
    "internal.user.profile",
    "internal.onboarding.status",
    "gamma.market.category"
  ],
  "checked_at": "2026-05-09T10:22:01Z"
}

RiskVote — APPROVE (all checks pass)

{
  "guard_id": "risk.compliance_gate",
  "decision": "APPROVE",
  "severity": "INFO",
  "reason_code": "COMPLIANCE_GATE_PASS",
  "message": "All compliance checks passed for wallet 0xAb12...Cd34.",
  "constraints": {},
  "inputs_used": [
    "internal.sanctions.OFAC_SDN",
    "internal.user.profile",
    "internal.onboarding.status",
    "gamma.market.category"
  ],
  "checked_at": "2026-05-09T10:22:01Z"
}

12. Decision Logic

APPROVE

Wallet is not sanctioned, jurisdiction is not blocked, user is onboarded (if required), and the target market is eligible for this user's category and jurisdiction profile.

RESHAPE_REQUIRED

User fails a non-sanctions policy check (e.g. jurisdiction change) but close_only_on_violation=true — emit constraints.close_only=true to allow position exit only.

REJECT

Wallet matches a sanctions list entry, user jurisdiction is in blocked_jurisdictions (with close_only disabled), user is not onboarded, market category is restricted, KillSwitch is active, or any compliance data source is unavailable (fail-closed).

WARNING_ONLY

Not used — ComplianceGate has reject authority and is fail-closed. No compliance check result is advisory-only.

13. Standard Decision Output

This bot returns a RiskVote object. See RiskVote schema.

{
  "guard_id": "risk.compliance_gate",
  "decision": "HARD_REJECT",
  "severity": "HARD",
  "reason_code": "COMPLIANCE_GATE_JURISDICTION_BLOCKED",
  "message": "Wallet jurisdiction GB is on the blocked list. Order rejected.",
  "constraints": {},
  "inputs_used": [
    "internal.user.jurisdiction",
    "internal.sanctions.OFAC_SDN",
    "internal.onboarding.status",
    "gamma.market.category"
  ],
  "checked_at": "2026-05-09T10:22:00Z"
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
KILL_SWITCH_ACTIVEHARD_REJECTGlobal kill switch is active; no orders may proceed.Immediately return HARD_REJECT without running any compliance check.Trading is currently paused. Please try again later.
COMPLIANCE_GATE_SANCTIONS_HITHARD_REJECTThe originating wallet address matched an entry in the configured sanctions list (e.g. OFAC SDN).Return HARD_REJECT; do not expose which list triggered the match in user-facing messaging.This wallet cannot be used for trading on this platform.
COMPLIANCE_GATE_JURISDICTION_BLOCKEDHARD_REJECTThe user's detected jurisdiction is on the blocked_jurisdictions list and close_only_on_violation is false.Return HARD_REJECT; log the country_code for compliance reporting.Trading is not available in your region due to regulatory restrictions.
COMPLIANCE_GATE_JURISDICTION_CLOSE_ONLYRESHAPEThe user's jurisdiction is blocked but close_only_on_violation=true and this is a close/reduce order.Return RESHAPE_REQUIRED with constraints.close_only=true.You may only close existing positions in this market from your current region.
COMPLIANCE_GATE_NOT_ONBOARDEDHARD_REJECTThe wallet has not completed Polymarket's required onboarding/KYC flow.Return HARD_REJECT; direct user to complete onboarding.Your account must complete Polymarket onboarding before placing orders.
COMPLIANCE_GATE_MARKET_INELIGIBLEHARD_REJECTThe target market is blocked by category policy, admin override, or a NegRisk geopolitical restriction for this user.Return HARD_REJECT; log market_id and category for audit.This market is not available for trading in your region or account profile.
COMPLIANCE_GATE_DATA_UNAVAILABLEHARD_REJECTOne or more compliance data sources (sanctions API, onboarding cache, Gamma market metadata, user profile) returned an error or were unreachable.Return HARD_REJECT (fail-closed). Log which data source failed and alert on-call if sustained.We could not verify your eligibility at this time. Please try again shortly.
COMPLIANCE_GATE_PASSINFOAll compliance checks passed for this wallet and market.Emit APPROVE and continue to next guardrail.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_risk_compliancegate_decisions_totalcountercountdecision, reason_codeTotal RiskVote decisions emitted by ComplianceGate, broken down by decision type and reason code. Monitors block rate per policy dimension.
polytraders_risk_compliancegate_sanctions_checks_totalcountercountprovider, resultNumber of sanctions screenings performed, broken down by provider and result (hit / clean / error).
polytraders_risk_compliancegate_jurisdiction_blocks_totalcountercountcountry_codeNumber of hard-rejects due to blocked jurisdiction, by country code. Used for compliance reporting.
polytraders_risk_compliancegate_data_source_errors_totalcountercountsourceNumber of fail-closed rejections caused by unavailable compliance data, by source name. Triggers alert when non-zero.
polytraders_risk_compliancegate_eval_latency_mshistogrammillisecondsWall-clock time from OrderIntent receipt to RiskVote emit. P99 target < 50ms (cache-backed checks).
polytraders_risk_compliancegate_sanctions_cache_age_secondsgaugesecondsproviderAge of the cached sanctions list snapshot. Alerts if stale beyond configured refresh interval.

Alerts

AlertConditionSeverityRunbook
ComplianceGateDataSourceErrorrate(polytraders_risk_compliancegate_data_source_errors_total[5m]) > 0page#runbook-compliancegate-data-source-error
ComplianceGateSanctionsCacheStalepolytraders_risk_compliancegate_sanctions_cache_age_seconds > 3600page#runbook-compliancegate-sanctions-cache
ComplianceGateHighRejectRaterate(polytraders_risk_compliancegate_decisions_total{decision='HARD_REJECT'}[5m]) / rate(polytraders_risk_compliancegate_decisions_total[5m]) > 0.2warn#runbook-compliancegate-reject-rate
ComplianceGateHighLatencyhistogram_quantile(0.99, rate(polytraders_risk_compliancegate_eval_latency_ms_bucket[5m])) > 100warn#runbook-compliancegate-latency

Dashboards

  • Grafana — Risk overview / ComplianceGate
  • Grafana — Compliance audit / jurisdiction and sanctions breakdown

16. Developer Reporting

{
  "bot_id": "risk.compliance_gate",
  "decision": "HARD_REJECT",
  "reason_code": "COMPLIANCE_GATE_JURISDICTION_BLOCKED",
  "inputs_used": [
    "internal.user.jurisdiction",
    "internal.sanctions.OFAC_SDN"
  ],
  "metrics": {
    "wallet": "0xaabbcc...redacted",
    "jurisdiction": "GB",
    "sanctions_hit": false,
    "onboarded": true,
    "market_category": "politics",
    "negrisk": false
  },
  "checked_at": "2026-05-09T10:22:00Z"
}

17. Plain-English Reporting

SituationUser-facing explanation
Order blocked — jurisdiction restrictedTrading is not available in your region due to regulatory restrictions on this platform.
Order blocked — account not onboardedYour account must complete the Polymarket onboarding process before placing orders. Please complete verification and try again.
Order blocked — wallet flaggedThis wallet cannot be used for trading on this platform. Please contact support if you believe this is an error.
Order blocked — market not available in your regionThis market is not available for trading in your region due to local regulations.
Order limited to close-onlyYou may only close or reduce existing positions in this market. New positions cannot be opened from your account.

18. Failure-Mode Block

main_failure_modeFailing open during a compliance-service outage, allowing ineligible users or sanctioned wallets to trade undetected. ComplianceGate must never approve when its data sources are unavailable.
false_positive_riskIncorrectly blocking a legitimate user due to a stale jurisdiction record, a cached sanctions-list false positive, or a market-eligibility misconfiguration in the admin override map.
false_negative_riskApproving an order using a stale sanctions-list snapshot that does not reflect a recently added entry, creating a compliance gap.
safe_fallbackIf any compliance data source (sanctions API, onboarding cache, Gamma market metadata) returns an error or stale data, ComplianceGate hard-rejects the order with COMPLIANCE_GATE_DATA_UNAVAILABLE. It never approves on missing policy data.
required_dependenciesSanctions screening service (OFAC SDN or configured provider), Polymarket onboarding status cache, Gamma API — market category and negRisk metadata, Internal user jurisdiction service, KillSwitch active flag, Admin market eligibility override map

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
SANCTIONS_SERVICE_DOWNBlock outbound calls to sanctions provider; let cache TTL expireReturns to normal within one cache-refresh cycle after connectivity is restored.
JURISDICTION_BLOCKED_USERSet user.country_code = 'US' in internal user profile mockImmediate on next evaluation once user profile is updated to a non-blocked jurisdiction.
SANCTIONS_HITAdd test wallet address to OFAC SDN mock listReturns to APPROVE once wallet is removed from the sanctions list and cache is refreshed.
GAMMA_API_DOWNReturn 503 from Gamma API mock for market metadata endpointReturns to APPROVE once Gamma API is reachable and market metadata is cached.
NOT_ONBOARDED_WALLETSet onboarding.completed = false in onboarding cache mock for test walletReturns to APPROVE once onboarding.completed = true is set and cache is refreshed.
KILL_SWITCH_ONSet internal.killswitch.status.active = trueReturns to normal pipeline on manual KillSwitch reset.

20. State & Persistence

Cold-start recovery

On cold start, all caches are empty. First evaluation per wallet/market triggers a blocking fetch. If the fetch fails, the order is rejected fail-closed until the cache is populated.

21. Concurrency & Idempotency

AspectSpecification
Execution modelsingle-threaded event loop
Max in-flight500
Idempotency keyintent_id
Per-call timeout (ms)50
Backpressure strategydrop newest
Locking / mutual exclusionper-wallet mutex for onboarding cache writes; sanctions list accessed read-only after refresh

22. Dependencies

Depends on (must run first)

BotWhyContract
risk.kill_switchGlobal brake — checked first before any compliance data is read.RiskVote.HARD_REJECT(KILL_SWITCH_ACTIVE) short-circuits all policy evaluation.

Emits to (downstream consumers)

BotWhyContract
exec.smart_routerApproved RiskVote passes to SmartRouter for ExecutionPlan construction.APPROVE passes through; HARD_REJECT causes SmartRouter to discard the intent without retry.

Sibling bots (same OrderIntent)

BotWhyContract
risk.portfolio_guardSibling guardrail; both must APPROVE or RESHAPE before SmartRouter runs.
risk.liquidity_guardSibling guardrail.

External services

ServiceEndpointSLA assumedOn failure
Gamma API (market metadata)https://gamma-api.polymarket.com99.9% / 300ms p99
OFAC SDN / Sanctions Screening Serviceinternal sanctions cache refreshed from upstream provider99.99% (in-memory cache); upstream refresh every 1h

23. Security Surfaces

Abuse vectors considered

  • VPN / proxy use to circumvent jurisdiction detection
  • Presenting a freshly created wallet not yet on sanctions lists
  • Timing attack: submitting order during sanctions-list refresh window when cache has just expired

Mitigations

  • Jurisdiction check uses platform-side geo signal, not user-declared location
  • Sanctions list refresh is asynchronous with overlap: old list is held until new list is fully loaded
  • Fail-closed policy: expired or unavailable cache → HARD_REJECT, never APPROVE

24. Polymarket V2 Compatibility

AspectValue
CLOB versionv2
Collateral assetpUSD
EIP-712 Exchange domain version2
Aware of builderCode fieldno
Aware of negative-risk marketsyes
Multi-chain readyno
SDK usedpy-clob-client-v2
Settlement contractCTFExchangeV2
NotesComplianceGate inspects market metadata from Gamma V2 to apply negRisk-aware category rules; geopolitical markets have zero platform fees and are flagged as negRisk, which affects eligibility checks. The bot does not sign orders or interact with CTFExchangeV2 directly.

API surfaces declared

gammainternal

Networks supported

polygon

25. Versioning & Migration

FieldValue
spec2.0.0
implementation2.1.0
schema2
released2026-04-28

Migration history

DateFromToReasonAction taken
2026-04-28v1v2CLOB V2 cutoverSwitched to py-clob-client-v2; all notional amounts now denominated in pUSD. Removed feeRateBps references from compliance-context payloads. Updated Gamma market-metadata fetch to use V2 negRisk and enableNegRisk flags for geopolitical category classification. EIP-712 domain version updated to '2' in downstream intent inspection.

26. Acceptance Tests

Unit Tests

TestSetupExpected result
Approve when all checks passwallet not sanctioned, jurisdiction=DE, onboarded=true, market category=crypto, negrisk=falseAPPROVE with no constraints
Reject when jurisdiction is blockedjurisdiction=US, close_only_on_violation=falseHARD_REJECT with reason_code=COMPLIANCE_GATE_JURISDICTION_BLOCKED
Reshape to close-only when jurisdiction blocked and close_only_on_violation=truejurisdiction=US, close_only_on_violation=true, order_type=REDUCERESHAPE with constraints.close_only=true
Reject when wallet is on OFAC SDN listwallet address matches OFAC SDN entryHARD_REJECT with reason_code=COMPLIANCE_GATE_SANCTIONS_HIT, regardless of close_only_on_violation
Reject when user not onboardedrequire_polymarket_onboarded=true, onboarded=falseHARD_REJECT with reason_code=COMPLIANCE_GATE_NOT_ONBOARDED
Reject when market category restricted for usermarket category=geopolitical, user jurisdiction eligibility=restrictedHARD_REJECT with reason_code=COMPLIANCE_GATE_MARKET_INELIGIBLE
Reject when sanctions service unavailable (fail-closed)sanctions service returns 503HARD_REJECT with reason_code=COMPLIANCE_GATE_DATA_UNAVAILABLE

Integration Tests

TestExpected result
Sanctions list refresh picked up within TTL windowHARD_REJECT(COMPLIANCE_GATE_SANCTIONS_HIT) after a wallet is added to the OFAC SDN snapshot and the cache is refreshed
NegRisk geopolitical market category check via Gamma APIHARD_REJECT(COMPLIANCE_GATE_MARKET_INELIGIBLE) when Gamma returns negRisk=true and category=geopolitical for a restricted user
KillSwitch active bypasses all policy checksHARD_REJECT(KILL_SWITCH_ACTIVE) without consulting any compliance data source

Property Tests

PropertyRequired behaviour
Sanctions-hit wallets are never approved regardless of other checksAlways true — COMPLIANCE_GATE_SANCTIONS_HIT cannot be overridden by close_only_on_violation
Unavailable policy data never results in APPROVEAlways true — any data source error produces HARD_REJECT(COMPLIANCE_GATE_DATA_UNAVAILABLE)
Reshape close_only is only emitted for non-sanctions violations with close_only_on_violation=trueAlways true — sanctions hits are always HARD_REJECT

27. Operational Runbook

ComplianceGate incidents typically involve a data-source outage causing fail-closed mass rejections, or a sanctions-list staleness alert. Any COMPLIANCE_GATE_SANCTIONS_HIT in production must be escalated to the compliance team immediately and never silently cleared.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
ComplianceGateDataSourceError
ComplianceGateSanctionsCacheStale
ComplianceGateHighRejectRate
ComplianceGateHighLatency

Manual overrides

Healthcheck

GET /internal/health/compliancegate → 200 if all data source caches (sanctions, onboarding, market eligibility) are within TTL and last successful refresh < 3600s ago; red if any cache is expired or last refresh attempt failed, or evaluation latency p99 > 100ms.

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
All unit tests pass including fail-closed scenariosCI test run100% pass
Sanctions screening integration test with mock OFAC SDN listIntegration test suitePass

Promote to Limited live

GateHow measuredThreshold
Shadow reject rate matches expected baseline within 5% over 48hGrafana shadow vs live comparison dashboard< 5% divergence
p99 evaluation latency < 50ms (cache-backed)polytraders_risk_compliancegate_eval_latency_ms histogramp99 < 50ms

Promote to General live

GateHow measuredThreshold
Zero COMPLIANCE_GATE_DATA_UNAVAILABLE rejections during normal operating hours over 7 daysComplianceGateDataSourceError alert history0 firings
Compliance team sign-off on sanctions list integrationWritten approval from compliance teamApproved
Fail-closed injection test confirmed in stagingFailure injection test suitePass

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