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 LayerExecution2.4 NonceShepherd

2.4 NonceShepherd

Execution Execution Utility Reshape LIVE General live capital · Direct P5 · Execution rails pending stub

NonceShepherd manages the on-chain nonce sequence for the Polytraders signing wallet on Polygon, ensuring that every V2 EIP-712 signed order carries a valid, non-colliding nonce. It detects nonce gaps (caused by dropped or reverted transactions), resequences pending orders when a gap is found, and enforces a ceiling on the number of pending signed-but-unposted orders. NonceShepherd also tracks CLOB ClobAuth credential TTLs and triggers re-authentication before they expire.

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

LayerExecution  Execution
Bot classExecution Utility
AuthorityReshape
StatusLIVE
ReadinessGeneral live
Runs beforeOrder signing (EIP-712 buildOrderTypedData) and CLOB POST /order submission
Runs afterExecutionPlan assembly (SmartRouter) and GasDecision (GasOracle)
Applies toEvery order that will be signed with the Polytraders wallet and submitted via CLOB V2
Default modegeneral_live
User-visibleAdvanced details only
Developer ownerPolytraders core

Operational profile

Modes supportedquarantine

2. Purpose

NonceShepherd manages the on-chain nonce sequence for the Polytraders signing wallet on Polygon, ensuring that every V2 EIP-712 signed order carries a valid, non-colliding nonce. It detects nonce gaps (caused by dropped or reverted transactions), resequences pending orders when a gap is found, and enforces a ceiling on the number of pending signed-but-unposted orders. NonceShepherd also tracks CLOB ClobAuth credential TTLs and triggers re-authentication before they expire.

3. Why This Bot Matters

  • Duplicate nonce on two simultaneous orders

    CTFExchangeV2 rejects one of the two orders; fill rate drops and the strategy misses partial execution on fast-moving markets.

  • Nonce gap after a reverted transaction

    All orders with nonces above the gap are stuck pending on-chain; they cannot fill until the gap is resolved, potentially leaving open risk positions unhedged.

  • Pending order count exceeds threshold

    Signing queue backs up; later orders are assigned nonces that depend on earlier unfilled orders, creating a chain of blocked submissions.

  • Expired ClobAuth credential

    CLOB POST /order returns 401; orders cannot be submitted until re-authentication completes, causing submission blackout.

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
ClobAuth credential (L2 API key + secret + passphrase)CLOB V2 auth endpointYesAuthenticate order submissions to the Polymarket CLOB V2. ClobAuth domain version is '1' (unchanged in V2).

5. Required Internal Inputs

InputSourceRequired?Use
On-chain nonce for signing wallet from Polygon stateonchain RPC (eth_getTransactionCount)YesDetermine the next valid nonce for the pending order; detect gaps by comparing on-chain nonce to internal sequence.
Internal pending-order sequence tableinternal Redis storeYesTrack which nonces have been assigned to signed-but-unposted orders to detect gaps and collisions.
ExecutionPlan from SmartRouterexec.smart_routerYesReceive the order to be signed; assign nonce and inject builderCode (bytes32) before handing off to wallet adapter.

6. Parameter Guide

ParameterDefaultWarningHardWhat it controls
pending_orders_threshold101520Maximum number of signed-but-unposted (pending) orders allowed in the signing queue at once. New order signing is blocked when this ceiling is reached.
resequence_on_gapTrueWhen true, NonceShepherd automatically re-signs pending orders above a nonce gap with corrected nonces after gap resolution.
refuse_during_gap_s3060120Seconds to refuse new signing requests while a nonce gap is being resolved. Prevents new orders from accumulating on top of an unresolved gap.
l2_credential_ttl_h2420Hours before a ClobAuth L2 credential expires at which NonceShepherd triggers a proactive re-authentication.

7. Detailed Parameter Instructions

pending_orders_threshold

What it means

Maximum number of signed-but-unposted (pending) orders allowed in the signing queue at once. New order signing is blocked when this ceiling is reached.

Default

{ "pending_orders_threshold": 10 }

Why this default matters

Allowing up to 10 pending orders balances throughput with the risk of a nonce gap cascading through a large queue.

Threshold logic

