Skip to content

Comments

feat: add signed context oracle support#436

Open
hardyjosh wants to merge 9 commits intomasterfrom
feat/oracle-support
Open

feat: add signed context oracle support#436
hardyjosh wants to merge 9 commits intomasterfrom
feat/oracle-support

Conversation

@hardyjosh
Copy link
Contributor

@hardyjosh hardyjosh commented Feb 23, 2026

Adds support for fetching signed context from oracle servers for orders that specify an oracle-url in their metadata. Changes: adds meta field to subgraph queries, new oracle module for fetching signed contexts (batch format per spec), wires oracle fetch into quoting pipeline. Note: extractOracleUrl is a placeholder pending SDK update.

Summary by CodeRabbit

  • New Features
    • Oracle integration for quoting: quotes can include oracle-signed context fetched in batches with timeout and validation; failures are logged but do not block quoting.
    • Subgraph enhancements: order payloads now include additional metadata for richer order information.

1. Add meta field to subgraph queries for order discovery
2. Create oracle module with:
   - extractOracleUrl() placeholder for meta parsing
   - fetchSignedContext() for batch oracle requests
   - Support for batch format (array of contexts)
3. Wire oracle into quoting logic:
   - Extract oracle URL from order meta before quote2
   - Fetch signed context and inject into takeOrder struct
   - Graceful fallback on oracle failures
4. Ensure signed context flows through to takeOrdersConfig

The solver now automatically fetches oracle data for orders that
specify an oracle-url, enabling external data integration.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 23, 2026

Warning

Rate limit exceeded

@hardyjosh has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 7 minutes and 8 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 5513fce and 1c39eea.

📒 Files selected for processing (5)
  • src/oracle/fetch.ts
  • src/oracle/index.ts
  • src/order/index.ts
  • src/order/quote.ts
  • src/state/index.ts

Walkthrough

Adds an oracle integration module that encodes order batches, POSTs to an oracle endpoint with timeout handling, and validates signed-context responses; quote routines attempt to fetch and inject oracle-signed context before quoting; subgraph queries now request order meta field.

Changes

Cohort / File(s) Summary
Oracle Integration Module
src/oracle/index.ts
New module exporting `extractOracleUrl(metaHex): string
Quote Logic Integration
src/order/quote.ts
Imports extractOracleUrl and fetchSignedContext; adds fetchOracleContext(orderDetails) and calls it from quoteSingleOrderV3 and quoteSingleOrderV4 (wrapped in try/catch) to fetch and inject signedContext when oracle metadata is present.
GraphQL Query Updates
src/subgraph/query.ts
Adds meta to order selection sets in getQueryPaginated and both AddOrder/RemoveOrder branches of getTxsQuery so order metadata is returned by the subgraph.

Sequence Diagram

sequenceDiagram
    actor Client
    participant QuoteService as Quote Service
    participant OracleModule as Oracle Module
    participant OracleEndpoint as Oracle Endpoint

    Client->>QuoteService: quoteSingleOrderV4(order)
    QuoteService->>QuoteService: inspect order.meta for oracle info
    alt oracle metadata present
        QuoteService->>OracleModule: extractOracleUrl(metaHex)
        OracleModule-->>QuoteService: oracle URL or null
        alt URL found
            QuoteService->>OracleModule: fetchSignedContext(url, [orderRequest])
            OracleModule->>OracleModule: encode batch (ABI)
            OracleModule->>OracleEndpoint: POST (application/octet-stream) with timeout
            OracleEndpoint-->>OracleModule: HTTP 200 + JSON array
            OracleModule->>OracleModule: validate parse SignedContextV1[]
            OracleModule-->>QuoteService: SignedContextV1[]
            QuoteService->>QuoteService: inject signedContext into orderDetails
        end
    end
    QuoteService->>QuoteService: perform quote
    QuoteService-->>Client: return quote result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main change: adding support for fetching signed context from oracle servers, which is the primary objective across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/oracle-support

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/oracle/index.ts`:
- Around line 89-93: Validate the oracle JSON before casting to
SignedContextV1[]: after parsing into contexts, iterate over each item and
ensure it has the required keys and types (e.g., that "signer" is a string,
"signature" is a string and/or bytes representation, and "context" is an object
or array as expected) and reject or throw a descriptive error for any malformed
entry; update the code around the contexts variable and the SignedContextV1
validation logic (the parsing block that populates contexts and the code that
later uses contexts for takeOrder) to perform these checks and sanitize/convert
fields as needed before returning or passing into takeOrder.
- Around line 36-41: The OracleOrderRequest currently types order as any;
replace that with the concrete OrderV4 type (e.g., use OrderV4) so the compiler
enforces structural compatibility during ABI encoding. Import or reference the
resolved OrderV4 type where OracleOrderRequest is declared, update the interface
field to order: OrderV4, and run TypeScript/tsc to fix any resulting type
mismatches in functions that construct or pass OracleOrderRequest objects
(adjust those call sites to produce a valid OrderV4).
- Around line 77-83: The fetch call that posts the oracle request (the statement
creating response = await fetch(url, { method: 'POST', headers: {...}, body:
ethers.getBytes(body) })) has no timeout and can hang; wrap the request with an
AbortController (or use AbortSignal.timeout(ms)) and pass its signal to fetch,
enforce a reasonable deadline (e.g., 5–10s), and ensure you abort the controller
on timeout; update the code around the fetch invocation to create controller =
new AbortController(), pass signal: controller.signal into fetch, use
setTimeout(() => controller.abort(), TIMEOUT_MS) (or AbortSignal.timeout) and
handle the abort in the existing error handling so the quoting pipeline fails
fast instead of blocking.
- Around line 12-18: The extractOracleUrl function currently logs a console.warn
on every invocation which will flood logs; change this by removing the per-call
console.warn or replace it with a one-time warning emitted via a module-level
flag (e.g., a let warned = false at module scope) so the message is logged only
once, or eliminate the log altogether and simply return null; update the
extractOracleUrl function to reference the module-level flag (or remove the
call) and ensure no per-invocation console.warn remains.
- Line 1: The code is using ethers v6 APIs but the project depends on ethers
v5.7.0; update the v6-specific calls to their v5 equivalents: replace uses of
ethers.AbiCoder.defaultAbiCoder() with ethers.utils.defaultAbiCoder and replace
ethers.getBytes(...) with ethers.utils.arrayify(...). Locate these calls in
src/oracle/index.ts (references around the symbols AbiCoder.defaultAbiCoder and
getBytes) and update any related variable names/usages so they call
ethers.utils.* accordingly, ensuring types/inputs remain the same.
- Around line 69-75: The code is ABI-encoding OrderV4 tuples using a hardcoded
tuple signature; replace that raw string with the canonical ABI constant from
the common ABI module. Import the Orderbook ABI export (e.g., Orderbook or
Orderbook.V5) and use its Order/OrderV4 type or parsed ABI parameter (via
parseAbiParameters if needed) in the abiCoder.encode call instead of the inline
tuple string so the encode uses the shared OrderV4 definition; update the import
and pass the referenced ABI constant where the current abiCoder.encode([...],
[tuples]) call appears.

