feat: oracle signed context - full stack (Phases 2-7)#2505
Conversation
…n RaindexOrder - Add SignedContextOracleV1 variant to ParsedMeta enum - Add match arm for KnownMagic::SignedContextOracleV1 in parsing logic - Add oracle_url() wasm_bindgen getter on RaindexOrder - Depends on rain.metadata feat/signed-context-oracle-meta branch
- Add 3 tests for SignedContextOracleV1 parsing (from_meta_item, parse_multiple, parse_from_bytes roundtrip) - Add oracle_url() to non-wasm impl block (mirrors wasm getter) - All 11 parsed_meta tests passing
…leV1 metadata Points rain.interpreter at feat/signed-context-oracle-meta-submodule branch which updates the rain.metadata submodule to feat/signed-context-oracle-meta.
Matches rename in rain.metadata#92. The metadata type is specific to the Raindex calculateOrderIO entrypoint.
… flows Phase 3 of signed context oracle discovery: - New oracle.rs module with fetch_signed_context(url) and OracleResponse type - OracleResponse maps directly to SignedContextV1 (signer, context as bytes32[], signature) - Added signed_context field to TakeOrderCandidate - Wired oracle fetching into: - build_take_order_candidates_for_pair (batch flow, concurrent fetch) - execute_single_take (single take flow, oracle_url param) - build_take_orders_config_from_simulation (passes through to TakeOrderConfigV4) - Oracle fetch is best-effort: failures log a warning and use empty signed context - 3 oracle tests + 9 parsed_meta tests passing
- Add get_order_quotes_with_context() to quote crate (accepts signed_context param) - RaindexOrder.get_quotes() now fetches oracle data and passes to quotes - Original get_order_quotes() unchanged (delegates with empty context)
reqwest::ClientBuilder::timeout() is not available on WASM targets. Use cfg(not(target_family = "wasm")) to only set it on native.
- OrderDetail: show Oracle card property with URL link when order has oracle metadata - Includes tooltip explaining signed context oracle usage - TanstackOrderQuote: show purple 'Oracle' badge next to quotes heading when oracle is active - Indicates quotes include signed context data from oracle - Both use the oracleUrl getter exposed via WASM bindings on RaindexOrder
The oracle endpoint now receives order details via POST so it can tailor responses based on the specific order, counterparty, and IO indexes. POST body: abi.encode(OrderV4, inputIOIndex, outputIOIndex, counterparty) Falls back to GET when no body is provided (simple oracles). Callers currently pass None - ABI encoding will be wired in once the order data is available at each call site.
POST with ABI-encoded order data is mandatory. Callers currently pass empty vec — will be wired to abi.encode(OrderV4, inputIOIndex, outputIOIndex, counterparty) at each call site.
- encode_oracle_body: abi.encode(OrderV4, inputIOIndex, outputIOIndex, counterparty) - get_quotes: fetches oracle per IO pair concurrently, counterparty=address(0) - build_take_order_candidates: fetches oracle per quote pair - execute_single_take: encodes with actual taker as counterparty - get_order_quotes_with_context_fn: accepts per-pair context callback
- Oracle fetch logic moved from common to quote crate (common re-exports) - get_order_quotes now extracts oracle URL directly from SgOrder.meta - Removed get_order_quotes_with_context and get_order_quotes_with_context_fn - No more closures, HashMaps, or pre-fetching — oracle context fetched inline per IO pair inside the quote loop - RaindexOrder.get_quotes() simplified to just call get_order_quotes()
- Add encode_oracle_body_batch() for array encoding: abi.encode((OrderV4, uint256, uint256, address)[]) - Update fetch_signed_context_batch() to handle Vec responses - Maintain backward compatibility with single request functions - Add comprehensive tests for both single and batch formats - Response format now expects JSON array per spec
Adds optional oracle-url field to OrderCfg, parsed from the YAML front matter orders section. When present, this URL identifies a signed context oracle server for the order. Changes: - Add oracle_url: Option<String> to OrderCfg struct - Parse oracle-url via optional_string in YAML parsing - Add oracle-url to ALLOWED_ORDER_KEYS - Update Default and PartialEq impls - Add test for oracle-url parsing (present + absent) Spec: rainlanguage/specs#45 Chained on: #2459 (Phase 4)
When an order has oracle_url set in its config, new_from_deployment now creates a SignedContextOracleV1 meta item and includes it in the order's additional_meta. This means orders deployed with oracle-url in their YAML will have the oracle endpoint encoded in their onchain RainMetaDocumentV1, making it discoverable by takers and indexers (Phase 2 reads it back). Changes: - Import SignedContextOracleV1 in add_order.rs - In new_from_deployment: parse oracle_url, create meta item, append to additional_meta
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds optional per-order Changes
Sequence DiagramsequenceDiagram
participant Client
participant QuoteModule
participant OracleModule
participant OracleEndpoint
participant TakeOrderModule
Client->>QuoteModule: get_order_quotes(orders, block, rpcs, chunk_size)
QuoteModule->>QuoteModule: extract_oracle_url(order)
alt oracle_url present
QuoteModule->>OracleModule: encode_oracle_body(order, in_idx, out_idx, counterparty)
OracleModule-->>QuoteModule: body (bytes)
QuoteModule->>OracleEndpoint: POST body (application/octet-stream)
OracleEndpoint-->>QuoteModule: OracleResponse
QuoteModule->>OracleModule: convert OracleResponse -> SignedContextV1
OracleModule-->>QuoteModule: SignedContextV1
QuoteModule->>QuoteModule: attach signed_context to quote
else oracle_url absent
QuoteModule->>QuoteModule: use empty signed_context
end
QuoteModule-->>TakeOrderModule: BatchOrderQuotesResponse (with signed_context)
TakeOrderModule->>TakeOrderModule: build candidates including signed_context
TakeOrderModule->>TakeOrderModule: execute_single_take(..., oracle_url)
alt oracle_url provided
TakeOrderModule->>OracleModule: fetch_signed_context(url, body)
OracleModule-->>TakeOrderModule: SignedContextV1 (or error -> warn)
TakeOrderModule->>TakeOrderModule: set candidate.signed_context
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 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.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
crates/common/src/take_orders/config.rs (1)
87-95: 🧹 Nitpick | 🔵 TrivialAdd one non-empty
signedContextregression test here.Line 94 is now the only point where fetched oracle data enters
TakeOrderConfigV4. The current tests in this module still build candidates viamake_simulation_candidatefromcrates/common/src/test_helpers.rs, which hardcodessigned_context: vec![], and the updatedexecute_single_taketests only passNonefororacle_url. A regression back to an emptysignedContextwould still leave this PR green; please assert a non-emptyconfig.orders[*].signedContextsomewhere in this path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/common/src/take_orders/config.rs` around lines 87 - 95, The current tests never exercise non-empty signedContext because candidates are built with make_simulation_candidate (which sets signed_context: vec![]) and existing execute_single_take tests pass oracle_url = None; add or update a unit test that constructs a simulation with at least one leg whose candidate.signed_context is populated (or call the code path that fetches oracle data so signedContext is set) and then after building TakeOrderConfigV4 via the map (the orders variable produced from sim.legs.iter().map(...) that creates TakeOrderConfigV4) assert that config.orders[i].signedContext is non-empty; reference the TakeOrderConfigV4 construction and the execute_single_take test helper to locate where to inject the non-empty signed context and the assertion.crates/quote/src/order_quotes.rs (1)
53-60:⚠️ Potential issue | 🟠 MajorOracle-backed historical quotes ignore
block_number.Lines 53-60 pin the EVM quote to
req_block_number, but Lines 97-106 always fetch live oracle context with no block/timestamp input. For oracle-driven orders,block_number: Some(...)now mixes historical chain state with current oracle state and can return a quote that never existed. Either reject historical quotes for oracle-backed orders, or make the oracle request block-aware.Also applies to: 97-106
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/quote/src/order_quotes.rs` around lines 53 - 60, The code computes req_block_number using ReadableClient::new_from_http_urls(...).get_block_number() but later (around the oracle context fetch block at lines ~97-106) always requests live oracle state, causing mismatches for oracle-backed orders when block_number is Some(...); fix by detecting oracle-backed orders and either rejecting historical quotes (return an error if block_number.is_some() and the order is oracle-backed) or by making the oracle request block-aware: extend the oracle-context fetch to accept a block/timestamp parameter and pass req_block_number there so the oracle snapshot and chain state are consistent (locate req_block_number, the ReadableClient::new_from_http_urls/get_block_number call, and the oracle-context fetch block to implement the check or the parameter plumbing).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@crates/common/src/add_order.rs`:
- Around line 134-147: The block that appends a RaindexSignedContextOracleV1
meta item can create duplicates when additional_meta already contains one;
update the logic in the additional_meta construction (the code that reads
deployment.order.oracle_url, calls RaindexSignedContextOracleV1::parse(...) and
pushes oracle.to_meta_item()) to first scan existing meta for an existing
RaindexSignedContextOracleV1 entry (by matching meta item type/name or parsing
existing meta into RaindexSignedContextOracleV1) and only push the new oracle
meta if none exists or if you explicitly want to replace it; ensure you
reference the same symbols (additional_meta,
RaindexSignedContextOracleV1::parse, oracle.to_meta_item,
deployment.order.oracle_url) so the duplicate is prevented or handled
deterministically.
In `@crates/common/src/raindex_client/take_orders/single.rs`:
- Around line 117-133: The oracle fetch (crate::oracle::fetch_signed_context /
encode_oracle_body) is performed before early exits in execute_single_take,
causing unnecessary external POSTs when the function will immediately return
NoLiquidity or approval calldata; move the entire block that builds body and
calls fetch_signed_context (and the assignment to candidate.signed_context) to
after the price-cap rejection and approval short-circuit checks so it only runs
when proceeding to execution, ensuring oracle_url and taker are still available
in that later scope.
In `@crates/common/src/take_orders/candidates.rs`:
- Around line 84-117: The code is refetching oracle context with Address::ZERO
inside the quotes loop (via fetch_oracle_for_pair) and can bake wrong or missing
signatures into TakeOrderCandidate.signed_context which later becomes
TakeOrderConfigV4.signedContext; instead, stop refetching with a zero address
and either (A) thread the real counterparty address into this builder and pass
it into fetch_oracle_for_pair so the fetched signed_context is bound to the
correct taker, or (B) propagate the exact signed context produced by get_quotes
into try_build_candidate (and ultimately into TakeOrderCandidate.signed_context)
instead of calling fetch_oracle_for_pair here; update the loop around quotes and
the try_build_candidate call sites accordingly (also apply the same fix to the
analogous block at the later 128-147 region).
In `@crates/quote/src/oracle.rs`:
- Around line 89-145: The fetch_signed_context and fetch_signed_context_batch
functions currently POST to unvalidated URLs; before building the reqwest Client
and issuing the POST, validate the input URL: parse it and ensure it uses an
allowed scheme (https, optionally http only for explicit opt-in), resolve the
hostname to IP(s) and reject loopback, private (RFC1918), link-local, and other
non-public ranges, and/or enforce an explicit allowlist of hostnames or CIDRs
from configuration; perform these checks early in each function (e.g., right
after parsing the url param) and return an OracleError::InvalidInput or similar
if validation fails so that no request is sent to disallowed destinations.
- Around line 126-145: The function fetch_signed_context_batch must reject
responses whose array length doesn't match the requested batch size: after
deserializing response into Vec<OracleResponse> (the response variable) compare
response.len() against the expected request count (obtainable from the request
payload or caller-provided count) and if they differ return an OracleError
(e.g., BatchSizeMismatch with expected and got) instead of silently mapping to
SignedContextV1; update fetch_signed_context_batch to perform this length check
before mapping and returning the Vec<SignedContextV1>.
In `@crates/quote/src/order_quotes.rs`:
- Around line 97-123: The current code awaits
crate::oracle::fetch_signed_context inside the per-pair loop (the signed_context
logic using oracle_url, order_struct, input_index and output_index), causing
serial HTTP POSTs; instead, build and collect futures for all valid IO pairs
(calling fetch_signed_context with the encoded body) into a Vec or a
FuturesUnordered outside the per-pair await path and then await them in parallel
(e.g., futures::future::join_all or stream aggregation), mapping each result
back into the corresponding signed_context (handling Err by logging as done now)
so the orbit of fetch_signed_context calls runs concurrently rather than
sequentially.
---
Outside diff comments:
In `@crates/common/src/take_orders/config.rs`:
- Around line 87-95: The current tests never exercise non-empty signedContext
because candidates are built with make_simulation_candidate (which sets
signed_context: vec![]) and existing execute_single_take tests pass oracle_url =
None; add or update a unit test that constructs a simulation with at least one
leg whose candidate.signed_context is populated (or call the code path that
fetches oracle data so signedContext is set) and then after building
TakeOrderConfigV4 via the map (the orders variable produced from
sim.legs.iter().map(...) that creates TakeOrderConfigV4) assert that
config.orders[i].signedContext is non-empty; reference the TakeOrderConfigV4
construction and the execute_single_take test helper to locate where to inject
the non-empty signed context and the assertion.
In `@crates/quote/src/order_quotes.rs`:
- Around line 53-60: The code computes req_block_number using
ReadableClient::new_from_http_urls(...).get_block_number() but later (around the
oracle context fetch block at lines ~97-106) always requests live oracle state,
causing mismatches for oracle-backed orders when block_number is Some(...); fix
by detecting oracle-backed orders and either rejecting historical quotes (return
an error if block_number.is_some() and the order is oracle-backed) or by making
the oracle request block-aware: extend the oracle-context fetch to accept a
block/timestamp parameter and pass req_block_number there so the oracle snapshot
and chain state are consistent (locate req_block_number, the
ReadableClient::new_from_http_urls/get_block_number call, and the oracle-context
fetch block to implement the check or the parameter plumbing).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 2b0bf5c2-a542-4450-9922-8083de28f885
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (20)
crates/common/src/add_order.rscrates/common/src/lib.rscrates/common/src/oracle.rscrates/common/src/parsed_meta.rscrates/common/src/raindex_client/order_quotes.rscrates/common/src/raindex_client/orders.rscrates/common/src/raindex_client/take_orders/single.rscrates/common/src/raindex_client/take_orders/single_tests.rscrates/common/src/take_orders/candidates.rscrates/common/src/take_orders/config.rscrates/common/src/test_helpers.rscrates/quote/Cargo.tomlcrates/quote/src/lib.rscrates/quote/src/oracle.rscrates/quote/src/order_quotes.rscrates/settings/src/gui.rscrates/settings/src/order.rscrates/settings/src/yaml/context.rslib/rain.interpreterpackages/ui-components/src/lib/components/detail/OrderDetail.svelte
| let oracle_url = { | ||
| #[cfg(target_family = "wasm")] | ||
| { | ||
| order.oracle_url() | ||
| } | ||
| #[cfg(not(target_family = "wasm"))] | ||
| { | ||
| order.oracle_url() | ||
| } | ||
| }; | ||
|
|
||
| for quote in "es { | ||
| let signed_context = match &oracle_url { | ||
| Some(url) => { | ||
| fetch_oracle_for_pair( | ||
| url, | ||
| &order_v4, | ||
| quote.pair.input_index, | ||
| quote.pair.output_index, | ||
| Address::ZERO, // counterparty unknown at candidate building time | ||
| ) | ||
| .await | ||
| } | ||
| None => vec![], | ||
| }; | ||
|
|
||
| if let Some(candidate) = try_build_candidate( | ||
| orderbook, | ||
| &order_v4, | ||
| quote, | ||
| input_token, | ||
| output_token, | ||
| signed_context, | ||
| )? { |
There was a problem hiding this comment.
Don't bake Address::ZERO oracle signatures into candidates.
TakeOrderCandidate.signed_context is copied straight into TakeOrderConfigV4.signedContext in crates/common/src/take_orders/config.rs:87-96. Here Line 103 fetches the oracle payload for Address::ZERO, and Lines 137-146 silently downgrade fetch failures to vec![], so a candidate can carry signed context that is either bound to the wrong counterparty or missing entirely. Any oracle that includes the taker in its signature will then fail at preflight/execution, and even taker-agnostic oracles can drift from the quote generated on Line 72 because this path refetches context after quoting. Thread the real counterparty through this builder, or propagate the exact signed context used by get_quotes instead of refetching it here.
Also applies to: 128-147
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@crates/common/src/take_orders/candidates.rs` around lines 84 - 117, The code
is refetching oracle context with Address::ZERO inside the quotes loop (via
fetch_oracle_for_pair) and can bake wrong or missing signatures into
TakeOrderCandidate.signed_context which later becomes
TakeOrderConfigV4.signedContext; instead, stop refetching with a zero address
and either (A) thread the real counterparty address into this builder and pass
it into fetch_oracle_for_pair so the fetched signed_context is bound to the
correct taker, or (B) propagate the exact signed context produced by get_quotes
into try_build_candidate (and ultimately into TakeOrderCandidate.signed_context)
instead of calling fetch_oracle_for_pair here; update the loop around quotes and
the try_build_candidate call sites accordingly (also apply the same fix to the
analogous block at the later 128-147 region).
b48a03d to
9abd25d
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
crates/quote/src/order_quotes.rs (1)
100-120:⚠️ Potential issue | 🟠 MajorDon't await the oracle POST inside the per-pair loop.
This makes one HTTP round trip per valid IO pair before the multicall even starts, so quote latency grows linearly with pair count. Collect these oracle requests and resolve them in parallel, or use the batch oracle path, before building
signedContext.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/quote/src/order_quotes.rs` around lines 100 - 120, The per-pair code is performing an awaited HTTP POST via crate::oracle::fetch_signed_context inside the loop that builds signed_context, causing sequential round-trips; instead, collect the oracle requests when oracle_url.is_some() (capture order_struct, input_index, output_index, Address::ZERO and the URL) into a collection of futures (or IDs to use with the batch oracle path) before constructing signedContext, then resolve them in parallel (e.g., spawn tasks or use futures::future::join_all or call the batch oracle endpoint) and after they complete, map the results back into the signed_context for each pair; update the logic that currently awaits crate::oracle::fetch_signed_context in the loop (referenced by signed_context, crate::oracle::fetch_signed_context, oracle_url, order_struct, input_index, output_index, Address::ZERO) to use the pre-fetched/parallelized results.crates/common/src/add_order.rs (1)
135-140:⚠️ Potential issue | 🟠 MajorReplace any existing oracle meta before pushing the deployment URL.
If
additional_metaalready contains aKnownMagic::RaindexSignedContextOracleV1, this appends a second oracle item. Downstream readers only extract a single oracle entry, so the earlier meta can shadowdeployment.order.oracle_urland send quote/take flows to the wrong endpoint.🛠️ Minimal fix
let additional_meta = { let mut meta = additional_meta.unwrap_or_default(); if let Some(ref oracle_url) = deployment.order.oracle_url { + meta.retain(|item| item.magic != KnownMagic::RaindexSignedContextOracleV1); let oracle = RaindexSignedContextOracleV1::parse(oracle_url) .map_err(AddOrderArgsError::RainMetaError)?; meta.push(oracle.to_meta_item()); } if meta.is_empty() {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/common/src/add_order.rs` around lines 135 - 140, When adding the deployment.order.oracle_url into additional_meta, first remove any existing meta items whose magic equals KnownMagic::RaindexSignedContextOracleV1 so you don't append a duplicate; locate the block that builds additional_meta (uses additional_meta.unwrap_or_default(), deployment.order.oracle_url, RaindexSignedContextOracleV1::parse and oracle.to_meta_item()) and filter or retain only non-oracle entries before pushing oracle.to_meta_item() so the new oracle replaces any prior one.crates/common/src/raindex_client/take_orders/single.rs (1)
117-132:⚠️ Potential issue | 🟠 MajorMove the oracle POST behind the cheap exits.
Lines 117-132 run before the price-cap rejection at Lines 136-138 and the approval short-circuit at Lines 151-153, so
execute_single_takecan block on external I/O even when it will return early. Approval-first flows also pay this POST twice: once beforeNeedsApproval, then again on the retry that actually builds calldata. Fetch the signed context only once the function knows it is proceeding to config/simulation.Also applies to: 136-153
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/common/src/raindex_client/take_orders/single.rs` around lines 117 - 132, The oracle POST (fetch_signed_context) is being called too early in execute_single_take and can block or run twice; move the call that sets candidate.signed_context (currently done when oracle_url is Some and using crate::oracle::fetch_signed_context) to after the cheap exits (the price-cap rejection check and the NeedsApproval short-circuit) so it only runs when the function actually proceeds to config/simulation and calldata building, and ensure the retry path does not re-post unnecessarily by fetching the signed context once and reusing it for subsequent calldata construction.crates/common/src/take_orders/candidates.rs (1)
72-80:⚠️ Potential issue | 🔴 CriticalDon't freeze oracle context with a zero taker or empty fallback.
Line 79 binds the oracle request to
Address::ZERO, and Lines 115-123 turn fetch failures intovec![].build_take_orders_config_from_simulationlater copiescandidate.signed_contextstraight intoTakeOrderConfigV4.signedContext, so this can send either the wrong signature or no signature at all to preflight/execution, with no oracle URL left onTakeOrderCandidateto recover later. Defer the fetch until the real taker is known, or carry the oracle URL forward instead of materializingsigned_contexthere. It also POSTs before candidate filtering, so rejected pairs still pay the network round-trip.Also applies to: 113-123
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@crates/common/src/take_orders/candidates.rs` around lines 72 - 80, The code currently calls fetch_oracle_for_pair early (binding Address::ZERO) and stores a materialized signed_context (or vec![] on error) on TakeOrderCandidate, which can yield wrong/missing signatures and causes unnecessary POSTs; instead, stop calling fetch_oracle_for_pair here (remove the Address::ZERO call) and either (A) defer fetching until you have the real taker in the path that builds TakeOrderConfigV4 so fetch_oracle_for_pair is invoked with the real taker before populating TakeOrderConfigV4.signedContext, or (B) preserve the oracle URL on TakeOrderCandidate (e.g., keep oracle_url: Option<String>) and propagate that into build_take_orders_config_from_simulation so the actual signed_context is fetched there; also avoid swallowing errors into vec![]—return or propagate the error so callers can decide, and ensure you only POST to the oracle after candidate filtering so rejected pairs never trigger network requests (update code paths referencing signed_context, fetch_oracle_for_pair, TakeOrderCandidate, build_take_orders_config_from_simulation, and TakeOrderConfigV4.signedContext).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/ui-components/src/lib/components/detail/OrderDetail.svelte`:
- Around line 215-222: OrderDetail.svelte renders untrusted data.oracleUrl
directly into an anchor href; restrict the link to http(s) schemes before
binding to href to avoid javascript:/data: URIs. Update the rendering so you
validate/sanitize data.oracleUrl (e.g., try to parse with the URL constructor or
check startsWith 'http://' or 'https://') and only set href when the scheme is
http or https; otherwise render the value as plain text (or disable the anchor)
and avoid creating a clickable link. Ensure you change the <a> binding where
data.oracleUrl is used so the href is conditionally set based on that
validation.
---
Duplicate comments:
In `@crates/common/src/add_order.rs`:
- Around line 135-140: When adding the deployment.order.oracle_url into
additional_meta, first remove any existing meta items whose magic equals
KnownMagic::RaindexSignedContextOracleV1 so you don't append a duplicate; locate
the block that builds additional_meta (uses additional_meta.unwrap_or_default(),
deployment.order.oracle_url, RaindexSignedContextOracleV1::parse and
oracle.to_meta_item()) and filter or retain only non-oracle entries before
pushing oracle.to_meta_item() so the new oracle replaces any prior one.
In `@crates/common/src/raindex_client/take_orders/single.rs`:
- Around line 117-132: The oracle POST (fetch_signed_context) is being called
too early in execute_single_take and can block or run twice; move the call that
sets candidate.signed_context (currently done when oracle_url is Some and using
crate::oracle::fetch_signed_context) to after the cheap exits (the price-cap
rejection check and the NeedsApproval short-circuit) so it only runs when the
function actually proceeds to config/simulation and calldata building, and
ensure the retry path does not re-post unnecessarily by fetching the signed
context once and reusing it for subsequent calldata construction.
In `@crates/common/src/take_orders/candidates.rs`:
- Around line 72-80: The code currently calls fetch_oracle_for_pair early
(binding Address::ZERO) and stores a materialized signed_context (or vec![] on
error) on TakeOrderCandidate, which can yield wrong/missing signatures and
causes unnecessary POSTs; instead, stop calling fetch_oracle_for_pair here
(remove the Address::ZERO call) and either (A) defer fetching until you have the
real taker in the path that builds TakeOrderConfigV4 so fetch_oracle_for_pair is
invoked with the real taker before populating TakeOrderConfigV4.signedContext,
or (B) preserve the oracle URL on TakeOrderCandidate (e.g., keep oracle_url:
Option<String>) and propagate that into build_take_orders_config_from_simulation
so the actual signed_context is fetched there; also avoid swallowing errors into
vec![]—return or propagate the error so callers can decide, and ensure you only
POST to the oracle after candidate filtering so rejected pairs never trigger
network requests (update code paths referencing signed_context,
fetch_oracle_for_pair, TakeOrderCandidate,
build_take_orders_config_from_simulation, and TakeOrderConfigV4.signedContext).
In `@crates/quote/src/order_quotes.rs`:
- Around line 100-120: The per-pair code is performing an awaited HTTP POST via
crate::oracle::fetch_signed_context inside the loop that builds signed_context,
causing sequential round-trips; instead, collect the oracle requests when
oracle_url.is_some() (capture order_struct, input_index, output_index,
Address::ZERO and the URL) into a collection of futures (or IDs to use with the
batch oracle path) before constructing signedContext, then resolve them in
parallel (e.g., spawn tasks or use futures::future::join_all or call the batch
oracle endpoint) and after they complete, map the results back into the
signed_context for each pair; update the logic that currently awaits
crate::oracle::fetch_signed_context in the loop (referenced by signed_context,
crate::oracle::fetch_signed_context, oracle_url, order_struct, input_index,
output_index, Address::ZERO) to use the pre-fetched/parallelized results.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 135299e9-a7b4-4665-80cd-c4311ec13bc3
📒 Files selected for processing (11)
crates/common/src/add_order.rscrates/common/src/raindex_client/order_quotes.rscrates/common/src/raindex_client/orders.rscrates/common/src/raindex_client/take_orders/single.rscrates/common/src/raindex_client/take_orders/single_tests.rscrates/common/src/take_orders/candidates.rscrates/quote/src/order_quotes.rscrates/settings/src/gui.rscrates/settings/src/order.rscrates/settings/src/yaml/context.rspackages/ui-components/src/lib/components/detail/OrderDetail.svelte
| <a | ||
| href={data.oracleUrl} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| class="break-all text-blue-500 hover:underline" | ||
| > | ||
| {data.oracleUrl} | ||
| </a> |
There was a problem hiding this comment.
Restrict the Oracle link to http(s) before binding it to href.
data.oracleUrl comes from order metadata, not a trusted constant. Rendering it directly into an anchor keeps javascript:/data: payloads clickable if upstream validation ever misses one.
🔒 Suggested hardening
+ {`@const` safeOracleUrl = (() => {
+ try {
+ const url = new URL(data.oracleUrl);
+ return url.protocol === 'http:' || url.protocol === 'https:' ? url.href : null;
+ } catch {
+ return null;
+ }
+ })()}
- <a
- href={data.oracleUrl}
- target="_blank"
- rel="noopener noreferrer"
- class="break-all text-blue-500 hover:underline"
- >
- {data.oracleUrl}
- </a>
+ {`#if` safeOracleUrl}
+ <a
+ href={safeOracleUrl}
+ target="_blank"
+ rel="noopener noreferrer"
+ class="break-all text-blue-500 hover:underline"
+ >
+ {data.oracleUrl}
+ </a>
+ {:else}
+ <span class="break-all">{data.oracleUrl}</span>
+ {/if}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/ui-components/src/lib/components/detail/OrderDetail.svelte` around
lines 215 - 222, OrderDetail.svelte renders untrusted data.oracleUrl directly
into an anchor href; restrict the link to http(s) schemes before binding to href
to avoid javascript:/data: URIs. Update the rendering so you validate/sanitize
data.oracleUrl (e.g., try to parse with the URL constructor or check startsWith
'http://' or 'https://') and only set href when the scheme is http or https;
otherwise render the value as plain text (or disable the anchor) and avoid
creating a clickable link. Ensure you change the <a> binding where
data.oracleUrl is used so the href is conditionally set based on that
validation.
… degrading When oracle-url is present but the fetch fails (e.g. wrong response format, timeout, network error), the quote now shows the oracle error in the UI rather than proceeding without signed context and getting an unhelpful out-of-bounds panic.
- URL validation (SSRF prevention): validate http/https scheme before POST - Deduplicate oracle meta in add_order to prevent double entries - Move oracle fetch below early exits in take-order flow (price-cap, approval) - Document historical quote + live oracle limitation - Validate batch response length matches request count - Restrict oracle URL href to http(s) in Svelte UI - Redact oracle URL from warning logs - Remove redundant token equality check (outer loop already filters) - Add URL validation unit tests - Add explanatory comment for Address::ZERO counterparty - Fix single-fetch doc comment to match actual JSON array contract
There was a problem hiding this comment.
We should have tests for the new UI elements in this file.
| )) | ||
| } | ||
|
|
||
| #[allow(clippy::too_many_arguments)] |
There was a problem hiding this comment.
Is there a way that we can get rid of this one?
- Refactor execute_single_take to reduce parameter count - Created TakeOrderExecutionParams and RpcContext structs - Removed #[allow(clippy::too_many_arguments)] attribute - Updated function signature from 8 to 3 parameters - Added create_execution_params helper for tests - Updated 3 test calls (12 more need updating) - Add comprehensive UI tests for Oracle URL functionality - Tests Oracle section visibility based on oracleUrl presence - Tests clickable links for http/https URLs with proper security attributes - Tests XSS protection: non-http URLs render as safe plain text - Tests Oracle tooltip information display
- Add missing Address import in single_tests.rs - Update execute_single_take call in orders.rs to use new signature - Fix all 15 test function calls to use new parameter structs - Apply cargo formatting to pass static checks All static checks now pass ✅
- Simplify Oracle tooltip test to avoid complex DOM text matching - All UI tests now pass (635 tests ✅) - CI should now pass completely
- Apply prettier formatting to fix CI lint check - This was the missing step causing CI to fail despite tests passing locally
|
@coderabbitai assess this PR size classification for the totality of the PR with the following criterias and report it in your comment: S/M/L PR Classification Guidelines:This guide helps classify merged pull requests by effort and complexity rather than just line count. The goal is to assess the difficulty and scope of changes after they have been completed. Small (S)Characteristics:
Review Effort: Would have taken 5-10 minutes Examples:
Medium (M)Characteristics:
Review Effort: Would have taken 15-30 minutes Examples:
Large (L)Characteristics:
Review Effort: Would have taken 45+ minutes Examples:
Additional Factors to ConsiderWhen deciding between sizes, also consider:
Notes:
|
|
Tip For best results, initiate chat on the files or code changes. SIZE=L |
Merges the full reviewed oracle signed context stack into main.
This branch accumulates all the reviewed+approved stacked PRs:
All code was previously reviewed and approved in the stacked PRs.
Summary by CodeRabbit
New Features
Configuration
User Interface
Behavior