ConditionAction
pending_count <= 10APPROVE new signing request
10 < pending_count <= 15WARN — pending queue growing
15 < pending_count <= 20RESHAPE — slow down; hold new signing requests until queue drains below 10
pending_count > 20HARD_REJECT — queue at maximum; block signing until cleared

Developer check

if pending_count >= params.hard: raise NonceQueueFullError('NONCE_SHEPHERD_QUEUE_FULL')

User-facing English

Order placement is temporarily queued because several orders are still being processed.

resequence_on_gap

What it means

When true, NonceShepherd automatically re-signs pending orders above a nonce gap with corrected nonces after gap resolution.

Default

{ "resequence_on_gap": true }

Why this default matters

Automatic resequencing minimises the window during which orders are stuck; manual resequencing would require operator intervention on every reverted transaction.

Threshold logic

ConditionAction
resequence_on_gap=true AND gap detectedRe-sign affected orders with corrected nonces
resequence_on_gap=false AND gap detectedBlock signing until operator resolves gap manually

Developer check

if nonce_gap and params.resequence_on_gap: resequence_pending_above(gap_nonce)

User-facing English

— not yet authored —

refuse_during_gap_s

What it means

Seconds to refuse new signing requests while a nonce gap is being resolved. Prevents new orders from accumulating on top of an unresolved gap.

Default

{ "refuse_during_gap_s": 30 }

Why this default matters

A 30-second hold is long enough for most on-chain reorg resolutions without causing excessive submission latency.

Threshold logic

ConditionAction
gap_age_s <= 30Hold new signing — GAS_ORACLE_RPC_FAILURE or revert pending resolution
30 < gap_age_s <= 60WARN — gap taking longer than expected
gap_age_s > 120Escalate: emit NONCE_SHEPHERD_GAP_UNRESOLVED; pause signing until operator clears

Developer check

if gap_age_s > params.hard: emit_alert('NONCE_SHEPHERD_GAP_UNRESOLVED')

User-facing English

Order submission is paused briefly while a sequencing issue is resolved automatically.

l2_credential_ttl_h

What it means

Hours before a ClobAuth L2 credential expires at which NonceShepherd triggers a proactive re-authentication.

Default

{ "l2_credential_ttl_h": 24 }

Why this default matters

Renewing credentials 24 hours before expiry ensures that a re-auth failure (e.g. CLOB downtime) still leaves a large buffer to retry before the credential actually expires.

Threshold logic

ConditionAction
time_to_expiry > 24hNo action
2h < time_to_expiry <= 24hWARN — schedule re-auth
time_to_expiry <= 2hWARN — trigger immediate re-auth
time_to_expiry <= 0HARD_REJECT all order submissions until re-auth completes

Developer check

if cred.expires_at - now_ms() < params.l2_credential_ttl_h * 3600 * 1000: schedule_reauth()

User-facing English

— not yet authored —

8. Default Configuration

{
  "bot_id": "exec.nonce_shepherd",
  "version": "2.1.0",
  "mode": "general_live",
  "defaults": {
    "pending_orders_threshold": 10,
    "resequence_on_gap": true,
    "refuse_during_gap_s": 30,
    "l2_credential_ttl_h": 24
  },
  "locked": {
    "pending_orders_threshold": {
      "max": 20
    }
  }
}

9. Implementation Flow

  1. Receive ExecutionPlan from SmartRouter including order parameters and builderCode (bytes32).
  2. Check current pending-order count; if >= pending_orders_threshold hard limit, emit HARD_REJECT with NONCE_SHEPHERD_QUEUE_FULL.
  3. Fetch current on-chain nonce for signing wallet via onchain RPC eth_getTransactionCount('latest').
  4. Compare on-chain nonce to internal sequence table; detect any gap (on-chain nonce > highest unconfirmed internal nonce).
  5. If gap detected and resequence_on_gap=true: mark gap, hold new signing for refuse_during_gap_s seconds, re-sign pending orders above gap with corrected nonces.
  6. If gap_age_s > refuse_during_gap_s hard limit: emit NONCE_SHEPHERD_GAP_UNRESOLVED alert and pause signing.
  7. Assign next available nonce to the incoming order; record assignment in internal sequence table.
  8. Check ClobAuth credential TTL; if time_to_expiry < l2_credential_ttl_h: trigger background re-auth.
  9. Inject nonce and builderCode (bytes32) into the order payload; forward to wallet adapter for EIP-712 signing.
  10. After CLOB POST /order confirms receipt, mark nonce as consumed in sequence table.
  11. On fill or cancellation confirmation, remove the nonce entry from the pending table.

