1. Bot Identity
| Layer | Execution Execution |
|---|
| Bot class | Execution Utility |
|---|
| Authority | Reshape |
|---|
| Status | LIVE |
|---|
| Readiness | General live |
|---|
| Runs before | Order signing (EIP-712 buildOrderTypedData) and CLOB POST /order submission |
|---|
| Runs after | ExecutionPlan assembly (SmartRouter) and GasDecision (GasOracle) |
|---|
| Applies to | Every order that will be signed with the Polytraders wallet and submitted via CLOB V2 |
|---|
| Default mode | general_live |
|---|
| User-visible | Advanced details only |
|---|
| Developer owner | Polytraders core |
|---|
Operational profile
| Modes supported | quarantine |
|---|
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.
6. Parameter Guide
| Parameter | Default | Warning | Hard | What it controls |
|---|
| pending_orders_threshold | 10 | 15 | 20 | 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. |
| resequence_on_gap | True | — | — | When true, NonceShepherd automatically re-signs pending orders above a nonce gap with corrected nonces after gap resolution. |
| refuse_during_gap_s | 30 | 60 | 120 | Seconds 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_h | 24 | 2 | 0 | Hours 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
| Condition | Action |
|---|
| pending_count <= 10 | APPROVE new signing request |
| 10 < pending_count <= 15 | WARN — pending queue growing |
| 15 < pending_count <= 20 | RESHAPE — slow down; hold new signing requests until queue drains below 10 |
| pending_count > 20 | HARD_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
| Condition | Action |
|---|
| resequence_on_gap=true AND gap detected | Re-sign affected orders with corrected nonces |
| resequence_on_gap=false AND gap detected | Block 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
| Condition | Action |
|---|
| gap_age_s <= 30 | Hold new signing — GAS_ORACLE_RPC_FAILURE or revert pending resolution |
| 30 < gap_age_s <= 60 | WARN — gap taking longer than expected |
| gap_age_s > 120 | Escalate: 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
| Condition | Action |
|---|
| time_to_expiry > 24h | No action |
| 2h < time_to_expiry <= 24h | WARN — schedule re-auth |
| time_to_expiry <= 2h | WARN — trigger immediate re-auth |
| time_to_expiry <= 0 | HARD_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
- Receive ExecutionPlan from SmartRouter including order parameters and builderCode (bytes32).
- Check current pending-order count; if >= pending_orders_threshold hard limit, emit HARD_REJECT with NONCE_SHEPHERD_QUEUE_FULL.
- Fetch current on-chain nonce for signing wallet via onchain RPC eth_getTransactionCount('latest').
- Compare on-chain nonce to internal sequence table; detect any gap (on-chain nonce > highest unconfirmed internal nonce).
- 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.
- If gap_age_s > refuse_during_gap_s hard limit: emit NONCE_SHEPHERD_GAP_UNRESOLVED alert and pause signing.
- Assign next available nonce to the incoming order; record assignment in internal sequence table.
- Check ClobAuth credential TTL; if time_to_expiry < l2_credential_ttl_h: trigger background re-auth.
- Inject nonce and builderCode (bytes32) into the order payload; forward to wallet adapter for EIP-712 signing.
- After CLOB POST /order confirms receipt, mark nonce as consumed in sequence table.
- 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
| Code | Severity | Meaning | Action | User-facing message |
|---|
NONCE_SHEPHERD_OK | INFO | Nonce assigned successfully; no gap detected; queue healthy. | Emit NonceAssignment; proceed to wallet adapter. | |
NONCE_SHEPHERD_QUEUE_FULL | HARD_REJECT | Pending 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_DETECTED | WARN | A 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_UNRESOLVED | HARD_REJECT | Nonce 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_RENEWING | WARN | ClobAuth 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_EXPIRED | HARD_REJECT | ClobAuth 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_FAILURE | HARD_REJECT | Cannot 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_ACTIVE | HARD_REJECT | Global kill switch active; no new nonces assigned. | Block all signing requests. | Trading is currently paused. |
15. Metrics & Logs
Metrics emitted
| Metric | Type | Unit | Labels | Meaning |
|---|
polytraders_exec_nonceshepherd_assignments_total | counter | count | verdict, reason_code | Total nonce assignment attempts by verdict and reason code. |
polytraders_exec_nonceshepherd_pending_count | gauge | count | | Current number of signed-but-unposted orders in the pending queue. |
polytraders_exec_nonceshepherd_gap_events_total | counter | count | resolved | Total nonce gap events detected; label resolved=true/false. |
polytraders_exec_nonceshepherd_credential_ttl_hours | gauge | hours | | Remaining TTL (hours) of the current ClobAuth credential; alert when < 2h. |
polytraders_exec_nonceshepherd_resequence_count | counter | count | | Total orders that have been re-signed due to nonce gap resequencing. |
polytraders_exec_nonceshepherd_assign_latency_ms | histogram | ms | | Wall-clock time from ExecutionPlan receipt to NonceAssignment emit. |
Alerts
| Alert | Condition | Severity | Runbook |
|---|
NonceShepherdGapUnresolved | rate(polytraders_exec_nonceshepherd_gap_events_total{resolved='false'}[5m]) > 0 | page | #runbook-nonceshepherd-gap-unresolved |
NonceShepherdQueueFull | polytraders_exec_nonceshepherd_pending_count >= 18 | warn | #runbook-nonceshepherd-queue-full |
NonceShepherdCredentialExpiringSoon | polytraders_exec_nonceshepherd_credential_ttl_hours < 2 | page | #runbook-nonceshepherd-credential-expiry |
NonceShepherdRpcDown | rate(polytraders_exec_nonceshepherd_assignments_total{reason_code='NONCE_SHEPHERD_RPC_FAILURE'}[5m]) > 0 | page | #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
| Situation | User-facing explanation |
|---|
| Order queued due to pending order backlog | Your order is in the queue while earlier orders are being confirmed. It will be submitted shortly. |
| Submission paused — nonce gap being resolved | Order submission is briefly paused while a sequencing issue is corrected automatically. No orders are lost. |
| Order blocked — credential renewal in progress | Order submission is paused for a moment while authentication credentials are refreshed. |
18. Failure-Mode Block
| main_failure_mode | A 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_risk | Detecting a phantom nonce gap due to RPC latency returning an outdated nonce value, causing unnecessary resequencing and submission delay. |
|---|
| false_negative_risk | Missing a true nonce gap because the RPC node is lagging behind actual on-chain state, allowing duplicate-nonce submissions that CTFExchangeV2 will reject. |
|---|
| safe_fallback | If 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_dependencies | Polygon onchain RPC (eth_getTransactionCount), Internal Redis nonce sequence table, CLOB V2 auth endpoint (ClobAuth), Wallet adapter for EIP-712 signing |
|---|
19. Failure-Injection Recipes
| Scenario | How to inject | Expected behaviour | Recovery |
|---|
NONCE_GAP_SIMULATED | Remove entry for nonce N from Redis sequence table while orders N+1..N+3 are pending | | After resequencing completes, signing resumes; gap_events_total{resolved='true'} increments |
QUEUE_FULL | Inject 20 pending nonce entries into Redis without consuming any | | Clears as pending orders confirm on-chain and are marked consumed |
RPC_FAILURE | Block TCP to Polygon RPC endpoint | | Automatically resumes signing on RPC reconnect |
CREDENTIAL_EXPIRED | Set ClobAuth credential expires_at to now() - 1s in Redis | | Re-auth completes; new credential stored; signing resumes |
KILL_SWITCH_ON | Activate global KillSwitch | | Resumes 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
| Aspect | Specification |
|---|
| Execution model | single-threaded event loop |
| Max in-flight | 20 |
| Idempotency key | intent_id |
| Per-call timeout (ms) | 300 |
| Backpressure strategy | Block new requests when pending_count >= hard limit; internal queue for retry after gap resolution |
| Locking / mutual exclusion | Redis SETNX per nonce to prevent double-assignment under concurrent requests |
22. Dependencies
Depends on (must run first)
| Bot | Why | Contract |
|---|
| exec.smart_router | Receives ExecutionPlan containing order parameters and builderCode for nonce assignment. | ExecutionPlan must include intent_id and eip712_domain_version='2'. |
| exec.gas_oracle | GasOracle DEFER signals pause nonce reservation for queued ops to prevent nonce starvation. | NonceShepherd holds reservations while GasOracle DEFER is active. |
| risk.kill_switch | KillSwitch active blocks all new nonce assignments. | No NonceAssignment emitted when KillSwitch is active. |
Emits to (downstream consumers)
Used by (auto-aggregated)
2.3
External services
| Service | Endpoint | SLA assumed | On failure |
|---|
| Polygon RPC (nonce feed) | https://polygon-rpc.com (or internal RPC node) | best-effort / internal node target 99.9% | |
| Polymarket CLOB V2 auth | https://clob.polymarket.com/auth/api-key | 99.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
| Aspect | Value |
|---|
| CLOB version | v2 |
| Collateral asset | pUSD |
| EIP-712 Exchange domain version | 2 |
| Aware of builderCode field | yes |
| Aware of negative-risk markets | no |
| Multi-chain ready | no |
| SDK used | py-clob-client-v2 |
| Settlement contract | CTFExchangeV2 |
| Notes | NonceShepherd 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
| Field | Value |
|---|
| spec | 2.0.0 |
| implementation | 2.1.0 |
| schema | 2 |
| released | 2026-04-28 |
Migration history
| Date | From | To | Reason | Action taken |
|---|
| 2026-04-28 | v1 | v2 | CLOB V2 cutover — EIP-712 domain version updated, order nonce semantics changed | Updated 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
| Test | Setup | Expected result |
|---|
| Assign next sequential nonce when no gap exists | on_chain_nonce=100, internal_sequence_head=102, pending=2 | NonceAssignment.assigned_nonce=103 |
| Detect and hold on nonce gap | on_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=true | gap at nonce 105; pending orders with nonces 106..109 | Orders 106..109 re-signed with corrected nonces 105..108 |
| HARD_REJECT when pending count >= 20 | pending_count=20 | NONCE_SHEPHERD_QUEUE_FULL emitted; no nonce assigned |
| Trigger re-auth when credential TTL < l2_credential_ttl_h | credential_expires_at = now + 20h, l2_credential_ttl_h=24 | Background re-auth scheduled; WARN annotation on NonceAssignment |
| builderCode (bytes32) injected into every NonceAssignment | Standard ExecutionPlan input | NonceAssignment.builder_code = 32-byte hex string |
Integration Tests
| Test | Expected result |
|---|
| End-to-end: ExecutionPlan → NonceAssignment → signed V2 order → CLOB POST /order | Order accepted by CLOB; nonce marked consumed in sequence table |
| Nonce gap resolves and pending orders drain correctly | After gap_resolved, resequenced orders submit in correct sequence with no duplicates |
Property Tests
| Property | Required behaviour |
|---|
| Each assigned nonce is unique across all pending orders | Always true — sequence table enforces uniqueness |
| builderCode is always present (non-null bytes32) on every NonceAssignment | Always 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
| Alert | First step | Diagnosis | Mitigation | Escalate to |
|---|
NonceShepherdGapUnresolved | Check 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 |
NonceShepherdQueueFull | Check 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 |
NonceShepherdCredentialExpiringSoon | Trigger manual re-auth via: polytraders bot reauth exec.nonce_shepherd | ClobAuth 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 |
NonceShepherdRpcDown | Verify 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.
29. Developer Checklist
Ready-to-ship score: 27/27 sections complete · 100%
| Requirement | Status |
|---|
| 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 |