System diagram
Off-chain services
core-api
core-api
Client-facing HTTP gateway. Partner traffic authenticates with
X-Api-Key on core.api.dev.predictstreet.sde.adifoundation.ai; retail user sessions
(SIWE) live on a separate surface, app.predictstreet.io, and
are out of scope for this documentation. Rate limits, geo and
compliance guards, KYC gating. Every request enters here before
being routed to the appropriate back-end. Owns compliance
screening (Global Ledger), sanctions, banned-wallet checks, and
cleared deposit-sources enforcement. See
Authentication overview and
API keys.exchange-service
exchange-service
Orders, balances, trades, fees, withdrawals. PostgreSQL-backed
single source of truth for off-chain state. Verifies EIP-712
signatures, locks balance, calls matching-core over gRPC, persists
results, publishes events to NATS JetStream.
matching-core
matching-core
In-memory central-limit orderbook engine (Go). One engine per
market. Implements FIFO priority within a price level; supports
GTC / IOC / FOK time-in-force and LIMIT / MARKET order types.
Stateless from a client perspective — exchange-service holds
authoritative PG state; matcher state is warm-restartable.
oracle-service
oracle-service
Polls three independent sports data providers
(Stats Perform primary + API-Football + Sportmonks), evaluates
consensus (3-of-3 auto-proposes; 2-of-3 blocks unless the
discrepancy is explainable against the primary source), and
posts outcomes on-chain via
PredictStreetOracle.proposeOutcome.
Handles the 2-hour challenge window, override, void, and
delayed-data paths. See
Settlement overview.match-submitter
match-submitter
Broadcasts matched trades on-chain in batches. Maintains a pool
of pre-funded operator EOAs (whitelisted via
CTFExchange.addOperator() on both binary and neg-risk exchanges),
an atomic per-wallet nonce manager, and a tx-tracker that polls
receipts and reclassifies stuck or dropped transactions. A
gas-refiller cron tops up operator wallets from a treasury
address. On a full revert, trades are marked
settlement_failed; per-order failures come in via
MatchFailed events from chain-watcher.ws-gateway
ws-gateway
WebSocket fan-out. Consumes NATS JetStream events from
exchange-service and oracle-service and multiplexes channels.
Handshake auth accepts both
X-Api-Key (partners) and Bearer JWT
(retail SIWE) on /ws/user AND /ws/market — same two schemes
as HTTP, same scope rules. Neither gateway is open: a handshake
without a key or token closes 1008 unauthorized. See
WebSocket overview.chain-watcher
chain-watcher
Ponder-based indexer for every PredictStreet contract (Exchange,
ConditionalTokens, NegRiskAdapter, VaultFactory, Oracle).
Persists events in durable outbox tables and publishes them to
NATS JetStream (
chain.> subjects) with at-least-once delivery.
Handles seq-gap recovery and reorg rewind. Internal — partners
consume confirmed state through core-api / WebSocket, not
directly from chain-watcher.On-chain contracts
User Vault (EIP-1167 clone)
User Vault (EIP-1167 clone)
One minimal-proxy clone per user, deployed through
VaultFactory
on first deposit. Holds the user’s ERC-20 collateral (USDC) and
ERC-1155 outcome positions. Non-upgradeable — the implementation
is pinned and never swapped under existing vaults.Custody model: withdrawals, split / merge, and neg-risk
convertPositions require a dual EIP-712 signature (vault owner- factory owner). The backend cannot move user funds unilaterally. An emergency-withdraw path gives the owner unilateral access with a 7-day on-chain timelock.
CTFExchange
CTFExchange
Matches and settles signed orders. Implements the PS-QUADRATIC-V1
taker-fee curve
fee = k × P × (1−P) per
Fees. Admin-gated for
pause / unpause / registerToken / setVaultFactory; operator-gated
for fillOrder / matchOrders.ConditionalTokens
ConditionalTokens
Polymarket-compatible ERC-1155 position tokens.
prepareCondition
registers a new market condition; position IDs are deterministic
from (collateral, conditionId, indexSet). Oracle-service calls
prepareCondition as part of market deployment.NegRiskAdapter + PredictStreetNegRiskCtfExchange
NegRiskAdapter + PredictStreetNegRiskCtfExchange
Separate deployment path for multi-outcome categorical markets
(e.g. 3+ exclusive outcomes). Reuses the same EIP-712 order shape
as the binary exchange; differences are in the position-token
derivation and
convertPositions for NO-basket → collateral
conversions.PredictStreetOracle
PredictStreetOracle
Single on-chain oracle contract. Oracle-service is its only
authorized writer for
proposeOutcome / finalizeOutcome /
resolveChallenge* / voidMarket / markDelayed. Enforces a
10 USDC challenger bond and a 2-hour challenge window; emergency
proposal clear is gated by a 7-day admin timelock. See
Settlement flow.The hot path in one paragraph
A partner callsPOST /api/orders/place with an EIP-712-signed order. core-api
verifies the X-Api-Key header, runs compliance guards, and forwards to
exchange-service. exchange-service verifies the EIP-712 signature
against the expected signer EOA (matching the vault owner for
SignatureType.VAULT), locks the required quote notional
(available → locked), persists the order row, and calls matching-core
over gRPC. matching-core runs the book, returns fills synchronously.
exchange-service writes the trade rows + fee ledger in a single PG
transaction, publishes events to NATS, and returns the response.
No on-chain transaction happens here. Trades are picked up from
the trades table by match-submitter, which batches them into
fillOrder / matchOrders calls against CTFExchange (or the
neg-risk exchange), signed by an operator EOA from its wallet pool.
chain-watcher sees the resulting OrderFilled / MatchFailed
events and notifies exchange-service to finalize the trade state.
Strict vs optimistic settlement
PredictStreet uses a strict settlement model: a trade row is persisted immediately after match, but the user’s balance is only decremented fromlocked after on-chain confirmation from
chain-watcher. The upside is simpler revert semantics (a reverted chain
tx simply refunds locked → available) and no need for an unsettled
balance bucket. The tradeoff is a small settlement delay between
match and the balance update being visible as “available” again.
What partners own vs what PredictStreet owns
| Concern | Who owns it |
|---|---|
| User custody of USDC + outcome tokens | User (via their vault) |
| EIP-712 key | User (or their delegated signer) |
| API-key issuance + session lifecycle | PredictStreet (core-api) |
| Balance bookkeeping off-chain | PredictStreet (exchange-service) |
| Orderbook state (hot) | PredictStreet (matching-core, in-memory) |
| Final settlement ledger | On-chain — ConditionalTokens / CTFExchange / Vault |
| Oracle resolution proposal | PredictStreet (oracle-service) |
| Oracle challenge (dispute) | User (via on-chain bond) |
| Fee configuration | PredictStreet (admin-api — changes require audit trail) |
Next
Authentication
X-Api-Key format, scope taxonomy, and rotation.Order lifecycle
State machine and transition rules.
Contracts reference
Deployed addresses, ABIs, invariants.
Environments
Testnet, staging, mainnet endpoints.