10. Reference Implementation

Receives an ExecutionPlan, assigns the next available on-chain nonce, injects the builderCode (bytes32), checks ClobAuth credential TTL, and emits a NonceAssignment for the wallet adapter to sign. Detects and resequences nonce gaps automatically.

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

FUNCTION assignNonce(execution_plan):
  // --- 1. Pending queue gate ---
  pending = FETCH internal.nonce_sequence_table.pending_count()
  IF pending >= params.pending_orders_threshold_hard:
    EMIT HARD_REJECT NONCE_SHEPHERD_QUEUE_FULL
    RETURN

  // --- 2. Fetch on-chain nonce ---
  onchain_nonce = FETCH onchain_rpc.eth_getTransactionCount(wallet_address, 'latest')
  IF onchain_nonce IS NULL:
    EMIT HARD_REJECT NONCE_SHEPHERD_RPC_FAILURE
    RETURN

  // --- 3. Detect nonce gap ---
  seq_head = internal.nonce_sequence_table.highest_pending()
  IF onchain_nonce < seq_head - pending:  // gap exists
    gap_nonce = onchain_nonce
    EMIT WARN NONCE_SHEPHERD_GAP_DETECTED
    IF params.resequence_on_gap:
      resequenced = resequence_pending_above(gap_nonce)
      // re-sign each affected order with corrected nonce
      FOR order IN resequenced:
        order.nonce = order.corrected_nonce
        wallet.sign(buildOrderTypedData(order, { version: '2' }))
    ELSE:
      hold_new_signing(refuse_during_gap_s=params.refuse_during_gap_s)
    gap_age_s = now_s() - gap_detected_at_s
    IF gap_age_s > params.refuse_during_gap_s_hard:
      EMIT ALERT NONCE_SHEPHERD_GAP_UNRESOLVED
      RETURN

  // --- 4. Assign nonce ---
  next_nonce = seq_head + 1
  internal.nonce_sequence_table.reserve(next_nonce, execution_plan.intent_id)

  // --- 5. Inject builderCode (bytes32) ---
  // V2 order field: builder(bytes32) — required for builder attribution
  builder_code = config.builder_code  // 32-byte hex from config

  // --- 6. ClobAuth credential TTL check ---
  cred = FETCH internal.clob_auth_credentials()
  ttl_h = (cred.expires_at - now_ms()) / 3_600_000
  IF ttl_h <= 0:
    EMIT HARD_REJECT NONCE_SHEPHERD_CREDENTIAL_EXPIRED
    RETURN
  IF ttl_h < params.l2_credential_ttl_h:
    schedule_background_reauth()
    EMIT WARN NONCE_SHEPHERD_CREDENTIAL_RENEWING

  // --- 7. Emit NonceAssignment ---
  EMIT NonceAssignment({
    intent_id: execution_plan.intent_id,
    assigned_nonce: next_nonce,
    builder_code: builder_code,       // bytes32
    eip712_domain_version: '2',
    clob_auth_domain_version: '1',
    credential_ttl_remaining_h: ttl_h,
    pending_count_after: pending + 1,
    assigned_at_ms: now_ms()
  })

SDK calls used

  • onchain_rpc.eth_getTransactionCount(wallet_address, 'latest')
  • buildOrderTypedData(orderParams, { name: 'CTFExchange', version: '2', chainId: 137 })
  • clob_auth.POST('/auth/api-key', credentials)
  • wallet.sign(typedData)

Complexity: O(N) for resequencing where N = number of pending orders above the gap; O(1) in the normal (no-gap) path

11. Wire Examples

Input — what arrives on the wire

