Oracle Integration (Switchboard)
Astromarkets uses Switchboard (Surge / Pull Feed) as its result data source.
What is Switchboard
Switchboard is a decentralized oracle protocol in the Solana ecosystem — similar in role to Pyth or Chainlink, but with two properties that make it a natural fit for prediction markets:
- Permissionless custom feeds: anyone can define a data source as a task graph — freely composing HTTP fetches, JSON parsing, conditional logic, and more — rather than being limited to officially maintained price pairs. Prediction-market settlement conditions come in every shape ("Is BTC above X on date Y?", "Who won the match?"), which is exactly the flexibility this provides;
- Pull-based feeds: data is not continuously pushed on-chain. Instead, when a value is needed (e.g. at settlement), a user or keeper requests a signed quote from the oracle nodes and submits it on-chain together with the signature for verification. You only pay for the data you actually use, and every reading carries a slot-based freshness guarantee.
Surge is Switchboard's low-latency quote channel: oracle nodes run the task graph inside a TEE (Trusted Execution Environment) and sign the result with Ed25519; the on-chain program accepts the value only after QuoteVerifier validates the signature — data authenticity is guaranteed by cryptography rather than trust in a centralized server.
To learn more about Switchboard, see the official website and documentation.
Value Convention
Every market's feed must output one of three values:
| Feed value | Meaning |
|---|---|
1 × 10¹⁸ | Yes — the event happened |
0 | No — the event did not happen |
2 × 10¹⁸ | Invalid — value error / undeterminable |
Source Locking & Verification
- Locked at creation:
CreateMarketwrites thefeed_id(Switchboard feedHash) into the market account and verifies once on the spot that the value format is legal (∈{0, 1e18, 2e18}); non-conforming feeds can't create markets; - Verified at settlement:
SubmitMarketResultrequires:- the quote account address derives from
(queue, feed_id)— only the source locked at creation is accepted; - the quote signature is verified (Surge path);
- slot freshness — no settling on stale data.
- the quote account address derives from
The same transaction must first run Switchboard's Ed25519 verify + verified_update instructions before SubmitMarketResult.
Feed Creation Workflow
Creating a settlement-ready feed takes five steps:
- Translate the market title into a decidable condition — which source to read, at what moment, compared how. The condition must be objective and unambiguous: "Will BTC go up by year end?" cannot settle; "Is the Coinbase BTC-USD spot price ≥ 150,000 at 2026-12-31 00:00 UTC?" can;
- Design the task graph — compose Switchboard tasks into a pipeline: fetch (
httpTask) → parse (jsonParseTask) → conditional mapping (comparison/conditional tasks), finally outputting1/0/2(read on-chain as 18-decimal fixed point, i.e.1e18/0/2e18per the table above); - Simulate — dry-run the task graph through Crossbar's simulation endpoint (the same Simulate Feed described in Transparency Tools) and confirm the current output is what you expect;
- Deploy the feed — obtaining the
feed_id; - Pass the
feed_idwhen creating the market —CreateMarketverifies one live reading on the spot and rejects malformed feeds.
The frontend's creation wizard embeds a Feed Task Library for visual task-graph configuration and one-click deployment — all five steps happen inside the wizard; see Creating a Market.
Three Rules for Task-Graph Design
- Output only 1 / 0 / 2 — never emit a raw price or any other number; the final step must map the result onto these three values;
- Aggregate multiple sources against manipulation — for manipulable data (prices especially), read several independent sources and take the
medianTaskmedian, so no single outlier can swing the result; - Fail into Invalid (2), not into an error — if the API is down or a field is missing, have the graph output
2. An Invalid value never settles (see When the Oracle Is Wrong) and can be retried after a fix, whereas a feed that simply errors cannot complete settlement.
Example 1: Price-Threshold Market
Market: "Is the BTC price ≥ 150,000 USD at 2026-12-31 00:00 UTC?"
Note that the decision moment is fixed, so a "current spot price" endpoint is wrong — settlement happens after expiry, and it would read the price at settlement time rather than at the moment the market specifies. Use a historical kline endpoint that accepts a timestamp instead, e.g. Binance's /api/v3/klines: pass startTime=1798675200000 (the millisecond timestamp of 2026-12-31 00:00:00 UTC) and take that minute candle's open price — the price at exactly that moment. Whenever the feed is pulled, it returns the same historical record: deterministic and reproducible.
{
"tasks": [
{
"comparisonTask": {
"op": "OPERATION_GT",
"lhs": {
"tasks": [
{
"httpTask": {
"url": "https://api.binance.com/api/v3/klines?symbol=BTCUSDT&interval=1m&startTime=1798675200000&limit=1"
}
},
{ "jsonParseTask": { "path": "$[0][1]" } }
]
},
"rhs": { "tasks": [{ "valueTask": { "value": 150000 } }] },
"onTrue": { "tasks": [{ "valueTask": { "value": 1 } }] },
"onFalse": { "tasks": [{ "valueTask": { "value": 0 } }] },
"onFailure": { "tasks": [{ "valueTask": { "value": 2 } }] }
}
}
]
}
Binance klines return [[openTime, open, high, low, close, …]], so $[0][1] extracts the open price at 00:00:00 sharp → compare against 150,000 → output 1 (Yes) if above, 0 (No) otherwise, and 2 (Invalid) if the fetch fails. The market's expiry (e.g. 2026-12-31 00:05 UTC) just needs to be after the decision moment; once the candle exists, settlement can run at any time.
Example 2: Multi-Source Median (Manipulation-Resistant)
Relying on a single exchange means its downtime or an outlier price skews the result. Replace the lhs with the median of several independent sources and no single source can swing it. The snippet below uses three exchanges' spot endpoints to show the shape (for a fixed decision moment, swap each source for its historical-kline equivalent, same idea as Example 1):
{
"lhs": {
"tasks": [
{
"medianTask": {
"jobs": [
{ "tasks": [
{ "httpTask": { "url": "https://api.coinbase.com/v2/prices/BTC-USD/spot" } },
{ "jsonParseTask": { "path": "$.data.amount" } }
]},
{ "tasks": [
{ "httpTask": { "url": "https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT" } },
{ "jsonParseTask": { "path": "$.price" } }
]},
{ "tasks": [
{ "httpTask": { "url": "https://api.kraken.com/0/public/Ticker?pair=XBTUSD" } },
{ "jsonParseTask": { "path": "$.result.XXBTZUSD.c[0]" } }
]}
]
}
}
]
}
}
Example 3: Non-Price Event (Official Data)
Market: "Is US CPI YoY ≥ 3.0% for July 2026?" — same pattern, pointed at an authoritative statistics API, comparing the officially published number:
{
"tasks": [
{
"comparisonTask": {
"op": "OPERATION_GT",
"lhs": {
"tasks": [
{ "httpTask": { "url": "https://api.bls.gov/publicAPI/v2/timeseries/data/CUUR0000SA0?latest=true" } },
{ "jsonParseTask": { "path": "$.Results.series[0].data[0].calculations.pct_changes.12" } }
]
},
"rhs": { "tasks": [{ "valueTask": { "value": 3.0 } }] },
"onTrue": { "tasks": [{ "valueTask": { "value": 1 } }] },
"onFalse": { "tasks": [{ "valueTask": { "value": 0 } }] },
"onFailure": { "tasks": [{ "valueTask": { "value": 2 } }] }
}
}
]
}
Sports results, election outcomes and the like work the same way: find an authoritative API with structured results, parse out the winner field, and compare it with OPERATION_EQ against the target value.
Example 4: Private Data Source Requiring an API Key
Many high-quality sources (sports data, financial terminals, weather services) require an API key on every request. Never write the key into the task graph in plaintext — feed definitions are publicly readable through Crossbar (see Transparency Tools), so a plaintext key is an instant leak.
The right way is Switchboard Secrets: the key is stored encrypted and can only be decrypted by oracle nodes running inside a TEE; the task graph references it with a ${VARIABLE} placeholder.
Market: "Did the home team win a given NBA game?" (the sports data API requires a key):
{
"tasks": [
{ "secretsTask": { "authority": "<your-wallet-pubkey>" } },
{
"comparisonTask": {
"op": "OPERATION_EQ",
"lhs": {
"tasks": [
{
"httpTask": {
"url": "https://api.sportsdata.example/v3/nba/scores/GAME_ID",
"headers": [
{ "key": "Ocp-Apim-Subscription-Key", "value": "${SPORTSDATA_API_KEY}" }
]
}
},
{ "jsonParseTask": { "path": "$.HomeTeamWon" } }
]
},
"rhs": { "tasks": [{ "valueTask": { "value": 1 } }] },
"onTrue": { "tasks": [{ "valueTask": { "value": 1 } }] },
"onFalse": { "tasks": [{ "valueTask": { "value": 0 } }] },
"onFailure": { "tasks": [{ "valueTask": { "value": 2 } }] }
}
}
]
}
How it works:
- Store the secret first — upload
SPORTSDATA_API_KEYencrypted via the Switchboard Secrets SDK/CLI, bound to your wallet (thesecretsTask.authorityin the graph); - The graph only contains a placeholder —
${SPORTSDATA_API_KEY}is publicly visible, but the real key is decrypted and injected only inside the oracle node's TEE; nobody (including node operators) ever sees the plaintext; - Auditability is preserved — traders still see the full URL, parse path and decision logic; only the key itself is hidden.
Note: when simulating a feed that uses secrets, the public simulation endpoint cannot inject your key and may return a failure — test with Switchboard's secrets-aware simulation before deploying.
These task graphs show the design approach; for exact task fields, refer to the Switchboard documentation and the built-in Feed Task Library templates. Always Simulate before deploying.
Transparency Tools
The trading page's Oracle tab provides:
- the Feed ID linking to the Switchboard Explorer;
- Feed JSON / Task Graph: the feed definition fetched via Crossbar (
crossbar.switchboardlabs.xyz), exposing the full data-source logic; - Simulate Feed: runs the task graph live through Crossbar's simulation endpoint, previewing "if we settled now, what would the result be".
Any trader can fully audit a market's settlement source before trading — something centralized oracle setups can't offer.
When the Oracle Is Wrong
The oracle is only the first of three resolution layers:
- malformed value → result Invalid, never settles, retry after fixing;
- "valid but wrong" value (e.g. manipulated source) → corrected by dispute + voting;
- voting still wrong → DAO M-of-N review as the backstop.
Before trading, always audit the data source yourself via the Oracle tab:
-
does the task-graph logic actually match the market title
-
is the data source (API/website) reliable
-
does the
Simulateresult match expectations
Do not trade on data sources that look suspicious or that you cannot understand!