Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions src/oracle/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { ethers } from 'ethers';
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

/**
* Extract oracle URL from meta bytes.
*
* TODO: This will use the SDK's extractOracleUrl once the wasm package is updated.
* For now, this is a placeholder that should parse meta bytes to find oracle URL.
*
* @param metaHex - Hex string of meta bytes (e.g. "0x1234...")
* @returns Oracle URL if found, null otherwise
*/
export function extractOracleUrl(metaHex: string): string | null {
// TODO: Implement CBOR decoding to find RaindexSignedContextOracleV1
// magic number 0xff7a1507ba4419ca and extract URL.
// For now, return null as a stub.
console.warn('extractOracleUrl not yet implemented - waiting for SDK update');
return null;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

/**
* Signed context response from oracle endpoint.
* Maps directly to SignedContextV1 in the orderbook contract.
*/
export interface SignedContextV1 {
/** The signer address (EIP-191 signer of the context data) */
signer: string;
/** The signed context data as bytes32[] values */
context: string[];
/** The EIP-191 signature over keccak256(abi.encodePacked(context)) */
signature: string;
}

/**
* Order details for oracle request.
*/
export interface OracleOrderRequest {
order: any; // OrderV4 struct
inputIOIndex: number;
outputIOIndex: number;
counterparty: string;
}

/**
* Fetch signed context from oracle endpoint.
*
* POSTs the ABI-encoded batch body and returns the array of signed contexts.
* The request body is abi.encode((OrderV4, uint256, uint256, address)[]).
* The response is an array of SignedContextV1 JSON objects.
*
* @param url - Oracle endpoint URL
* @param orders - Array of order requests
* @returns Array of signed contexts matching the request array length and order
*/
export async function fetchSignedContext(
url: string,
orders: OracleOrderRequest[]
): Promise<SignedContextV1[]> {
// Encode the batch request body
const abiCoder = ethers.AbiCoder.defaultAbiCoder();

// For each order, create a tuple: (OrderV4, uint256, uint256, address)
const tuples = orders.map(req => [
req.order,
req.inputIOIndex,
req.outputIOIndex,
req.counterparty
]);

// ABI encode the array of tuples
// Note: This needs the actual OrderV4 struct ABI definition
// TODO: Import proper OrderV4 type definition
const body = abiCoder.encode(
['tuple(tuple(address owner, tuple(address interpreter, address store, bytes bytecode) evaluable, tuple(address token, bytes32 vaultId)[] validInputs, tuple(address token, bytes32 vaultId)[] validOutputs, bytes32 nonce), uint256, uint256, address)[]'],
[tuples]
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream'
},
body: ethers.getBytes(body)
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

if (!response.ok) {
throw new Error(`Oracle request failed: ${response.status} ${response.statusText}`);
}

const contexts: SignedContextV1[] = await response.json();

if (!Array.isArray(contexts)) {
throw new Error('Oracle response must be an array');
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

if (contexts.length !== orders.length) {
throw new Error(`Oracle response length (${contexts.length}) must match request length (${orders.length})`);
}

return contexts;
}
47 changes: 47 additions & 0 deletions src/order/quote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AppOptions } from "../config";
import { ABI, normalizeFloat } from "../common";
import { BundledOrders, Pair, TakeOrder } from "./types";
import { decodeFunctionResult, encodeFunctionData, PublicClient } from "viem";
import { extractOracleUrl, fetchSignedContext } from "../oracle";

/**
* Quotes a single order
Expand Down Expand Up @@ -38,6 +39,29 @@ export async function quoteSingleOrderV3(
blockNumber?: bigint,
gas?: bigint,
) {
// Check if order has oracle metadata and fetch signed context
try {
const orderMeta = (orderDetails as any).orderDetails?.meta;
if (orderMeta) {
const oracleUrl = extractOracleUrl(orderMeta);
if (oracleUrl) {
// Fetch signed context for this order
const signedContexts = await fetchSignedContext(oracleUrl, [{
order: orderDetails.takeOrder.struct.order,
inputIOIndex: orderDetails.takeOrder.struct.inputIOIndex,
outputIOIndex: orderDetails.takeOrder.struct.outputIOIndex,
counterparty: '0x0000000000000000000000000000000000000000' // Unknown at quote time
}]);

// Update the signed context in the takeOrder struct
orderDetails.takeOrder.struct.signedContext = signedContexts;
}
}
} catch (error) {
// Oracle failures should not prevent quoting - log warning and continue with empty context
console.warn('Failed to fetch oracle data for quote:', error);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

const { data } = await viemClient
.call({
to: orderDetails.orderbook as `0x${string}`,
Expand Down Expand Up @@ -82,6 +106,29 @@ export async function quoteSingleOrderV4(
blockNumber?: bigint,
gas?: bigint,
) {
// Check if order has oracle metadata and fetch signed context
try {
const orderMeta = (orderDetails as any).orderDetails?.meta;
if (orderMeta) {
const oracleUrl = extractOracleUrl(orderMeta);
if (oracleUrl) {
// Fetch signed context for this order
const signedContexts = await fetchSignedContext(oracleUrl, [{
order: orderDetails.takeOrder.struct.order,
inputIOIndex: orderDetails.takeOrder.struct.inputIOIndex,
outputIOIndex: orderDetails.takeOrder.struct.outputIOIndex,
counterparty: '0x0000000000000000000000000000000000000000' // Unknown at quote time
}]);

// Update the signed context in the takeOrder struct
orderDetails.takeOrder.struct.signedContext = signedContexts;
}
}
} catch (error) {
// Oracle failures should not prevent quoting - log warning and continue with empty context
console.warn('Failed to fetch oracle data for quote:', error);
}

const { data } = await viemClient
.call({
to: orderDetails.orderbook as `0x${string}`,
Expand Down
3 changes: 3 additions & 0 deletions src/subgraph/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export function getQueryPaginated(skip: number, filters?: SgFilter): string {
owner
orderHash
orderBytes
meta
active
nonce
orderbook {
Expand Down Expand Up @@ -108,6 +109,7 @@ export const getTxsQuery = (startTimestamp: number, skip: number, endTimestamp?:
owner
orderHash
orderBytes
meta
active
nonce
orderbook {
Expand Down Expand Up @@ -142,6 +144,7 @@ export const getTxsQuery = (startTimestamp: number, skip: number, endTimestamp?:
owner
orderHash
orderBytes
meta
active
nonce
orderbook {
Expand Down
Loading