In `@src/order/quote.ts`:
- Around line 42-63: Extract the duplicated oracle-fetch block into a private
helper named fetchOracleContext that accepts the orderDetails object, uses
extractOracleUrl(orderMeta) and fetchSignedContext(oracleUrl, [...]) to obtain
signed context, and updates orderDetails.takeOrder.struct.signedContext; then
replace the inline try/catch in quoteSingleOrderV3 and quoteSingleOrderV4 with a
call to await fetchOracleContext(orderDetails) wrapped in the same try/catch
that logs the warning on failure (preserve the same warning text), ensuring you
reference the existing functions fetchSignedContext and extractOracleUrl and the
same orderDetails.takeOrder.struct shape so behavior is unchanged.
- Around line 42-63: The oracle metadata access is wrong because
quoteSingleOrderV3 and quoteSingleOrderV4 receive a Pair (PairV3 | PairV4) that
doesn’t contain the original SgOrder.meta, so the fetch block using
(orderDetails as any).orderDetails?.meta is unreachable; fix by changing the
function(s) signature to accept the original SgOrder (or at least its
meta/oracle URL) alongside the Pair, or alternatively persist the SgOrder.meta
onto the Pair when the Pair is constructed so extractOracleUrl can read it; then
factor the duplicated fetch logic into a shared helper (e.g.,
fetchSignedContextForOrder) that calls extractOracleUrl and fetchSignedContext
and sets takeOrder.struct.signedContext (referencing
orderDetails.takeOrder.struct.signedContext) and use that helper from both
quoteSingleOrderV3 and quoteSingleOrderV4.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4928590 and 9fd313d.

📒 Files selected for processing (3)
  • src/oracle/index.ts
  • src/order/quote.ts
  • src/subgraph/query.ts

- Fix ethers v6 → v5 APIs (defaultAbiCoder, arrayify)
- Use ABI.Orderbook.V5.OrderV4 constant instead of hardcoded tuple string
- Add 5s timeout on oracle fetch via AbortController
- Validate SignedContextV1 shape on each response entry
- Extract fetchOracleContext helper to deduplicate quote logic
- Remove noisy console.warn from stub extractOracleUrl
- Type OracleOrderRequest.order properly instead of any
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/order/quote.ts (1)

14-16: ⚠️ Potential issue | 🟠 Major

(orderDetails as any).meta will always be undefined — oracle fetch is dead code.

