Architecture
GoMarket is a hybrid-decentralized system: order matching off-chain, settlement on-chain, non-custodial throughout.
The whole system
Section titled “The whole system”┌─────────────────────────────────────────────────────────────────────┐│ CLIENTS ││ Web · Mobile · Bots · Platform API (Django) │└───────────────┬─────────────────────────────────────────────────────┘ │ HTTP/JSON (SIWS) · gRPC ▼┌─────────────────────────────────────────────────────────────────────┐│ OFF-CHAIN SERVICES (Rust) ││ ││ ┌───────────────────────────────────────────────────────────────┐ ││ │ Order Manager │ ││ │ Books (BTreeMap) · Matching · Validator · Event loop (3 thr) │ ││ │ Transports: axum HTTP /v1 (SIWS) + tonic gRPC │ ││ └────┬──────────────────┬────────────────────┬─────────────────┘ ││ │ gRPC │ gRPC │ REST ││ ▼ ▼ ▼ ││ ┌──────────┐ ┌──────────────┐ ┌──────────────┐ ││ │ Ledger │ │ Executor │ │ Markets API │ ││ │ PG │◀─────│ build/sign/ │───▶│ catalogue │ ││ │ Redis │ │ send + conf │ │ PG │ ││ └──────────┘ └──────┬───────┘ └──────────────┘ ││ │ OrderSizeUpdate → Order Manager │└───────────────────────────┼─────────────────────────────────────────┘ │ Solana JSON-RPC ▼┌─────────────────────────────────────────────────────────────────────┐│ ON-CHAIN (Solana, Anchor) ││ ││ markets high-market-parlay ││ ├ execute_trade ├ create_parlay ││ ├ mint / merge ├ settle_leg (permissionless) ││ ├ redeem / refund ├ claim_parlay ││ ├ lifecycle + disputes ├ void_parlay (admin) ││ ├ set_delegate └ vault: seed / withdraw ││ └ price accumulator ──────── read (no CPI) ─────────▶ │└─────────────────────────────────────────────────────────────────────┘Components
Section titled “Components”| Component | Type | Responsibility |
|---|---|---|
| markets program | On-chain | Settlement (execute_trade), market lifecycle, mint/merge/redeem/refund, maker delegation, fees, price accumulator |
| high-market-parlay program | On-chain | Parlay slips, vault solvency, per-leg settlement, claims |
| Order Manager | Rust | In-memory books, validation, matching; HTTP (SIWS) + gRPC |
| Markets API | Rust | The market catalogue; create-market CLI |
| Ledger | Rust | PostgreSQL order/trade persistence; Redis notifications fan-out |
| Executor | Rust | Builds/signs/sends settlement txs; confirmation tracking; retries; reconciliation |
| Platform API | Django | Indexes both programs from Helius webhooks; product REST surface |
On-chain: markets
Section titled “On-chain: markets”| PDA | Seeds |
|---|---|
| Config | ["markets_config"] |
| Market | ["market", market_id:u64 LE] |
| YES mint | ["yes_mint", market_pda] |
| NO mint | ["no_mint", market_pda] |
| Collateral vault | ["vault", market_pda] |
| Fee vault | ["fee_vault", mint] |
| Delegate | ["delegate", maker] |
execute_trade
Section titled “execute_trade”Settles one maker leg as a direct swap. Signers: the taker, the config
operator, and the program (for the maker’s delegate PDA). The maker
does not sign — its tokens move under a standing Approve allowance.
The handler recomputes the domain-separated order hash and requires a
top-level ed25519 verify instruction in the same transaction; the
ed25519 program cannot be CPI’d, so it scans the Instructions sysvar to
prove presence.
Same-side matches prepend mint_tokens or merge_tokens in the same
transaction, keeping the sequence atomic.
No on-chain order state
Section titled “No on-chain order state”There is no nonce PDA and no fill-status PDA. Nonce, replay and fill bookkeeping are off-chain, in the executor — the trusted single settlement instance. A caller bypassing it can settle the same signed order repeatedly, bounded only by the maker’s delegate allowance.
On-chain: high-market-parlay
Section titled “On-chain: high-market-parlay”Separate program, its own USDC vault, its own Parlay accounts. It never
touches outcome token mints.
| PDA | Seeds |
|---|---|
| ParlayConfig | ["parlay_config"] |
| Vault | ["parlay_vault"] |
| Parlay | ["parlay", user, user_seq:u64 LE] |
Keying the Parlay on (user, user_seq) lets two users create their first
slip in the same slot without racing a global counter.
Reads without CPI
Section titled “Reads without CPI”The parlay program deserialises Market and MarketEventLink accounts
directly:
1. require!(account.owner == amm_program_id, InvalidMarketOwner);2. let market = AmmMarket::try_deserialize(&mut data.as_ref()) .map_err(|_| InvalidMarketAccount)?;No CPI means the parlay is decoupled at runtime — an upgrade to the market program doesn’t require redeploying the parlay. The cost is a compile-time coupling: the parlay links the market crate as a workspace path dependency, so a layout change fails the build rather than silently misreading bytes.
amm_program_id is frozen at initialize and absent from update_config,
because a rotatable program id would let an admin point the parlay at a
malicious program whose accounts decode to chosen outcomes.
Off-chain services
Section titled “Off-chain services”Order Manager
Section titled “Order Manager”- Books — one
BTreeMapper market asset; O(log n) insert/delete, ordered iteration for price-time priority. - Matching — the taker walks the book applying fees, tick rounding, expiry checks, a price-distortion guard and a self-trade filter.
- Validation — one-time checks on insert plus on-chain reads over JSON-RPC (ATA balance, delegate allowance).
- Event loop — three std threads: new-order ingestion, catalogue pulls, game-start ejections. Queries run inline.
- Transports — axum HTTP under
/v1/with SIWS, tonic gRPC.
External dependencies sit behind traits — LedgerService,
ExecutionService, RiskService, MarketsService, UniqueOrderService,
pending services — each with an in-memory implementation, so the crate is
self-contained and testable. The binary boots with network backends by
default.
Ledger
Section titled “Ledger”One gRPC surface (clob.LedgerService) over PostgreSQL via sqlx. Every
state change is encoded as one JSON document and appended to the Redis
notifications stream by a single producer. Consumers filter on the message
type. Publish errors are fire-and-forget — the stream is a fan-out
convenience, not the source of truth.
Executor
Section titled “Executor”Validates, de-dupes by full trade key, builds the transaction (one
execute_trade per maker leg, the residual mint/merge, the ed25519 verify
ix, ComputeBudget), signs with the operator and taker keys, and sends. CU
budget is simulateTransaction × 1.2, capped; v0 + address lookup table when
configured, because MINT/MERGE transactions exceed the 1232-byte legacy limit.
A confirmation worker batches getSignatureStatuses per tick. The pending
map and de-dupe set are snapshotted to local JSON on every change; restored
transactions are re-confirmed, never re-sent.
Markets API
Section titled “Markets API”Owns the markets table, serves it over REST, hosts create-market. The
single source of truth for the order manager’s books and the executor’s
(market_id, outcome_mint) resolution — the program has no on-chain token
registry.
Data flows
Section titled “Data flows”An order
Section titled “An order”Client ─POST /v1/orders──▶ Order Manager ──validate──▶ match │ │ ├──CreateOrder──▶ Ledger │ └──Execute──▶ Executor ──┘ │ build/sign/send ▼ Solana tx │ finalized│ ▼ Ledger UpdateTrade(CONFIRMED) │ ▼ Redis notifications streamOn failure: UpdateTrade(FAILED) plus OrderSizeUpdate back to the order
manager, which ejects the affected resting orders.
A parlay
Section titled “A parlay”User ──create_parlay tx──▶ high-market-parlay │ reads Market + MarketEventLink per leg │ locks probabilities, reserves exposure ▼ Helius webhook │ ▼ Platform API indexer ──▶ PostgreSQL ──▶ RESTOut-of-order delivery is handled by raising DeferredParlayEvent on a
LegSettled whose parent row hasn’t landed; the webhook answers 503 and
Helius retries.
Design decisions
Section titled “Design decisions”| Decision | Rationale |
|---|---|
| Off-chain matching | Latency and cost; on-chain matching at this granularity isn’t viable |
| On-chain settlement | Atomicity and non-custody; a match is money or it isn’t |
| Delegate allowance | Makers can be offline; no co-signing |
| No on-chain order state | Smaller program surface, cheaper transactions — at the cost of a trusted executor |
| Trait-abstracted backends | Every service testable in memory |
| Catalogue off-chain | No on-chain registry to maintain or pay for |
| Parlay reads, no CPI | Runtime decoupling; compile-time layout safety |
Frozen amm_program_id | Closes the malicious-AMM substitution vector |
| TWAP for parlay legs | A spot price is one slot of manipulation away |
Permissionless settle_leg | Outcome-determined, so there’s nothing to gain by racing |
Security boundaries
Section titled “Security boundaries”| Boundary | Control |
|---|---|
| Client → Order Manager | SIWS; sub is the authoritative owner |
| Executor → Order Manager | Shared X-Internal-Token, constant-time, fails closed |
Anyone → execute_trade | Config operator must sign |
| Maker tokens | Delegate allowance — the real exposure ceiling |
| Order authenticity | On-chain ed25519 verify against the recomputed hash |
| Replay | Executor trade-key de-dupe, persisted across restarts |
| Parlay account reads | Owner check + discriminator + compile-time layout pin |
| Admin operations | Squads multisig |
Deployment
Section titled “Deployment”| Piece | Runtime |
|---|---|
| Order Manager | Rust binary — CLOB_HTTP_ADDR, CLOB_GRPC_ADDR |
| Ledger | Rust binary + PostgreSQL + Redis |
| Executor | Rust binary + operator keypair + local snapshot files |
| Markets API | Rust binary + PostgreSQL |
| Platform API | Django + PostgreSQL + Celery |
| Programs | Solana, Anchor 0.32 |
The order manager boots with network backends by default. A self-contained demo:
CLOB_LEDGER_BACKEND=in_memory CLOB_EXECUTION_BACKEND=in_memory \CLOB_RISK_BACKEND=in_memory CLOB_MARKETS_BACKEND=in_memory \cargo run -p order-manager