{
  "label": "ExecutionPlan from SmartRouter awaiting nonce assignment",
  "source": "exec.smart_router",
  "payload": {
    "intent_id": "int_9a0b1c2d3e4f5a6b",
    "market_id": "0x9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c",
    "side": "SELL",
    "outcome": "YES",
    "order_type": "GTC",
    "tick_aligned_price": 0.71,
    "size_usd": 300,
    "iceberg": false,
    "eip712_domain_version": "2",
    "generated_at_ms": 1746768990000
  }
}

Output — what the bot emits

{
  "label": "NonceAssignment — nonce assigned, builderCode injected",
  "payload": {
    "shepherd_id": "exec.nonce_shepherd",
    "intent_id": "int_9a0b1c2d3e4f5a6b",
    "assigned_nonce": 1042,
    "builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
    "eip712_domain_version": "2",
    "clob_auth_domain_version": "1",
    "credential_ttl_remaining_h": 22.5,
    "pending_count_after": 7,
    "assigned_at_ms": 1746769000000
  }
}

12. Decision Logic

APPROVE

Pending count below threshold, no active nonce gap, and ClobAuth credential is valid. Assign nonce and forward to wallet adapter.

RESHAPE_REQUIRED

Nonce gap detected: hold new signing, resequence pending orders above gap, assign corrected nonces after gap resolution. Pending count in warning band: slow submission rate.

REJECT

Pending count at hard ceiling (20), or nonce gap unresolved for > refuse_during_gap_s hard limit, or ClobAuth credential expired.

WARNING_ONLY

Pending count in 10–15 warning band; or ClobAuth TTL approaching renewal threshold. Signing continues with WARN annotation.

13. Standard Decision Output

This bot returns a NonceAssignment object. See NonceAssignment schema.

{
  "shepherd_id": "exec.nonce_shepherd",
  "intent_id": "int_9a0b1c2d3e4f5a6b",
  "assigned_nonce": 1042,
  "builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
  "eip712_domain_version": "2",
  "clob_auth_domain_version": "1",
  "credential_ttl_remaining_h": 22.5,
  "pending_count_after": 7,
  "assigned_at_ms": 1746769000000
}

14. Reason Codes

CodeSeverityMeaningActionUser-facing message
NONCE_SHEPHERD_OKINFONonce assigned successfully; no gap detected; queue healthy.Emit NonceAssignment; proceed to wallet adapter.
NONCE_SHEPHERD_QUEUE_FULLHARD_REJECTPending signed-but-unposted order count has reached the hard ceiling.Block new signing requests until pending count drops below threshold.Order placement is temporarily paused. Earlier orders are being confirmed.
NONCE_SHEPHERD_GAP_DETECTEDWARNA gap in the nonce sequence was detected, indicating a reverted or dropped transaction.Hold new signing for refuse_during_gap_s; trigger resequencing if resequence_on_gap=true.Order submission is briefly paused while a sequencing issue is corrected.
NONCE_SHEPHERD_GAP_UNRESOLVEDHARD_REJECTNonce gap has not resolved within refuse_during_gap_s hard limit.Pause all new signing; emit alert to on-call; await operator action.Order submission is paused. Our team has been notified and is resolving the issue.
NONCE_SHEPHERD_CREDENTIAL_RENEWINGWARNClobAuth credential TTL is within l2_credential_ttl_h hours of expiry; background re-auth triggered.Schedule background re-auth. Continue processing with current credential.
NONCE_SHEPHERD_CREDENTIAL_EXPIREDHARD_REJECTClobAuth credential has expired; CLOB submissions will fail with 401.Block all order submissions until re-auth completes.Order submission is briefly paused while authentication is refreshed.
NONCE_SHEPHERD_RPC_FAILUREHARD_REJECTCannot fetch on-chain nonce from Polygon RPC.Block new signing. Emit alert. Resume once RPC recovers.Order submission is paused due to a network connectivity issue.
KILL_SWITCH_ACTIVEHARD_REJECTGlobal kill switch active; no new nonces assigned.Block all signing requests.Trading is currently paused.

15. Metrics & Logs

Metrics emitted

