# AMM correlated-pairs liquidity

Explore AMM liquidity on **Robinhood, Ethereum L1, and Base**. Every initial page and API refresh includes all three datasets. Switching chains is immediate; each chain retains its own observation timestamp, token identities, pools, and totals.

```bash
git clone https://amm-correlated-pairs-liquidity.ryan-berckmans.chatgpt.site/amm-correlated-pairs-liquidity.git
cd amm-correlated-pairs-liquidity
npm ci
npm run dev
```

Use Node 24. The baked data works without a key or database. Hosted live collection requires the Cloudflare D1 binding `DB`; keyless GeckoTerminal access is best effort. Local development without D1 shows the baked data.

## Collection and quotas

One D1 lease controls **all upstream traffic for the entire site**, across all visitors, chains, and Worker isolates. Each permitted request fetches one batch, saves it durably, and rotates to the next chain. A failed later batch never discards earlier successes. Complete datasets publish atomically; their timestamp is the **oldest batch observation**, accounting for upstream cache age. The browser never calls the provider directly.

| Mode | Total request ceiling | Pools per bulk call | Full sweep of 3 × 80 pools, best case | Maximum calls in 31 days |
| --- | --- | --- | --- | --- |
| Public GeckoTerminal | 3/min | 30 | 3 min | 133,920 |
| CoinGecko Demo | 1 per 5 min | 30 | 45 min | 8,928 |
| CoinGecko Basic | 2/min | 30 | 4.5 min | 89,280 |
| CoinGecko Analyst | 6/min | 50 | 1 min | 267,840 |

These are collector ceilings, not freshness promises. Requests are driven by active viewers; there is **no configured cron** in this Sites deployment. With no visitors, collection pauses. A scheduled caller can use the same public API and shares the same global budget. Network delays, provider caches, discovery sweeps, and cooldowns add latency. Minute-level collection across three chains requires the Analyst configuration, active polling, and available quota. Do not use an LLM automation as a high-frequency data scheduler.

The [public API specification](https://api.geckoterminal.com/docs/index.html) says approximately 10 calls/minute with traffic-dependent limits, while the [FAQ](https://apiguide.geckoterminal.com/faq) says 30. We use the conservative budget. HTTP 429/503 respects `Retry-After` and persists exponential cooldowns (1, 2, 4, 8, then 15 minutes minimum, or longer if instructed). Visitor traffic and chain switching cannot bypass them.

For independent quota, create a [CoinGecko API key](https://www.coingecko.com/en/api/pricing) and configure the Site's runtime settings:

- Secret `COINGECKO_PRO_API_KEY`; `COINGECKO_API_PLAN=analyst` for Analyst, or `basic` for Basic.
- Alternatively, secret `COINGECKO_DEMO_API_KEY`. The free plan's monthly allowance cannot sustain frequent three-chain updates.
- Deploy the runtime configuration. Keys stay in server request headers; they never enter HTML, public JSON, URLs, logs, or Git.

Published pricing/quotas can change; verify your actual account plan. Never set `analyst` on a Basic key: batch size and budget differ.

## Coverage and interpretation

Discovery scans four pages per chain, ranked by 24h pool volume: **up to 80 pools, not 80 tokens**. Every distinct asset in that sample is shown. Discovery is due every 15 minutes and runs within the same global budget; bulk refreshes update the currently discovered pool universe in between. Missing pool addresses trigger rediscovery instead of publishing a silently incomplete graph. The volume/liquidity toggle changes visual weights, not the universe. There is no liquidity floor or wash-volume filter.

Token identity includes the provider network and contract address. Native ETH's documented zero-address and `0xeeee…` protocol aliases are combined; WETH remains a separate wrapped asset. Other duplicate symbols keep their distinct addresses. Uniswap versions are grouped as Uniswap; Aerodrome versions as Aerodrome; pools remain deduplicated by ID.

Green edges denote the same inferred asset class, **not measured price correlation**. Categories use indexer names and symbols without issuer verification. Liquidity means indexed pool reserves, not chain-wide TVL. Network totals count a pool once; node totals count all incident pools and must not be summed. Visual weights use square roots and minimum visible sizes.

## API, storage, and rendering

`GET /api/liquidity` returns `chains.robinhood`, `chains.ethereum`, and `chains.base`, each with nodes, edges, venues, totals, coverage, updatedAt, stale, and collector status. Root-level fields mirror Robinhood for compatibility; `?chain=ethereum` or `?chain=base` changes that root projection while retaining all three datasets. Unknown chains return 400. The complete initial snapshot is in HTML and `/data/latest.json`.

The API waits at most 1.2 seconds for a collection step; `waitUntil` continues the bounded fetch. D1 stores both in-progress batches and completed snapshots. An isolated **named cache** serves responses for 10 seconds and HTML for five minutes. `caches.default` is intentionally never used in hosted code because Workers for Platforms disables it.

Browser polling is jittered, follows the collector's next-attempt time, pauses hidden tabs, and keeps older responses from replacing newer data. `LIVE` expires when the oldest observation exceeds three minutes; it does not imply streaming or block-by-block finality. HTTP 200 means useful data, not a new observation. Throttling is explicitly shown. `npm run data:snapshot` copies all three public datasets into the release without changing their original timestamps.

## Social previews and languages

The root and all eight language routes include server-rendered Open Graph metadata and X `summary_large_image`, pointing to the public branded `/og.png` card. The card names Robinhood, Ethereum, and Base. English, Chinese, Spanish, Japanese, Korean, French, Portuguese, and German are supported.

## Public source and release integrity

Read-only Git smart HTTP serves a prebuilt pack. `/source.git` and the previous repository name redirect to `/amm-correlated-pairs-liquidity.git`. Offline alternatives: `/amm-correlated-pairs-liquidity.bundle` and `/amm-correlated-pairs-liquidity.tar.gz`.

Every publication manually synchronizes the public repository first. Stage all source, run `npm run source:package -- "Release description"`, stage its outputs, commit, then build/publish. The build rejects source-digest mismatches. Hosting identity and secrets are excluded; file modes, deletions, and history are preserved. `/source.json` and the clone's `.source-version.json` expose the release digest. Recompute it with:

```bash
node --input-type=module -e 'import { sourceFiles, sourceDigest } from "./scripts/source-files.mjs"; console.log(sourceDigest(await sourceFiles(process.cwd())))'
```

To host a fork, configure your own hosting identity and canonical URLs, then package its source. Tests exercise real SQLite concurrency/backoff, three-chain isolation, bulk collection, native aliases, SSR/social metadata, and an actual Git clone/fetch/fsck against the built Worker.

MIT applies to application code. Data retains upstream terms. This project is independent of the chains and AMMs shown.