orderDetails is typed as Pair (PairV3 | PairV4), and as any is required because meta is not a member of the Pair type. Consequently, orderMeta is always undefined, the guard on Line 16 returns immediately every time, and oracle signed context is never fetched — making the entire oracle integration a no-op.

The previous form (orderDetails as any).orderDetails?.meta exhibited the same issue; the path was shortened but the root cause (the original SgOrder.meta is not surfaced on Pair) was not addressed. The fix requires one of:

  • Adding a meta?: string field to PairBase (or the variant types) and populating it when constructing the Pair from an SgOrder, or
  • Changing quoteSingleOrderV3/quoteSingleOrderV4 (and quoteSingleOrder) to accept the original SgOrder (or at least its oracle URL) alongside the Pair.
#!/bin/bash
# Verify whether Pair / PairBase / PairV3 / PairV4 expose a `meta` field
rg -n "meta" --type=ts src/order/types/ -C 2

# Also check if SgOrder carries meta and whether it's threaded into Pair construction
rg -n "meta" --type=ts src/subgraph/types.ts -C 2
rg -n "fromArgs\|new Pair\|PairV3\|PairV4" --type=ts src/order/types/ -A 10 | grep -A 5 "meta"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/order/quote.ts` around lines 14 - 16, fetchOracleContext is dead because
(orderDetails as any).meta is always undefined—the Pair type (PairBase / PairV3
/ PairV4) doesn’t expose SgOrder.meta—so oracle logic never runs. Fix by either:
1) add an optional meta?: string to PairBase (and relevant PairV3/PairV4) and
ensure the Pair factory/constructor that converts an SgOrder copies SgOrder.meta
into the Pair, replacing the (orderDetails as any).meta access; or 2) change
quoteSingleOrderV3 / quoteSingleOrderV4 (and quoteSingleOrder) to accept the
original SgOrder (or at least its oracle URL) alongside the Pair and pass that
through to fetchOracleContext, then remove the incorrect cast in
fetchOracleContext. Ensure fetchOracleContext reads the strongly typed field
(meta or provided oracle URL) so oracle fetching actually executes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/oracle/index.ts`:
- Around line 110-122: The current validation in the SignedContextV1 mapping
(the contexts constant in src/oracle/index.ts) only checks that entry.context is
an array but not that its items are strings; update the validator inside the
json.map where you check (entry as any).context to ensure Array.isArray((entry
as any).context) && ((entry as any).context).every((c: unknown) => typeof c ===
"string") so that non-string elements are rejected and the thrown Error message
still references the index i; keep returning entry as SignedContextV1 after the
stricter check.

---

Duplicate comments:
In `@src/order/quote.ts`:
- Around line 14-16: fetchOracleContext is dead because (orderDetails as
any).meta is always undefined—the Pair type (PairBase / PairV3 / PairV4) doesn’t
expose SgOrder.meta—so oracle logic never runs. Fix by either: 1) add an
optional meta?: string to PairBase (and relevant PairV3/PairV4) and ensure the
Pair factory/constructor that converts an SgOrder copies SgOrder.meta into the
Pair, replacing the (orderDetails as any).meta access; or 2) change
quoteSingleOrderV3 / quoteSingleOrderV4 (and quoteSingleOrder) to accept the
original SgOrder (or at least its oracle URL) alongside the Pair and pass that
through to fetchOracleContext, then remove the incorrect cast in
fetchOracleContext. Ensure fetchOracleContext reads the strongly typed field
(meta or provided oracle URL) so oracle fetching actually executes.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fd313d and 8fabe2e.

📒 Files selected for processing (2)
  • src/oracle/index.ts
  • src/order/quote.ts