MetricTypeUnitLabelsMeaning
polytraders_exec_nonceshepherd_assignments_totalcountercountverdict, reason_codeTotal nonce assignment attempts by verdict and reason code.
polytraders_exec_nonceshepherd_pending_countgaugecountCurrent number of signed-but-unposted orders in the pending queue.
polytraders_exec_nonceshepherd_gap_events_totalcountercountresolvedTotal nonce gap events detected; label resolved=true/false.
polytraders_exec_nonceshepherd_credential_ttl_hoursgaugehoursRemaining TTL (hours) of the current ClobAuth credential; alert when < 2h.
polytraders_exec_nonceshepherd_resequence_countcountercountTotal orders that have been re-signed due to nonce gap resequencing.
polytraders_exec_nonceshepherd_assign_latency_mshistogrammsWall-clock time from ExecutionPlan receipt to NonceAssignment emit.

Alerts

AlertConditionSeverityRunbook
NonceShepherdGapUnresolvedrate(polytraders_exec_nonceshepherd_gap_events_total{resolved='false'}[5m]) > 0page#runbook-nonceshepherd-gap-unresolved
NonceShepherdQueueFullpolytraders_exec_nonceshepherd_pending_count >= 18warn#runbook-nonceshepherd-queue-full
NonceShepherdCredentialExpiringSoonpolytraders_exec_nonceshepherd_credential_ttl_hours < 2page#runbook-nonceshepherd-credential-expiry
NonceShepherdRpcDownrate(polytraders_exec_nonceshepherd_assignments_total{reason_code='NONCE_SHEPHERD_RPC_FAILURE'}[5m]) > 0page#runbook-nonceshepherd-rpc-failure

Dashboards

  • Grafana — Execution / NonceShepherd
  • Grafana — Order signing / nonce gap history and pending queue depth

16. Developer Reporting

{
  "shepherd_id": "exec.nonce_shepherd",
  "intent_id": "int_9a0b1c2d3e4f5a6b",
  "assigned_nonce": 1042,
  "on_chain_nonce": 1040,
  "gap_detected": false,
  "pending_count_before": 6,
  "pending_count_after": 7,
  "credential_ttl_remaining_h": 22.5,
  "resequence_triggered": false,
  "builder_code": "0x706f6c7974726164657273000000000000000000000000000000000000000000",
  "assigned_at_ms": 1746769000000
}

17. Plain-English Reporting

SituationUser-facing explanation
Order queued due to pending order backlogYour order is in the queue while earlier orders are being confirmed. It will be submitted shortly.
Submission paused — nonce gap being resolvedOrder submission is briefly paused while a sequencing issue is corrected automatically. No orders are lost.
Order blocked — credential renewal in progressOrder submission is paused for a moment while authentication credentials are refreshed.

18. Failure-Mode Block

main_failure_modeA nonce gap caused by a reverted transaction that NonceShepherd fails to detect (e.g. RPC returns stale nonce). All subsequent orders in the queue are stuck pending until the gap is detected and resolved.
false_positive_riskDetecting a phantom nonce gap due to RPC latency returning an outdated nonce value, causing unnecessary resequencing and submission delay.
false_negative_riskMissing a true nonce gap because the RPC node is lagging behind actual on-chain state, allowing duplicate-nonce submissions that CTFExchangeV2 will reject.
safe_fallbackIf on-chain RPC is unreachable, pause all new signing requests and emit NONCE_SHEPHERD_RPC_FAILURE. If ClobAuth credential has expired, block submissions and surface NONCE_SHEPHERD_CREDENTIAL_EXPIRED.
required_dependenciesPolygon onchain RPC (eth_getTransactionCount), Internal Redis nonce sequence table, CLOB V2 auth endpoint (ClobAuth), Wallet adapter for EIP-712 signing

19. Failure-Injection Recipes

ScenarioHow to injectExpected behaviourRecovery
NONCE_GAP_SIMULATEDRemove entry for nonce N from Redis sequence table while orders N+1..N+3 are pendingAfter resequencing completes, signing resumes; gap_events_total{resolved='true'} increments
QUEUE_FULLInject 20 pending nonce entries into Redis without consuming anyClears as pending orders confirm on-chain and are marked consumed
RPC_FAILUREBlock TCP to Polygon RPC endpointAutomatically resumes signing on RPC reconnect
CREDENTIAL_EXPIREDSet ClobAuth credential expires_at to now() - 1s in RedisRe-auth completes; new credential stored; signing resumes
KILL_SWITCH_ONActivate global KillSwitchResumes on manual KillSwitch reset

