Conversation
WalkthroughThis PR removes fixed chain-id usage, resolves chain IDs from configuration, and threads chain-aware filtering through swap, orders, trades, tokens, and vault routes. It also updates OpenAPI metadata, request/response types, caching, and tests. ChangesChain-ID Multi-chain Support
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant Routes as route handler
participant Helpers as resolve_required_chain_id
participant YAML as RaindexYaml
participant DataSource as Raindex data source
participant Response
Client->>Routes: request with optional chainId
Routes->>Helpers: resolve or validate chain_id
Helpers->>YAML: read configured networks
YAML-->>Helpers: configured chain ids
Helpers-->>Routes: resolved chain_id or filter
Routes->>DataSource: query with chain_ids / chain_id
DataSource-->>Routes: filtered results
Routes-->>Response: JSON with chain-aware fields
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/routes/mod.rs (1)
15-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd direct unit tests for the new chain-id resolution helpers.
configured_chain_ids,validate_chain_id,resolve_required_chain_id, andoptional_chain_ids_filterare pure functions that gate 400 vs. 500 behavior across every swap and vault endpoint, but no tests target them directly in this file. Existing swap/vault tests hardcodechain_id: Some(8453)and bypass this resolution logic entirely, so edge cases (zero configured networks, multiple configured networks, unsupported requested chain) aren't exercised.Consider adding a
#[cfg(test)]module here covering:
resolve_required_chain_id: no chain requested + exactly one configured network →Ok.resolve_required_chain_id: no chain requested + zero configured networks →Internal.resolve_required_chain_id: no chain requested + multiple configured networks →BadRequest.validate_chain_id/optional_chain_ids_filter: unsupported chain id →BadRequest.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/routes/mod.rs` around lines 15 - 70, Add direct unit tests in this module for the chain-id helper functions `configured_chain_ids`, `validate_chain_id`, `resolve_required_chain_id`, and `optional_chain_ids_filter`, since they control the 400 vs 500 behavior. Create a `#[cfg(test)]` module that exercises `resolve_required_chain_id` with no requested chain and exactly one configured network, zero configured networks, and multiple configured networks, and verify the expected `Ok`, `Internal`, and `BadRequest` results. Also add coverage for `validate_chain_id` and `optional_chain_ids_filter` with an unsupported chain id to confirm they return `BadRequest`.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/routes/mod.rs`:
- Around line 15-70: Add direct unit tests in this module for the chain-id
helper functions `configured_chain_ids`, `validate_chain_id`,
`resolve_required_chain_id`, and `optional_chain_ids_filter`, since they control
the 400 vs 500 behavior. Create a `#[cfg(test)]` module that exercises
`resolve_required_chain_id` with no requested chain and exactly one configured
network, zero configured networks, and multiple configured networks, and verify
the expected `Ok`, `Internal`, and `BadRequest` results. Also add coverage for
`validate_chain_id` and `optional_chain_ids_filter` with an unsupported chain id
to confirm they return `BadRequest`.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1f2e9184-28f3-423e-a8c7-c2b6d2f425bf
📒 Files selected for processing (8)
src/main.rssrc/routes/mod.rssrc/routes/swap/calldata.rssrc/routes/swap/mod.rssrc/routes/swap/quote.rssrc/routes/vaults.rssrc/types/swap.rssrc/types/vaults.rs
💤 Files with no reviewable changes (1)
- src/main.rs
cfbffc3 to
e22859b
Compare
7c99f00 to
d1d967f
Compare
d1d967f to
afd85b9
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (4)
src/routes/swap/calldata.rs (1)
118-118: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
unwrap_or_default()inFromimpls produceschain_id = 0, always overwritten.Both
From<SwapCalldataRequest>andFrom<SwapCalldataV2Request>setchain_id: req.chain_id.unwrap_or_default(), which yields0whenchain_idisNone. This value is immediately overridden inprocess_swap_calldata/process_swap_calldata_v2(lines 179, 189). While correct today,0is not a valid chain ID — if the override were accidentally removed in a future refactor, the bug would be silent. Consider adding a brief comment or using a sentinel likeu32::MAXto make the placeholder intent explicit.Also applies to: 133-133, 150-150
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/routes/swap/calldata.rs` at line 118, The `From<SwapCalldataRequest>` and `From<SwapCalldataV2Request>` impls currently use `unwrap_or_default()` for `chain_id`, which silently sets an invalid placeholder value of 0 before `process_swap_calldata` and `process_swap_calldata_v2` overwrite it. Update these conversions to make the placeholder intent explicit, either by using a clear sentinel value or adding a brief comment рядом to the `chain_id` assignment, and ensure the `process_swap_calldata`/`process_swap_calldata_v2` flow remains the only place that injects the real chain ID.src/routes/trades/get_by_tx.rs (1)
273-277: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd one assertion that
chain_idsreaches the tx data source.The tx tests all pass
None, so dropping the new filter would still pass. Mirror the taker/order-hash tests by capturing_chain_idsinMockTradesDataSourceand assertingSome(vec![8453])in one process test.Also applies to: 336-455
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/routes/trades/get_by_tx.rs` around lines 273 - 277, The tx path is not verifying that the new chain filter is actually forwarded into the data source. Update the `get_trades_by_tx` flow in `MockTradesDataSource` to capture `_chain_ids`, then add an assertion in one process test that the tx lookup receives `Some(vec![8453])`, mirroring the existing taker/order-hash test pattern so the filter can’t be dropped silently.src/routes/orders/get_by_token.rs (1)
20-23: 📐 Maintainability & Code Quality | 🔵 TrivialGrowing parameter list on
process_get_orders_by_token.Chain_ids pushed this function to 8 positional args, requiring a new
#[allow(clippy::too_many_arguments)]. Consider bundlingaddress/state/side/page/page_size/denomination(and nowchain_ids) into a small request struct to keep the signature maintainable as more filters get added.Also applies to: 59-65
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/routes/orders/get_by_token.rs` around lines 20 - 23, The signature of process_get_orders_by_token has grown too large, forcing a too_many_arguments allowance and making it harder to extend. Refactor this handler to accept a small request/filter struct that groups address, state, side, page, page_size, denomination, and chain_ids, then update the call sites and any internal references in process_get_orders_by_token to use that struct instead of many positional parameters.src/routes/trades/get_by_order_hashes.rs (1)
51-63: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winRead lock held across the whole request despite cloning the client.
raindex(theSharedRaindexProviderread guard) isn't scoped into a block, so it isn't dropped until the end of theasync moveblock — i.e., afterprocess_get_trades_by_order_hashes(...).awaitcompletes on line 59 — even thoughclientis already an owned clone. This holds the read lock for the entire query duration (hash parsing, data-source query, DB access), unlikeget_by_taker.rs/get_by_token.rsin this same PR, which explicitly scope the guard in a nested block to drop it immediately after cloning the client.♻️ Suggested fix: scope the guard like sibling handlers
- let raindex = shared_raindex.read().await; - let chain_ids = - crate::routes::optional_chain_ids_filter(raindex.raindex_yaml(), request.chain_id)?; - let client = raindex.client().clone(); + let (client, chain_ids) = { + let raindex = shared_raindex.read().await; + let chain_ids = crate::routes::optional_chain_ids_filter( + raindex.raindex_yaml(), + request.chain_id, + )?; + (raindex.client().clone(), chain_ids) + }; let ds = RaindexTradesDataSource { client: &client, pool: pool.inner(), }; process_get_trades_by_order_hashes(&ds, chain_ids, request).await🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/routes/trades/get_by_order_hashes.rs` around lines 51 - 63, The SharedRaindexProvider read guard in get_by_order_hashes is held for the full async request because raindex stays in scope until process_get_trades_by_order_hashes(...).await finishes. Scope the read lock inside a nested block, clone the client there, and extract chain_ids before the block ends so the guard is dropped immediately after the needed data is copied. Mirror the pattern used in get_by_taker and get_by_token, keeping RaindexTradesDataSource and process_get_trades_by_order_hashes as the key call sites to update.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/main.rs`:
- Around line 177-187: The v2-mounted route groups in main::mount currently
reuse the v1 route tables, so the OpenAPI generator will not emit distinct /v2
paths for tokens, order, orders, vaults, and trades. Update the relevant route
modules and their routes_v2() handlers to add matching #[utoipa::path(...)]
annotations for the v2 endpoints, and extend the existing path assertions/tests
to verify the /v2 paths are present alongside the v1 ones.
---
Nitpick comments:
In `@src/routes/orders/get_by_token.rs`:
- Around line 20-23: The signature of process_get_orders_by_token has grown too
large, forcing a too_many_arguments allowance and making it harder to extend.
Refactor this handler to accept a small request/filter struct that groups
address, state, side, page, page_size, denomination, and chain_ids, then update
the call sites and any internal references in process_get_orders_by_token to use
that struct instead of many positional parameters.
In `@src/routes/swap/calldata.rs`:
- Line 118: The `From<SwapCalldataRequest>` and `From<SwapCalldataV2Request>`
impls currently use `unwrap_or_default()` for `chain_id`, which silently sets an
invalid placeholder value of 0 before `process_swap_calldata` and
`process_swap_calldata_v2` overwrite it. Update these conversions to make the
placeholder intent explicit, either by using a clear sentinel value or adding a
brief comment рядом to the `chain_id` assignment, and ensure the
`process_swap_calldata`/`process_swap_calldata_v2` flow remains the only place
that injects the real chain ID.
In `@src/routes/trades/get_by_order_hashes.rs`:
- Around line 51-63: The SharedRaindexProvider read guard in get_by_order_hashes
is held for the full async request because raindex stays in scope until
process_get_trades_by_order_hashes(...).await finishes. Scope the read lock
inside a nested block, clone the client there, and extract chain_ids before the
block ends so the guard is dropped immediately after the needed data is copied.
Mirror the pattern used in get_by_taker and get_by_token, keeping
RaindexTradesDataSource and process_get_trades_by_order_hashes as the key call
sites to update.
In `@src/routes/trades/get_by_tx.rs`:
- Around line 273-277: The tx path is not verifying that the new chain filter is
actually forwarded into the data source. Update the `get_trades_by_tx` flow in
`MockTradesDataSource` to capture `_chain_ids`, then add an assertion in one
process test that the tx lookup receives `Some(vec![8453])`, mirroring the
existing taker/order-hash test pattern so the filter can’t be dropped silently.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ee1e424f-045e-46d2-986b-dedde235f5b1
📒 Files selected for processing (21)
src/main.rssrc/routes/mod.rssrc/routes/order/mod.rssrc/routes/orders/get_by_owner.rssrc/routes/orders/get_by_token.rssrc/routes/orders/mod.rssrc/routes/swap/calldata.rssrc/routes/swap/mod.rssrc/routes/swap/quote.rssrc/routes/tokens.rssrc/routes/trades/get_by_address.rssrc/routes/trades/get_by_order_hashes.rssrc/routes/trades/get_by_taker.rssrc/routes/trades/get_by_token.rssrc/routes/trades/get_by_tx.rssrc/routes/trades/mod.rssrc/routes/vaults.rssrc/types/orders.rssrc/types/swap.rssrc/types/trades.rssrc/types/vaults.rs
✅ Files skipped from review due to trivial changes (1)
- src/types/swap.rs
🚧 Files skipped from review as they are similar to previous changes (4)
- src/routes/mod.rs
- src/types/vaults.rs
- src/routes/vaults.rs
- src/routes/swap/mod.rs
| .mount("/v2/tokens", routes::tokens::routes_v2()) | ||
| .mount("/v1/swap", routes::swap::routes()) | ||
| .mount("/v2/swap", routes::swap::routes_v2()) | ||
| .mount("/v1/order", routes::order::routes()) | ||
| .mount("/v2/order", routes::order::routes_v2()) | ||
| .mount("/v1/orders", routes::orders::routes()) | ||
| .mount("/v2/orders", routes::orders::routes_v2()) | ||
| .mount("/v1/vaults", routes::vaults::routes()) | ||
| .mount("/v2/vaults", routes::vaults::routes_v2()) | ||
| .mount("/v1/trades", routes::trades::routes()) | ||
| .mount("/v2/trades", routes::trades::routes_v2()) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify v2 OpenAPI path coverage in source/tests.
rg -n '"/v2/(tokens|order|orders|vaults|trades|swap)' src
rg -n '#\[utoipa::path|paths\(' src/main.rs src/routesRepository: ST0x-Technology/st0x.rest.api
Length of output: 2959
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the OpenAPI setup and v2 route definitions/annotations.
sed -n '1,140p' src/main.rs
printf '\n--- main OpenAPI tests ---\n'
sed -n '420,520p' src/main.rs
printf '\n--- tokens routes ---\n'
sed -n '720,860p' src/routes/tokens.rs
printf '\n--- order routes ---\n'
sed -n '1,220p' src/routes/order.rs
printf '\n--- orders routes ---\n'
sed -n '1,220p' src/routes/orders.rs
printf '\n--- vaults routes ---\n'
sed -n '280,390p' src/routes/vaults.rs
printf '\n--- trades routes ---\n'
sed -n '1,220p' src/routes/trades.rs
printf '\n--- swap routes ---\n'
sed -n '1,120p' src/routes/swap/quote.rs
sed -n '1,120p' src/routes/swap/calldata.rsRepository: ST0x-Technology/st0x.rest.api
Length of output: 14228
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the route modules and inspect the v2 OpenAPI-related definitions/tests.
fd -t f 'mod.rs|*.rs' src/routes | sort
printf '\n--- order-related files ---\n'
fd -t f 'order*.rs|*order*/*.rs' src/routes | sort
printf '\n--- orders-related files ---\n'
fd -t f 'orders*.rs|*orders*/*.rs' src/routes | sort
printf '\n--- vaults/trades/tokens files ---\n'
fd -t f 'vaults*.rs|trades*.rs|tokens*.rs' src/routes | sort
printf '\n--- openapi references in main.rs ---\n'
rg -n 'openapi|ApiDoc|/v2/(tokens|order|orders|vaults|trades|swap)' src/main.rs
printf '\n--- path annotations in route files ---\n'
rg -n '#\[utoipa::path|path = "/v2/|path = "/v1/' src/routes/tokens.rs src/routes/swap src/routes/orders.rs src/routes/vaults.rs src/routes/trades.rs src/routes/order -g '!**/target/**'Repository: ST0x-Technology/st0x.rest.api
Length of output: 465
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- route files ---'
fd -t f --glob '*.rs' src/routes | sort
printf '\n%s\n' '--- v2 route mounts/usages ---'
rg -n 'routes_v2|/v2/' src/routes src/main.rs
printf '\n%s\n' '--- OpenAPI annotations in candidate files ---'
rg -n '#\[utoipa::path|path = "/v2/|path = "/v1/' src/routes/tokens.rs src/routes/orders.rs src/routes/vaults.rs src/routes/trades.rs src/routes/swap/quote.rs src/routes/swap/calldata.rs src/main.rsRepository: ST0x-Technology/st0x.rest.api
Length of output: 4035
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- routes_v2 implementations ---'
sed -n '1,240p' src/routes/order/mod.rs
printf '\n--- orders mod ---\n'
sed -n '1,260p' src/routes/orders/mod.rs
printf '\n--- vaults ---\n'
sed -n '1,460p' src/routes/vaults.rs
printf '\n--- trades mod ---\n'
sed -n '1,520p' src/routes/trades/mod.rs
printf '\n--- tokens ---\n'
sed -n '1180,1265p' src/routes/tokens.rs
printf '\n--- swap mod ---\n'
sed -n '1,360p' src/routes/swap/mod.rs
printf '\n%s\n' '--- OpenAPI tests mentioning v2 paths ---'
rg -n '/v2/(tokens|order|orders|vaults|trades|swap)|swap_quote_v2_path|swap_calldata_v2_path' src/main.rs src/routes -g '!**/target/**'Repository: ST0x-Technology/st0x.rest.api
Length of output: 50385
Add v2 OpenAPI entries for the mounted route groups. routes_v2() here reuses the v1 route tables, so /v2/tokens, /v2/order, /v2/orders, /v2/vaults, and /v2/trades won’t appear as distinct OpenAPI paths unless you add matching #[utoipa::path(...)] entries and path assertions.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main.rs` around lines 177 - 187, The v2-mounted route groups in
main::mount currently reuse the v1 route tables, so the OpenAPI generator will
not emit distinct /v2 paths for tokens, order, orders, vaults, and trades.
Update the relevant route modules and their routes_v2() handlers to add matching
#[utoipa::path(...)] annotations for the v2 endpoints, and extend the existing
path assertions/tests to verify the /v2 paths are present alongside the v1 ones.
Source: Coding guidelines
## Motivation Pyth is being removed as the website pricing source. The REST API needs to own the executable platform price, preserve the latest valid price when markets are closed, and retain enough history for 24-hour movement and future gainers/losers views. The price service follows the registry-driven multi-chain model from #151. Chain IDs and quote-token addresses are discovered from the registry rather than duplicated in static REST configuration. ## Solution - Discover every configured registry network containing ST0x tokens on each sampling/request cycle. - Resolve each network's quote token from the registry token list by the `USDC` symbol, case-insensitively. - Pin dev, preview, and production to registry commit `208aad93753e6dff4d87c178af7eb8a3a9f7732b`, which includes the compatible subgraph and complete asset set. - Sample active ST0x/USDC order books every 60 seconds with bounded multi-chain concurrency. - Compute the exact midpoint of the highest executable bid and lowest executable ask only for complete, positive, two-sided, non-crossed books. - Exclude explicitly unsuccessful order quotes because those orders are not currently executable. Still fail a sample on transport failures, missing/duplicate quote results, or a successful quote without data so incomplete books cannot silently publish. - Normalize wrapped, unwrapped, legacy, and mixed-case token aliases into canonical wrapped-share units using ERC-4626 assets-per-share. - Persist exact decimal bid, ask, and midpoint strings in SQLite with idempotent sample buckets and seven-day retention. - Add authenticated latest, timestamp, and SQL-downsampled history endpoints with canonical lowercase addresses and camelCase JSON. - Return all registry markets when `GET /v1/prices` omits `chainId`; validate a requested chain and require `chainId` for history when multiple networks are configured. - Keep retained history visible across registry canonical-address rotations by querying current, unwrapped, and legacy aliases. - Expose live, cached, historical, and unavailable source states plus batched 24-hour percentage changes. - Supervise the sampler with timeout and detailed health reporting, and document the API contract. ## Intentionally unchanged - No historical backfill or regeneration outside the retention window. - No top-gainers UI in this PR. - No website changes in this PR. - No TradingView or Pyth pricing dependency. - This PR remains based directly on `main`; it mirrors #151's chain-resolution helpers and registry semantics without becoming its child. ## Verification - `nix develop -c rainix-rs-static` - `nix develop -c cargo test` — 323 passed - Focused market-price and route tests passed. - Two-stage read-only local review; all actionable correctness findings were fixed and retested. ## Runtime smoke test Started the REST API locally with the updated dev registry and a temporary API key: - The registry exposed all 27 configured ST0x assets. - `GET /v1/prices?chainId=8453` produced live bid, ask, and midpoint snapshots for 26 assets. - wtSPYM returned an executable midpoint around `$87.23`. - wtMSTR was correctly unavailable because its current book was crossed; no invalid midpoint was persisted. - The website's public pricing proxy and homepage consumed these snapshots successfully. The supplied registry commit removes the earlier subgraph-schema compatibility caveat. The temporary API key was deleted after testing. ## Review focus Please pay particular attention to registry-driven market discovery, ERC-4626 denomination normalization, quote-side interpretation, explicit failed-quote exclusion versus structural quote failures, alias continuity across registry rotations, and sampler health behavior. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Enabled configurable market-price sampling with periodic persistence and seven-day history retention. * Added authenticated pricing APIs for latest/historical market prices and per-token intervalized price history. * **Bug Fixes** * Improved reliability by capping concurrency for per-order quote fallback calls. * **Documentation** * Added Market Prices documentation describing pricing methodology and API responses. * **Tests** * Extended smoke checks to validate the new pricing endpoints. * **Health** * Enhanced `/health/detailed` to report market-price sampler status and freshness/failure signals. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Siddharth2207
left a comment
There was a problem hiding this comment.
Website multi-network notes (pairs with SARKEX/st0x#263)
Staging /v2/tokens is Base-only today, which is fine. Two payload gaps will block a real second-network UI:
- No block explorer on
network. The website currently hardcodeshttps://blockscan.combecauseTokenNetworkResponseSchemahas no explorer URL (key,rpcs,chainId,label,networkId,currencyonly). Please expose a per-network explorer (registry field or explicit map) so the UI can fail catalog build when it is missing. rpcsare intentionally stripped insanitize_network(network.rpcs.clear()). That is correct for a public token list, but SARKEX/st0x#263 then builds EIP-1271 clients from public registry YAML RPCs and dropped the productionBASE_RPC_URLfail-closed. A second chain needs a documented paid/primary RPC channel (env or authenticated registry), not an emptyrpcsarray.
Those two schema/sanitize sites are outside this PR’s diff hunks, so this is a top-level note rather than inline on those lines.
| #[serde(rename_all = "camelCase")] | ||
| pub struct TokenListParams { | ||
| #[field(name = "chainId")] | ||
| #[param(example = 8453)] |
There was a problem hiding this comment.
GET /v2/tokens is what the website catalog is built from.
Two gaps on the token network object will block SARKEX/st0x#263 from being actually multi-network:
- No explorer URL → UI hardcodes
https://blockscan.com(already wrong for Base; should behttps://basescan.org). rpcsis always[]aftersanitize_network→ UI falls back to public registry RPCs for EIP-1271.
Please add a per-network explorer to this payload (or /registry), and document a paid/primary RPC channel for authenticated website use. Empty public rpcs can stay.
How to use the Graphite Merge QueueAdd the label add-to-gt-merge-queue to this PR to add it to the merge queue. You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
|
Superseded by #193, rebuilt from current main without the obsolete block-explorer submodule bump. |
## Supersedes - Replaces #151 with a clean branch based on current `main`. - Excludes the obsolete block-explorer Raindex submodule bump from #174. ## Dependency - [Registry #79](ST0x-Technology/st0x.registry#79) ## Motivation Enable registry-driven multi-network behavior without breaking existing V1 clients or the currently deployed website, which uses V2 swaps without an explicit `chainId`. ## Version contract - V1 non-swap endpoints preserve the legacy Base (8453) default when `chainId` is omitted. - V2 data endpoints provide multi-network behavior; omitted `chainId` means all networks for list endpoints and remains an error where a single network is required. - V1 and V2 swap endpoints remain Base-compatible: omitted `chainId` selects Base, and explicit non-Base swaps are directed to V3. - New V3 quote and calldata endpoints require an explicit `chainId` and never infer a network. ## Solution - Mount canonical V2 data routes while retaining V1 compatibility. - Add strict `/v3/swap/quote` and `/v3/swap/calldata` routes using the existing mode-based swap contract. - Document V3 with dedicated OpenAPI schemas where `chainId` is required. - Add chain-aware selection across swaps, tokens, details and proofs, orders, trades, vaults, prices, attribution, analytics, and caches. - Scope token identity, SDK queries, wrap ratios, attribution workers, and cache keys by chain. - Restrict order, trade, and swap queries to networks with configured Raindexes. - Add the chain-keyed wrapped exchange-rate snapshot migration. - Pin dev, preview, and production configs to Registry #79's multi-network registry commit. - Select each network's market quote token through the registry `marketQuote` extension, with USDC fallback for existing registries. ## Deployment safety The REST API can be deployed before the website. Existing V1 clients continue to resolve omitted network selection to Base, and the current website's V2 swap requests continue to work unchanged. The website can migrate non-Base swaps to V3 independently. ## Validation - [x] `cargo fmt --check` - [x] `cargo check --all-targets` - [x] `cargo test` — 499 passed - [x] `cargo clippy --all-targets -- -D warnings` - [x] `git diff --check` - [x] No Raindex submodule delta - [x] Clean local DB bootstrap reached ready/healthy sync for Base (8453) and Robinhood (4663) - [x] Token, order, trade, vault-total, and price endpoints stayed isolated for both chain IDs - [x] Unsupported `chainId` returns HTTP 400 - [x] Robinhood prices use the configured USDG quote token and return live chain-4663 samples - [x] Website PR #263 validated end-to-end against the local server

Summary
mainand make chain selection registry-driven across swaps, tokens, token details/proofs/wrap ratios, orders, trades, vaults, prices, attribution reporting, analytics, and response caches./v2routes while retaining existing/v1mounts for compatibility.chainIdfor address-, order-, transaction-, and swap-scoped operations when more than one network is configured. When the registry has exactly one network, omission resolves to that network for backwards compatibility.chainIdon list endpoints as an all-configured-networks query; validate every explicit chain against the active remote registry.chainIdin chain-sensitive request/response types and analytics events.(chain_id, share_token_address, block_number). Historical rows are assigned Base chain 8453 because they predate multi-network operation.Compatibility contract
chainId.chainIdnow; omission becomes ambiguous and returns a 400 once multiple networks are configured.Validation
nix develop -c cargo fmt -- --checknix develop -c cargo checknix develop -c cargo test— 439 passednix develop -c rainix-rs-staticnix develop -c mdbook build docsmain