Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 10 additions & 0 deletions core/src/exchanges/rain/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export class RainExchange extends PredictionMarketExchange {
protected override readonly capabilityOverrides = {
fetchSeries: false as const,
fetchOrderBook: 'emulated' as const,
// Emulated batch: loops the single-outcome fetchOrderBook (no native batch endpoint).
fetchOrderBooks: 'emulated' as const,
watchOrderBook: 'emulated' as const,
watchTrades: 'emulated' as const,
// Trading is on-chain via Rain SDK + viem. open/closed orders + fetchOrder
Expand Down Expand Up @@ -157,6 +159,14 @@ export class RainExchange extends PredictionMarketExchange {
return this.normalizer.normalizeOrderBook(raw, outcomeId);
}

async fetchOrderBooks(outcomeIds: string[]): Promise<Record<string, OrderBook>> {
const response: Record<string, OrderBook> = {};
for (const outcomeId of outcomeIds) {
response[outcomeId] = await this.fetchOrderBook(outcomeId);
}
return response;
}

async fetchTrades(outcomeId: string, params: TradesParams | HistoryFilterParams): Promise<Trade[]> {
const parts = outcomeId.split(':');
if (parts.length < 2) {
Expand Down
47 changes: 47 additions & 0 deletions core/test/unit/rain-fetch-order-books.core.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { RainExchange } from '../../src/exchanges/rain';
import type { OrderBook } from '../../src/types';

/**
* Regression for #1666: RainExchange exposed a working single-outcome
* `fetchOrderBook` but never overrode the batch `fetchOrderBooks`, so calling
* it threw "Method fetchOrderBooks not implemented." and `has.fetchOrderBooks`
* reported false. The batch override loops the already-working single fetch and
* is reported as an emulated capability (matching `fetchOrderBook: 'emulated'`).
*/
describe('RainExchange.fetchOrderBooks', () => {
const BOOK_A: OrderBook = {
bids: [{ price: 0.4, size: 10 }],
asks: [],
timestamp: 1,
} as OrderBook;
const BOOK_B: OrderBook = {
bids: [],
asks: [{ price: 0.6, size: 5 }],
timestamp: 2,
} as OrderBook;

test('loops the single-outcome fetchOrderBook for each outcome id', async () => {
const exchange = new RainExchange();
const books: Record<string, OrderBook> = {
'rain:1:0': BOOK_A,
'rain:2:1': BOOK_B,
};
const calls: string[] = [];
// Stub the already-working single-outcome fetch so no network is hit.
(exchange as any).fetchOrderBook = async (outcomeId: string): Promise<OrderBook> => {
calls.push(outcomeId);
return books[outcomeId];
};

const ids = ['rain:1:0', 'rain:2:1'];
const result = await exchange.fetchOrderBooks(ids);

expect(calls).toEqual(ids);
expect(result).toEqual({ 'rain:1:0': BOOK_A, 'rain:2:1': BOOK_B });
});

test('reports fetchOrderBooks as a supported (emulated) capability', () => {
const exchange = new RainExchange();
expect(exchange.has.fetchOrderBooks).toBe('emulated');
});
});