20. State & Persistence

Cold-start recovery

On restart, Redis nonce table is replayed to reconstruct pending state. Any nonce without on-chain confirmation is treated as potentially gapped and re-verified via RPC.

21. Concurrency & Idempotency

AspectSpecification
Execution modelsingle-threaded event loop
Max in-flight20
Idempotency keyintent_id
Per-call timeout (ms)300
Backpressure strategyBlock new requests when pending_count >= hard limit; internal queue for retry after gap resolution
Locking / mutual exclusionRedis SETNX per nonce to prevent double-assignment under concurrent requests

22. Dependencies

Depends on (must run first)

BotWhyContract
exec.smart_routerReceives ExecutionPlan containing order parameters and builderCode for nonce assignment.ExecutionPlan must include intent_id and eip712_domain_version='2'.
exec.gas_oracleGasOracle DEFER signals pause nonce reservation for queued ops to prevent nonce starvation.NonceShepherd holds reservations while GasOracle DEFER is active.
risk.kill_switchKillSwitch active blocks all new nonce assignments.No NonceAssignment emitted when KillSwitch is active.

Emits to (downstream consumers)

BotWhyContract
gov.builder_attributionbuilder_code field non-null on every NonceAssignment.

Used by (auto-aggregated)

2.3

External services

ServiceEndpointSLA assumedOn failure
Polygon RPC (nonce feed)https://polygon-rpc.com (or internal RPC node)best-effort / internal node target 99.9%
Polymarket CLOB V2 authhttps://clob.polymarket.com/auth/api-key99.9% (Polymarket-published)

23. Security Surfaces

Abuse vectors considered

  • Injecting a crafted nonce into the sequence table to displace a legitimate pending order
  • Replaying a stale NonceAssignment with an already-consumed nonce to force a CTFExchangeV2 rejection and DOS the submission pipeline
  • Manipulating ClobAuth credential TTL to prevent timely renewal, causing a submission blackout

Mitigations

  • Redis nonce table write access restricted to NonceShepherd service account via ACL
  • NonceAssignment records are HMAC-signed; consumers reject tampered records
  • Nonce deduplication: each nonce can only be in state pending|consumed — duplicate reservation rejected
  • ClobAuth re-auth triggered 24h before expiry providing a large retry buffer

24. Polymarket V2 Compatibility

AspectValue
CLOB versionv2
Collateral assetpUSD
EIP-712 Exchange domain version2
Aware of builderCode fieldyes
Aware of negative-risk marketsno
Multi-chain readyno
SDK usedpy-clob-client-v2
Settlement contractCTFExchangeV2
NotesNonceShepherd injects builderCode (bytes32) into every order payload before EIP-712 signing, making it a required node in the V2 builder attribution chain. ClobAuth domain version is '1' (Polymarket spec unchanged); Exchange EIP-712 domain is '2'. Fees are NOT in the signed order — operator-set by CTFExchangeV2 at match time.

API surfaces declared

clob_authonchaininternal

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 cutover — EIP-712 domain version updated, order nonce semantics changedUpdated EIP-712 Exchange domain version from '1' to '2'. Removed feeRateBps and legacy taker field from the order payload plumbing. Added builderCode (bytes32) injection into every NonceAssignment. ClobAuth domain version remains '1' (unchanged in V2). Collateral references updated from USDC.e to pUSD throughout shepherd logs.

26. Acceptance Tests

Unit Tests

TestSetupExpected result
Assign next sequential nonce when no gap existson_chain_nonce=100, internal_sequence_head=102, pending=2NonceAssignment.assigned_nonce=103
Detect and hold on nonce gapon_chain_nonce=105, internal_sequence=[100..104, 106..109] (gap at 105)gap_detected=true; new signing held for refuse_during_gap_s=30s
Resequence pending orders above gap when resequence_on_gap=truegap at nonce 105; pending orders with nonces 106..109Orders 106..109 re-signed with corrected nonces 105..108
HARD_REJECT when pending count >= 20pending_count=20NONCE_SHEPHERD_QUEUE_FULL emitted; no nonce assigned
Trigger re-auth when credential TTL < l2_credential_ttl_hcredential_expires_at = now + 20h, l2_credential_ttl_h=24Background re-auth scheduled; WARN annotation on NonceAssignment
builderCode (bytes32) injected into every NonceAssignmentStandard ExecutionPlan inputNonceAssignment.builder_code = 32-byte hex string

