This document describes the system architecture of Predix, a prediction market for 1-minute BTC price movements built on Stacks and finalized on Bitcoin.
Predix consists of four layers: the client layer (browsers and AI agents), the platform layer (Next.js serverless functions), the state layer (Redis and Stacks blockchain), and the oracle layer (Pyth Network).
CLIENTS PLATFORM STATE ORACLE
+------------------+ +------------------------+ +----------------+ +--------------+
| | | | | | | |
| Browser (Xverse) |-->| Next.js API Routes |-->| Stacks Testnet | | Pyth Network |
| AI Agent (SDK) |-->| /api/sponsor | | predixv7 | | Hermes SSE |
| MCP Client |-->| /api/agent/* | | gatewayv6 | | Benchmarks |
| | | /api/cron/* | | test-usdcx | | |
+------------------+ | | +----------------+ +--------------+
| @predix/mcp (stdio) | | | |
| | | Upstash Redis | |
+------------------------+ | pools, nonces | |
| | tickets, keys | |
+-------------------->| |<--------+
+----------------+
Every interaction with the market contract routes through a thin gateway proxy (gatewayv6). The market contract (predixv7) rejects all direct calls.
Why: This pattern provides a single enforcement point for access control, enables sponsor-only settlement, supports timelocked upgrades without redeploying the market contract, and allows the gateway to be paused independently.
User/Agent --> gatewayv6.place-bet(...) --> predixv7.place-bet(...)
|
[round sanity check]
[pause check]
[sponsor check for settlement]
Users and agents never pay gas. The platform sponsors every transaction.
Flow:
- Client builds an unsigned transaction with
sponsored: true, fee: 0. - User or agent signs the transaction locally (private key never leaves the client).
- Signed hex is sent to
/api/sponsor. - Server validates the transaction (contract allowlist, function allowlist), adds sponsorship (sets fee, sets sponsor), and broadcasts to the Stacks network.
- Server tracks nonce in Redis to prevent
ConflictingNonceInMempoolerrors during rapid submissions.
Why: Gas fees are the primary friction for onboarding non-crypto users and AI agents. Sponsored transactions eliminate this barrier entirely.
Users never claim winnings. A Vercel cron job runs every 60 seconds, fetches the settlement price from Pyth Benchmarks, and calls resolve-and-distribute on-chain. This single contract call atomically resolves the round, computes payouts, distributes to all winners, and splits fees.
Why: Manual claiming creates a poor user experience, leaves funds in limbo, and requires users to pay additional gas. Atomic settlement is simpler, faster, and eliminates the entire claim UX.
Predix maintains two sources of truth:
| Source | Data | Latency |
|---|---|---|
| Stacks blockchain | Round outcomes, bet records, balances, jackpot treasury | ~10s (block time) |
| Upstash Redis | Optimistic pool totals, open prices, nonce tracking, jackpot tickets, agent keys | ~1ms |
Clients poll /api/round every second. The response merges on-chain state (from Hiro API) with optimistic state (from Redis) using max(on-chain, optimistic) for pool totals.
Why: On-chain finality takes ~10 seconds. Without optimistic state, the UI would appear frozen after placing a bet. Redis provides instant feedback while the blockchain catches up.
T+0s T+50s T+60s
| | |
| TRADING WINDOW | PRE-SETTLEMENT | SETTLEMENT
| (bets accepted) | (bets closed) | (cron resolves)
| | |
+----------------------+--------------------+
| | |
| open price set | cron fetches | resolve-and-distribute
| (first-write-wins) | Pyth Benchmarks | called on-chain
| | validates circuit | payouts distributed
| | breaker rules | fees split (2%+1%)
- Round opens (T+0s): Round ID is
floor(unix_timestamp / 60). The first bet triggers round creation on-chain. The open price is set via first-write-wins in Redis. - Trading window (T+0s to T+50s): Users and agents place bets through the gateway. Bets accumulate per side (UP/DOWN). Users can bet on both sides in the same round.
- Pre-settlement window (T+50s to T+60s): Betting closes. The cron job prepares for settlement: fetches the close price from Pyth Benchmarks, runs circuit breaker validation.
- Settlement (T+60s): The cron calls
resolve-and-distributethrough the gateway. The contract atomically resolves the round, distributes payouts to winners, and splits fees (2% operations + 1% jackpot treasury).
Predix implements two layers of price validation to prevent oracle manipulation and stale data.
| Check | Threshold | Action |
|---|---|---|
| Price movement | > 0.5% in 60 seconds | Skip round |
| Oracle divergence | > 0.3% between Hermes and Benchmarks | Skip round |
| Price range | Outside $10,000 - $500,000 | Skip round |
| Check | Threshold | Action |
|---|---|---|
| Price bounds | > 1% from last known price | Reject transaction (ERR_PRICE_OUT_OF_BOUNDS) |
The server-side checks are conservative (tighter thresholds). The on-chain check is a defense-in-depth measure that catches anything the server missed.
The jackpot is a hybrid on-chain/off-chain system.
On-chain: 1% of all fees stay in the contract as a jackpot-balance data variable. The pay-jackpot-winner function transfers from this treasury.
Off-chain (Redis): Ticket tracking and draw logic.
| Multiplier | Condition |
|---|---|
| 1x | Base: $1 bet in the early window (first 20 seconds) = 1 ticket |
| 2x | First bettor on a side |
| 2x | Largest bet on a side |
| 4x | Both first and largest |
Daily draw (21:00 ET): A Bitcoin block hash serves as verifiable randomness. The winner receives 10% of the on-chain treasury. The draw is executed via pay-jackpot-winner through the gateway.
Predix provides four integration paths for AI agents:
+------------------+
| Agent Client |
+--------+---------+
|
+----------------+----------------+
| | |
+------v------+ +-----v------+ +------v------+
| REST API | | MCP Server | | SDKs |
| /api/agent | | @predix/mcp | | TS + Python |
+------+------+ +-----+------+ +------+------+
| | |
+----------------+----------------+
|
+--------v---------+
| /api/sponsor |
| (sign + submit) |
+--------+---------+
|
+--------v---------+
| Stacks Network |
+------------------+
Authentication: Agents register by signing a message with their Stacks wallet, proving ownership. The platform issues an API key (pk_live_...) used for rate-limited access.
Transaction signing: The platform builds unsigned sponsored transactions. The agent signs locally with its private key. The signed transaction is submitted to /api/sponsor for sponsorship and broadcast. The agent's private key never leaves its machine.
Webhooks: Agents can subscribe to events (round.resolved, bet.confirmed, jackpot.drawn, etc.) and receive HMAC-signed HTTP callbacks.
Discovery: Standard manifests at /.well-known/agent.json and /.well-known/ai-plugin.json enable automated agent onboarding.
| Component | Platform | Configuration |
|---|---|---|
| Frontend + API | Vercel (serverless) | Next.js App Router |
| Database | Upstash Redis | Serverless, global replication |
| Blockchain | Stacks testnet | ~10s blocks (post-Nakamoto) |
| Oracle | Pyth Network | Hermes SSE (live) + Benchmarks (settlement) |
| Cron: Settlement | Vercel Cron | Every 60 seconds |
| Cron: Jackpot | Vercel Cron | Daily at 21:00 ET |
| Fallback daemon | Railway/Render | scripts/resolver-daemon.mjs |
| Boundary | Enforcement |
|---|---|
| User input | Contract validates side, amount, timing, round ID |
| Oracle data | Two-layer circuit breaker (server + contract) |
| Gateway access | Contract-level caller check, gateway-only |
| Settlement access | Sponsor-only via gateway |
| Upgrades | 144-block timelock on gateway and sponsor changes |
| Emergency | Pause + 200-block cooldown + 50% cap on withdrawals |
| Agent keys | SHA-256 hashed in Redis, rate-limited per tier |
| Webhooks | SSRF prevention, HMAC-SHA256 signing, auto-disable after 50 failures |
| Nonces | Redis lock + KV tracking prevents mempool conflicts |