Comment on lines 110 to 122
// Validate shape of each entry
const contexts: SignedContextV1[] = json.map((entry: unknown, i: number) => {
if (
typeof entry !== "object" ||
entry === null ||
typeof (entry as any).signer !== "string" ||
!Array.isArray((entry as any).context) ||
typeof (entry as any).signature !== "string"
) {
throw new Error(`Oracle response[${i}] is not a valid SignedContextV1`);
}
return entry as SignedContextV1;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

context array elements are not validated as strings.

Array.isArray((entry as any).context) only verifies it's an array; it doesn't check that every element is a string. A malformed oracle response with numeric or object entries in context would silently pass validation, violate the string[] contract, and fail unpredictably downstream.

🛡️ Proposed fix
         !Array.isArray((entry as any).context) ||
+        !(entry as any).context.every((elem: unknown) => typeof elem === "string") ||
         typeof (entry as any).signature !== "string"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/oracle/index.ts` around lines 110 - 122, The current validation in the
SignedContextV1 mapping (the contexts constant in src/oracle/index.ts) only
checks that entry.context is an array but not that its items are strings; update
the validator inside the json.map where you check (entry as any).context to
ensure Array.isArray((entry as any).context) && ((entry as
any).context).every((c: unknown) => typeof c === "string") so that non-string
elements are rejected and the thrown Error message still references the index i;
keep returning entry as SignedContextV1 after the stricter check.

Josh Hardy added 4 commits February 23, 2026 20:59
Replace ethers.utils.defaultAbiCoder/arrayify with viem's
encodeAbiParameters/hexToBytes. Use proper viem ABI parameter
definitions instead of string-based encoding.
- Up to 2 retries with exponential backoff (500ms, 1s)
- After 3 consecutive failures, oracle URL enters 5min cooloff
- During cooloff, requests to that URL are skipped immediately
- Cooloff resets on first successful response
- Invalid responses (bad shape, wrong length) also count as failures
- All configurable via module constants
No retries, no delays in the loop. Single attempt with 5s timeout —
if it fails, record the failure and move on. After 3 consecutive
failures the URL enters a 5min cooloff where it's skipped immediately
(no network call at all). This way one bad oracle can't block the
processing of other orders.
…ager

Extract oracle cooloff tracking from module-level singleton into an
OracleManager class. Instance lives on OrderManager, threaded through
to quote functions. This makes it properly scoped to the solver
instance lifecycle and testable.

- OracleManager class in src/oracle/manager.ts
- fetchSignedContext takes OracleManager as parameter
- OrderManager creates and owns the OracleManager instance
- OracleManager is optional in quote functions for backward compat
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/oracle/index.ts (1)

150-162: ⚠️ Potential issue | 🟡 Minor

context array elements are not validated as strings.

Line 156 only verifies Array.isArray((entry as any).context) without checking that every element is a string. A malformed oracle response with non-string entries silently passes validation.

🛡️ Proposed fix
         !Array.isArray((entry as any).context) ||
+        !(entry as any).context.every((elem: unknown) => typeof elem === "string") ||
         typeof (entry as any).signature !== "string"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/oracle/index.ts` around lines 150 - 162, The validation for
SignedContextV1 entries currently only checks Array.isArray((entry as
any).context) but doesn't ensure each element is a string; update the json.map
validation that creates contexts to assert that (entry as any).context is an
array of strings (e.g., use Array.isArray and .every(elem => typeof elem ===
"string")) and throw the same Error(`Oracle response[${i}] is not a valid
SignedContextV1`) if any element is non-string so the SignedContextV1 cast is
only applied after the full shape check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/oracle/index.ts`:
- Around line 32-43: OracleOrderRequest uses bare string for Ethereum-typed
fields which causes type errors when passed to encodeAbiParameters with
oracleBatchAbiParams; update the OracleOrderRequest interface so owner,
interpreter, store, and token are typed as viem Address (e.g. `0x${string}`),
and bytecode, vaultId, nonce (and any bytes/bytes32 fields) are typed as viem
Hex (e.g. `0x${string}`), or alternatively perform explicit casts to
`\`0x${string}\`` for those specific properties at the call site before calling
encodeAbiParameters; apply this change to the nested order fields referenced by
encodeAbiParameters and keep the already-casted counterparty treatment
consistent.

---

Duplicate comments:
In `@src/oracle/index.ts`:
- Around line 150-162: The validation for SignedContextV1 entries currently only
checks Array.isArray((entry as any).context) but doesn't ensure each element is
a string; update the json.map validation that creates contexts to assert that
(entry as any).context is an array of strings (e.g., use Array.isArray and
.every(elem => typeof elem === "string")) and throw the same Error(`Oracle
response[${i}] is not a valid SignedContextV1`) if any element is non-string so
the SignedContextV1 cast is only applied after the full shape check.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8fabe2e and 5513fce.

📒 Files selected for processing (1)
  • src/oracle/index.ts

Josh Hardy added 3 commits February 23, 2026 21:13
Follow codebase conventions:
- Oracle health map lives on SharedState.oracleHealth
- fetchOracleContext is a standalone fn with this: SharedState,
  called via .call(state) like processOrder/findBestTrade
- Health helpers (isInCooloff, recordOracleSuccess/Failure) are
  plain exported functions operating on the health map
- No new classes, no module-level singletons
- quoteSingleOrder receives SharedState to thread through
- fetchSignedContext returns Result<SignedContextV1[], string>
- fetchOracleContext returns Result<void, string>
- Callers check .isErr() instead of try/catch
- Follows codebase convention for error handling
…terface

- OracleOrderRequest.order uses Order.V3 | Order.V4 from order/types
- OracleOrderRequest.counterparty typed as 0x
- Drop custom SignedContextV1 interface — signed context is already
  typed as any[] on TakeOrderV3/V4, and the response validation
  ensures the right shape at runtime
- fetchSignedContext returns Result<any[], string> matching the
  existing signedContext field type
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.

1 participant