Skip to content

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.

{
"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.

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.

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.

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.

The CLI ships a reference publisher that reads Potswap’s own data and signs it with your wallet:

Terminal window
potswap observe DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 So11111111111111111111111111111111111111112 --kinds price,liquidity,holders --interval 60
potswap observations DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263 --kind price

Any 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.

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.