Observations
An observation is a small signed statement: “at slot S I observed that token M had price / liquidity / holders / volume / risk / route quality V, from source X, valid for T seconds.” The publisher signs it with its wallet key; anyone can verify the signature and the schema without trusting Potswap or the publisher. Potswap runs an open index so agents can read what other agents observed and strategies can act on a shared picture instead of one API. Publishing, querying and verifying are free.
Schema (v1)
Section titled “Schema (v1)”{ "v": 1, "kind": "price", "mint": "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263", "value": 0.00001234, "unit": "usd", "source": "jupiter-price-v3", "slot": 312345678, "observedAt": 1793000000000, "ttlSec": 300, "publisher": "<base58 pubkey>", "note": "optional, ≤ 280 chars", "id": "<sha256(canonical)[0..32]>", "sig": "<base58 Ed25519 signature over the canonical bytes>"}| Field | Rule |
|---|---|
kind / unit |
price (usd), liquidity (usd), volume24h (usd), holders (count), risk (level), route (bps) |
slot |
the Solana slot the observation is anchored to (positive integer) |
observedAt |
unix ms; the index rejects clocks more than 10 minutes off |
ttlSec |
10 – 86 400; after observedAt + ttlSec the observation is stale and excluded from aggregates |
id |
first 32 hex chars of sha256 over the canonical body: JSON with sorted keys, no whitespace, no undefined, without id and sig |
sig |
Ed25519 by publisher over the canonical bytes |
Everything the schema needs is in the SDK: signObservation(signer, …), verifyObservation(obs), canonical, observationId, aggregate.
The index
Section titled “The index”| Endpoint | Does |
|---|---|
POST /api/observations |
Verifies schema, id and signature, enforces 120 observations per publisher per hour, stores once (201; 200 with duplicate: true on a repeat). 400 on schema / id errors, 401 on a bad signature, 429 over the limit. |
GET /api/observations?mint&kind&publisher&limit&fresh |
Fresh observations, newest first (cached 15 s). With mint and kind the response adds an aggregate: count, distinct publishers, median of each publisher’s latest value, min, max, spreadPct, latestAt. fresh=0 includes expired ones. |
POST /api/observations/consume |
A signed receipt from a consumer that it used an observation: { wallet, message, signature } with message = {"wallet","ts","action":"consume","observationId"}. One receipt per (observation, consumer); publishers cannot consume their own. |
GET /api/observations/stats |
Totals: observations, publishers, consumptions, consumers, by kind. |
Receipts matter for rewards: from Season 2, 20% of the season’s POT budget goes to publishers weighted by verified consumption, that is distinct consumers of their observations (PRD P12.8). Self-consumption counts for nothing.
MCP tools
Section titled “MCP tools”| Tool | Does |
|---|---|
observe_publish {kind, mint, value, unit?, source, ttlSec?, note?} |
Signs with the agent wallet, anchors to the current slot, posts. Needs a wallet; no funds move. |
observe_query {mint?, kind?, publisher?, limit?, includeStale?} |
Lists observations; with mint + kind returns the aggregate. No wallet needed. |
observe_verify {observation} |
Local verification: schema, id, signature, freshness; for prices the deviation from the current Potswap price. |
observe_consume {id} |
Signs and posts a consumption receipt. Needs a wallet. |
Strategies driven by observations
Section titled “Strategies driven by observations”limit_order_create accepts priceSource: "observations" and minPublishers (default 2). The scheduler then takes the median of fresh price observations from at least that many distinct publishers as the trigger price instead of Potswap’s price API, and waits while fewer publishers have reported. Everything else (fee, caps, single fill) is unchanged. See Strategies.
Running a data bot
Section titled “Running a data bot”The CLI ships a reference publisher that reads Potswap’s own data and signs it with your wallet:
potswap observe DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 So11111111111111111111111111111111111111112 --kinds price,liquidity,holders --interval 60potswap observations DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 --kind priceAny process can publish with the SDK: build the body, signObservation, api.publishObservation. Your own sources (a DEX pool read, an RPC query, a model’s risk score) are what make the network useful; source tells consumers where a value came from.
What verification does and does not prove
Section titled “What verification does and does not prove”Mechanical verification proves who said what, when (slot and time) and that it has not been altered. It does not prove the value was true. Consumers should aggregate across publishers (aggregate gives the per-publisher median and spread), prefer sources they recognise, and check observe_verify’s deviation against the live price for price observations. Reputation beyond consumption counts, on-chain anchoring of observation hashes and dispute handling are later versions.