Integration Tests

TestExpected result
End-to-end: ExecutionPlan → NonceAssignment → signed V2 order → CLOB POST /orderOrder accepted by CLOB; nonce marked consumed in sequence table
Nonce gap resolves and pending orders drain correctlyAfter gap_resolved, resequenced orders submit in correct sequence with no duplicates

Property Tests

PropertyRequired behaviour
Each assigned nonce is unique across all pending ordersAlways true — sequence table enforces uniqueness
builderCode is always present (non-null bytes32) on every NonceAssignmentAlways true — builder_code_aware=true

27. Operational Runbook

NonceShepherd incidents are nonce gap events (usually caused by reverted transactions), queue full conditions during high-throughput periods, or ClobAuth credential expiry. All three cause submission blackouts of varying severity.

On-call actions

AlertFirst stepDiagnosisMitigationEscalate to
NonceShepherdGapUnresolvedCheck Grafana for gap_nonce value and pending order count. Verify on-chain nonce via eth_getTransactionCount.A transaction was reverted or dropped; gap_nonce is the nonce at which the sequence breaks.Run: polytraders bot resequence exec.nonce_shepherd --from-nonce <gap_nonce>Exec pod lead immediately — unresolved gap halts all order signing
NonceShepherdQueueFullCheck CLOB fill confirmation latency. If CLOB is slow to confirm, reduce strategy throughput temporarily.Pending orders are not being consumed fast enough; queue has reached the hard ceiling.Reduce strategy order rate. If CLOB is slow, check Polymarket status page.Exec pod lead if queue does not drain within 5 minutes
NonceShepherdCredentialExpiringSoonTrigger manual re-auth via: polytraders bot reauth exec.nonce_shepherdClobAuth credential approaching expiry with < 2h remaining.Run re-auth immediately. If re-auth fails, check CLOB auth endpoint availability.Exec pod lead if re-auth fails within 30 minutes of expiry
NonceShepherdRpcDownVerify Polygon RPC endpoint. Switch to fallback RPC node if primary is down.RPC unreachable; cannot determine on-chain nonce; all signing blocked.Switch to fallback RPC node. Restore primary if internal node.Infra on-call if RPC unreachable for > 5 minutes

Manual overrides

  • polytraders bot resequence exec.nonce_shepherd --from-nonce <N> — Manually triggers nonce resequencing from nonce N. Use when automatic resequencing fails.
  • polytraders bot reauth exec.nonce_shepherd — Forces immediate ClobAuth credential renewal. Use when automatic re-auth has failed or credential is near expiry.

Healthcheck

GET /internal/health/nonceshepherd → 200 if no active gap, pending_count < 15, ClobAuth credential valid > 2h, RPC reachable. RED if active nonce gap > refuse_during_gap_s, pending_count >= 20, credential expired, or RPC unreachable.

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 6 acceptance_tests.unit cases passCI test run100% pass
builderCode (bytes32) present on every NonceAssignment in integration testIntegration test: inspect NonceAssignment.builder_code fieldNon-null bytes32 on 100% of assignments

Promote to Limited live

GateHow measuredThreshold
Nonce gap simulation: gap detected and resequenced correctlyFailure injection: NONCE_GAP_SIMULATED scenarioPass — orders resequenced without duplicate nonces
p99 assign latency < 300ms over 24hpolytraders_exec_nonceshepherd_assign_latency_ms histogramp99 < 300ms

Promote to General live

GateHow measuredThreshold
E2E: ExecutionPlan → NonceAssignment → signed V2 order → fill on Polygon testnetE2E test on Polygon Amoy testnetPass with correct builderCode on signed order
7-day production shadow: zero duplicate nonces detectedAudit log — nonce sequence table reviewZero duplicates

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