Skip to content

feat: add GET /snapshot whole-network summary - #395

Merged
Miracle656 merged 5 commits into
Divinity-Labs:mainfrom
aliyuHabibu:feat/snapshot-endpoint
Oct 19, 2026
Merged

Miracle656 merged 5 commits into
Divinity-Labs:mainfrom
aliyuHabibu:feat/snapshot-endpoint

Conversation

@aliyuHabibu

Copy link
Copy Markdown
Contributor

One cached, network-scoped call that answers "what does this network look like right now": every venue's liveness, AMM liquidity (per-asset reserves and per-pool detail), 24h traded volume, the busiest pairs with their price direction, stablecoin peg readings, and data freshness — as a single timestamped object.

Everything is read in one SQL statement with a single as_of clock taken from the database, so every 24h cutoff and every ageSeconds is measured from the same instant rather than four queries landing on four clocks. Unknowns are reported, never invented: a venue with no rows is silent, a cross-pair volume total is null with a reason unless the pairs share one unit, a peg with no fiat-target quote is no_reference, and the endpoint always returns 200. Asset identities keep their issuers.

Cached per network for a few seconds (SNAPSHOT_CACHE_TTL), with cache.ageSeconds republished on a hit so a cached copy never claims to be fresher than it is. Public, and not x402-gated.

Summary

Type of change

  • Bug fix
  • New feature
  • Refactor
  • Docs
  • Tests
  • CI / tooling

Checklist

  • I have read CONTRIBUTING.md
  • npx tsc --noEmit passes
  • npm run build passes
  • I added / updated tests where relevant
  • I updated docs where relevant

Close #299

One cached, network-scoped call that answers "what does this network look
like right now": every venue's liveness, AMM liquidity (per-asset reserves
and per-pool detail), 24h traded volume, the busiest pairs with their price
direction, stablecoin peg readings, and data freshness — as a single
timestamped object.

Everything is read in one SQL statement with a single `as_of` clock taken
from the database, so every 24h cutoff and every `ageSeconds` is measured
from the same instant rather than four queries landing on four clocks.
Unknowns are reported, never invented: a venue with no rows is silent, a
cross-pair volume total is null with a reason unless the pairs share one
unit, a peg with no fiat-target quote is `no_reference`, and the endpoint
always returns 200. Asset identities keep their issuers.

Cached per network for a few seconds (SNAPSHOT_CACHE_TTL), with
`cache.ageSeconds` republished on a hit so a cached copy never claims to be
fresher than it is. Public, and not x402-gated.
@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

@aliyuHabibu Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Miracle656

Copy link
Copy Markdown
Collaborator

Needs a rebase — main moved six times this morning (#379, #380, #382, #392, #401, #310 all landed) and this now conflicts. Push a rebase and I will pick it up; nothing is queued ahead of it.

@aliyuHabibu

Copy link
Copy Markdown
Contributor Author

@Miracle656 Please review again. Thank you

@Miracle656

Copy link
Copy Markdown
Collaborator

Needs a rebase — main has moved a lot today (#306, #309, #252, #256, #355, #381, #390, #402 all landed) and this now conflicts.

Worth knowing before you rebase: #402 replaced every console.* call with a pino logger, so if this PR adds logging, express it as logger.* on a component child rather than console.* — there is a test that fails the build on a stray console call in non-test source.

Push the rebase and I will pick it up; nothing is queued ahead of it.

@aliyuHabibu

Copy link
Copy Markdown
Contributor Author

@Miracle656 Done

Merging current main produced two import lines from './config' in
src/redis.ts — this branch's, which also pulls in `type NetworkName`, and
main's narrower one:

    error TS2300: Duplicate identifier 'config'.
    error TS2300: Duplicate identifier 'activeNetwork'.

Kept the superset. tsc clean, 696 tests passing.

Claude-Session: https://claude.ai/code/session_01USgemLt4Rnz4SGB1Srf3GB

@Miracle656 Miracle656 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header comment on src/routes/snapshot.ts is the best statement of design intent in this repo, and every rule in it is honoured by the code.

One query, one clock.

Everything is read in ONE SQL statement with a single as_of timestamp taken from the database. Four separate queries could land on four different clocks, and then the response would describe an instant that never existed.

I counted: exactly one pgPool.query. That is the difference between a snapshot and a collage. Four round-trips would give you TVL from one moment, volume from another, and a peg reading from a third — and the composite would look perfectly consistent while describing no instant that ever occurred. Taking as_of from the database rather than Date.now() is the other half: the app's clock and the database's are not the same clock.

Absent data is reported, never invented. A silent venue is listed as silent; a pair with no trades gets no volume figure rather than a zero; the peg check says no_reference; a cross-pair total is null with a reason when the pairs do not share a unit. For a summary endpoint this is the whole ballgame — a dashboard cannot tell a real zero from a missing one, and "0" renders as confidently as any other number.

The cache reports its own age.

ageSeconds: storedAt ? ageSeconds(Date.now(), storedAt) : null

a hit never pretends to be fresher than it is

This is the detail almost everyone misses. Caching a response that contains its own freshness fields and serving it unchanged means the response lies for the length of the TTL — it says "as of now" while being seconds old. Recomputing cache.ageSeconds on the way out, with an X-Cache: HIT header, keeps it honest.

Always 200. "No data yet" is state, not a client error, and a summary that 404s is useless to the dashboard that polls it. Agreed — and it pairs correctly with the point above, because the way you say "nothing here" is in the body, not the status.

And issuers are never elided, because a coin called USDC is a different asset per issuer. That rule exists here for a reason and you did not need telling.

I pushed one commit to your branch. The merge with current main left two import lines from ./config in src/redis.ts — yours (which also pulls type NetworkName) and main's narrower one — so tsc refused with TS2300: Duplicate identifier. Kept the superset.

src/__tests__/snapshot.test.ts: 17 passed. Suite: 696 passed, tsc --noEmit clean. The single remaining local failure is graphqlSchemaDrift from CRLF in my working copy — git diff shows the committed blob untouched, and CI passes it.

Merging once CI confirms.

@Miracle656
Miracle656 merged commit 7d187ea into Divinity-Labs:main Oct 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

There is no snapshot anyone can cite

2 participants