You are migrating this codebase from the CoinGecko API to Codex (https://docs.codex.io). CoinGecko has two distinct API surfaces: a coin-ID-based market data API and a contract-address-based OnChain DEX API. Most of the OnChain DEX API maps cleanly to Codex; some of the market data API does not.
## Phase 1: Discovery (do this first, do not edit yet)
Search the codebase for every CoinGecko integration point. At minimum, look for:
- HTTP calls to `api.coingecko.com`, `pro-api.coingecko.com`, or `api.geckoterminal.com` (any path).
- Imports of any CoinGecko SDK (`coingecko-api-v3`, `@coingecko/coingecko-typescript`, `pycoingecko`, etc.) or GeckoTerminal client.
- Environment variables and config keys named `COINGECKO_*`, `CG_*`, `GECKOTERMINAL_*`.
- Header usage of `x-cg-pro-api-key` or `x-cg-demo-api-key`.
- Hardcoded coin-ID slugs (`"bitcoin"`, `"ethereum"`, `"solana"`, `"pepe"`, etc.) used to address CoinGecko endpoints.
- Network-slug mappings (`eth`, `polygon_pos`, `bsc`, `solana`) used in `/onchain` paths.
- Tests, fixtures, and mocks that reference any of the above.
Produce a Migration Plan with:
1. A grouped list of every call site, organized by CoinGecko endpoint.
2. The proposed Codex equivalent for each group (use the mapping below).
3. Any call sites you cannot map cleanly, flagged for human review.
4. The order you intend to make changes (shared client/config first, then leaf call sites, then tests).
5. New dependencies, env vars, and config you will introduce.
6. A list of every coin-ID slug used in the codebase that needs a contract-address mapping.
Stop and surface the plan before editing any source files. Wait for confirmation.
## Phase 2: Execution (after the plan is approved)
Ground rules:
1. Codex is a GraphQL API at `https://graph.codex.io/graphql`. Auth header is `Authorization: <api-key>` for long-lived keys, or `Authorization: Bearer <jwt>` for short-lived keys.
2. Prefer the official TypeScript SDK (`@codex-data/sdk`) for TS/JS projects. There is no official SDK for Python or other languages: for those, call raw GraphQL against `https://graph.codex.io/graphql`.
3. Network is a numeric parameter (`networkId`), not a URL path segment. Convert CoinGecko network slugs to Codex network IDs: `eth` → 1, `solana` → 1399811149, `base` → 8453, `bsc` → 56, `polygon_pos` → 137, `arbitrum` → 42161, `optimism` → 10, `avax` → 43114, `sui-network` → 101. For others, call `getNetworks` once and build a lookup.
4. Token IDs in Codex are the string `"<address>:<networkId>"`. Pair IDs use the same shape. Construct them explicitly.
5. Resolve coin-ID slugs to contract addresses once at startup (or call `filterTokens(phrase: ...)` at the call site). Store the resulting address+networkId in your config rather than carrying the slug through the codebase.
6. Where a CoinGecko integration hits two or three endpoints to fill one screen (for example `/coins/{id}` + `/coins/{id}/market_chart` + `/coins/{id}/tickers`), collapse them into a single GraphQL query.
7. For real-time data, replace polling loops with WebSocket subscriptions when the consumer is a long-lived client (dashboards, trading UIs) and webhooks when the consumer is a server endpoint (alerts, background workers, queues).
8. Preserve existing public function signatures, return shapes, and error semantics wherever possible. Internal helpers can be refactored freely.
9. Update tests as you change code. If a test relied on a CoinGecko response fixture, replace the fixture with a Codex equivalent rather than deleting the test.
10. When you hit a gap (CEX tickers, derivatives, NFTs, treasury holdings, global market cap aggregates, news, non-USD fiat conversions, hand-curated categories), do not silently drop the feature. Leave the call site intact, add a `TODO(migration):` comment with a one-line note explaining what's missing and what provider could fill it, and list it in your final report.
## CoinGecko → Codex endpoint mapping
OnChain DEX tokens (the most commonly migrated surface):
- `GET /onchain/networks/{network}/tokens/{address}` → `token`
- `GET /onchain/networks/{network}/tokens/multi/{addresses}` → `tokens`
- `GET /onchain/networks/{network}/tokens/{address}/info` → `token`
- `GET /onchain/simple/networks/{network}/token_price/{addresses}` → `getTokenPrices`
- `GET /onchain/networks/{network}/tokens/{address}/ohlcv/{timeframe}` → `getTokenBars`
- `GET /onchain/networks/{network}/tokens/{address}/trades` → `getTokenEvents` (pair-scoped: a token address resolves to its top pair; iterate the token's pairs or use `onTokenEventsCreated` for token-wide coverage across all pools)
- `GET /onchain/networks/{network}/tokens/{address}/top_holders` → `holders(input: { tokenId: "<address>:<networkId>" })` (default sort is holdings DESC)
- `GET /onchain/networks/{network}/tokens/{address}/top_traders` → `tokenTopTraders`
- `GET /onchain/networks/{network}/tokens/{address}/holders_chart` → partial via `onHoldersUpdated` (live only; flag if historical timeseries is required)
- `GET /onchain/tokens/info_recently_updated` → `filterTokens` ranked by recent activity (CoinGecko's path is cross-network, with no `networks/{network}` segment)
OnChain DEX (pools and pairs):
- `GET /onchain/networks/{network}/pools/{pool}` → `getDetailedPairStats(pairAddress: "0x...", networkId: <id>, durations: [...])` (top-level args, not `input: { pairId }`)
- `GET /onchain/networks/{network}/pools/multi/{addresses}` → `getDetailedPairsStats`
- `GET /onchain/networks/{network}/tokens/{address}/pools` → `listPairsForToken`
- `GET /onchain/networks/{network}/pools/{pool}/info` → `pairMetadata` (selects `exchangeId`, not `exchangeHash`)
- `GET /onchain/networks/{network}/pools/{pool}/trades` → `getTokenEvents` with pair filter
- `GET /onchain/networks/{network}/pools/{pool}/ohlcv/{timeframe}` → `getBars` (use `volume` field, not deprecated `v`)
- `GET /onchain/networks/{network}/pools` → `filterPairs(filters: { network: [<id>] })`
- `GET /onchain/networks/{network}/new_pools`, `/onchain/networks/new_pools` → `filterPairs` ranked by `createdAt` or subscribe to `onTokenLifecycleEventsCreated`
- `GET /onchain/networks/trending_pools`, `/onchain/networks/{network}/trending_pools` → `filterPairs(rankings: { attribute: trendingScore24, direction: DESC })`. `trendingScore24` is a ranking attribute, not a queryable result field.
- `GET /onchain/pools/trending_search` → `filterPairs(phrase: ..., rankings: { attribute: trendingScore24 })`
- `GET /onchain/networks/{network}/dexes/{dex}/pools` → `filterPairs` filtered by exchange
- `GET /onchain/pools/megafilter` → `filterPairs`
OnChain DEX (networks, dexes, search):
- `GET /onchain/networks` → `getNetworks`
- `GET /onchain/networks/{network}/dexes` → `filterExchanges`
- `GET /onchain/search/pools` → `filterPairs(phrase: ...)`
- `GET /onchain/categories`, `GET /onchain/categories/{id}/pools` → not supported (no curated DEX categories)
CoinGecko market data, coin-ID based (only relevant if the codebase hits these endpoints):
- `GET /simple/price?ids=...` → resolve IDs to addresses, then `getTokenPrices(inputs: [...])`. If the call uses `include_24hr_change`, `include_24hr_vol`, or `include_market_cap`, also call `filterTokens` and read `change24`, `volume24`, and `marketCap`/`circulatingMarketCap` from its results. `getDetailedTokenStats` provides bucketed volume/OHLC but does not expose market cap.
- `GET /simple/token_price/{platform}?contract_addresses=...` → `getTokenPrices(inputs: [...])` (no slug resolution needed). Same caveat for `include_*` flags.
- `GET /coins/{id}` → resolve ID first, then `token` + `getDetailedTokenStats` (+ `filterTokens` for `marketCap`/`circulatingMarketCap`, which neither of the first two exposes)
- `GET /coins/{id}/market_chart`, `/market_chart/range`, `/ohlc`, `/ohlc/range` → `getTokenBars`
- `GET /coins/{id}/contract/{address}/market_chart`, `/market_chart/range` → `getTokenBars` (already contract-addressed)
- `GET /coins/{id}/history` → `getBars` with a single bar covering the date
- `GET /coins/markets` → `filterTokens(rankings: ..., filters: ...)`
- `GET /coins/list`, `GET /token_lists/{asset_platform_id}/all.json` → `filterTokens` at point of use; drop the local coin list
- `GET /coins/list/new` → `filterTokens(rankings: { attribute: createdAt, direction: DESC })` or `onTokenLifecycleEventsCreated`
- `GET /coins/top_gainers_losers` → `filterTokens(rankings: { attribute: change24, direction: DESC })` (use `change24`; `priceChange24` is the pair-side analogue and is not valid on `TokenRankingAttribute`)
- `GET /coins/{id}/tickers` → `listPairsForToken` / `listPairsWithMetadataForToken` (DEX only; flag CEX)
- `GET /coins/{id}/contract/{address}` → `token`
- `GET /coins/{id}/circulating_supply_chart`, `/total_supply_chart` (+ `/range`) → not supported (no historical supply timeseries; current supply on `token.info`)
- `GET /search?query=...` → `filterTokens(phrase: "$SYMBOL", ...)`
- `GET /search/trending` → `filterTokens(rankings: { attribute: trendingScore24, direction: DESC })`. Tokens only — flag NFT and category results as gaps.
- `GET /exchange_rates`, `/simple/supported_vs_currencies` → not supported (USD only)
- `GET /coins/categories`, `/coins/categories/list` → not supported (no curated categories)
Real-time (polling → Codex subscription):
- Polled `/simple/price`, `/simple/token_price` → `onPriceUpdated` / `onPricesUpdated`
- Polled `/onchain/.../ohlcv` → `onBarsUpdated` / `onTokenBarsUpdated`
- Polled token-stats endpoints → `onDetailedTokenStatsUpdated`
- Polled pair-stats endpoints → `onDetailedStatsUpdated`
- Polled trades → `onTokenEventsCreated` / `onEventsCreated`
- Polled holders → `onHoldersUpdated`
- Polled `new_pools` → `onTokenLifecycleEventsCreated` / `onLaunchpadTokenEvent`
Gaps (flag, do not drop):
- `/coins/{id}/tickers` for CEX exchanges: Codex is onchain-only
- `/exchanges`, `/exchanges/list`, `/exchanges/{id}`, `/exchanges/{id}/tickers`, `/exchanges/{id}/volume_chart`, `/derivatives*`: not supported
- `/nfts/*` and NFT/category buckets in `/search/trending`: Codex is a fungible-token API
- `/entities/list`, `/public_treasury/*`, legacy `/companies/public_treasury/*`: not supported
- `/global`, `/global/decentralized_finance_defi`, `/global/market_cap_chart`: Codex does not aggregate global market totals
- `/coins/{id}/circulating_supply_chart`, `/total_supply_chart` (and `/range` variants): no historical supply timeseries (current supply available on `token.info`)
- `/news`: not supported
- `/exchange_rates`, non-USD `vs_currencies`: Codex returns USD only
- `/coins/categories`, `/coins/categories/list`, `/onchain/categories`, `/onchain/categories/{id}/pools`: Codex does not curate token or pool categories
When you need details on any Codex field, fetch the reference page at `https://docs.codex.io/api-reference/queries/<name>` (or `subscriptions`, `mutations`) rather than guessing. Before migrating any real-time code, fetch `https://docs.codex.io/concepts/subscriptions` and `https://docs.codex.io/concepts/webhooks` so you pick the right delivery mechanism.
## Phase 3: Final report
When the migration is done, produce a single report with:
1. Files changed, grouped by area (client/config, call sites, tests, docs).
2. Every `TODO(migration):` you added, with file path, line, and the reason.
3. The coin-ID → contract-address mapping you ended up with, so the human can audit it.
4. New env vars and dependencies, with the line to add to `.env.example` and the package manager command to install.
5. CoinGecko integrations that were removed entirely, and what replaced them.
6. A short manual-verification checklist the human should run before merging (which features to click through, which endpoints to spot-check, which dashboards to load).
Run the project's linter and test suite before declaring the migration complete. If tests fail, fix the underlying integration, do not weaken the test.