From d056249b9a2984c335ed8e67e53788815312c394 Mon Sep 17 00:00:00 2001 From: Bayyan16 Date: Thu, 25 Jun 2026 19:37:17 +0700 Subject: [PATCH] fix(routes): scope single-market queries by network --- src/routes/funding.ts | 1 + src/routes/insurance.ts | 3 +- src/routes/markets.ts | 1 + src/routes/open-interest.ts | 3 +- tests/routes/insurance.test.ts | 195 ++++++----------- tests/routes/open-interest.test.ts | 340 +++++++++++++---------------- 6 files changed, 228 insertions(+), 315 deletions(-) diff --git a/src/routes/funding.ts b/src/routes/funding.ts index f89b5b2..c68c70d 100644 --- a/src/routes/funding.ts +++ b/src/routes/funding.ts @@ -145,6 +145,7 @@ export function fundingRoutes(): Hono { // causes a PostgREST 400. Downstream defaults assetIndex to 0. Same fix as crank.ts (e471efb). .select("funding_rate, net_lp_pos, symbol, last_price") .eq("slab_address", slab) + .eq("network", getNetwork()) .single(); if (statsError && statsError.code !== "PGRST116") { diff --git a/src/routes/insurance.ts b/src/routes/insurance.ts index e41eb16..36da3ec 100644 --- a/src/routes/insurance.ts +++ b/src/routes/insurance.ts @@ -8,7 +8,7 @@ */ import { Hono } from "hono"; import { validateSlab } from "../middleware/validateSlab.js"; -import { getSupabase, createLogger, truncateErrorMessage } from "@percolator/shared"; +import { getSupabase, getNetwork, createLogger, truncateErrorMessage } from "@percolator/shared"; const logger = createLogger("api:insurance"); @@ -40,6 +40,7 @@ export function insuranceRoutes(): Hono { .from("market_stats") .select("insurance_balance, insurance_fee_revenue, total_open_interest") .eq("slab_address", slab) + .eq("network", getNetwork()) .single(); if (statsError && statsError.code !== "PGRST116") { diff --git a/src/routes/markets.ts b/src/routes/markets.ts index c765a66..c96837b 100644 --- a/src/routes/markets.ts +++ b/src/routes/markets.ts @@ -120,6 +120,7 @@ export function marketRoutes(): Hono { .from("market_stats") .select("slab_address, total_open_interest, total_accounts, last_crank_slot, last_price, mark_price, index_price, funding_rate, net_lp_pos, lp_sum_abs, lp_max_abs, insurance_balance, insurance_fee_revenue, volume_24h, updated_at") .eq("slab_address", slab) + .eq("network", getNetwork()) .single(); if (error && error.code !== "PGRST116") throw error; return c.json({ stats: data ?? null }); diff --git a/src/routes/open-interest.ts b/src/routes/open-interest.ts index 06e42d8..9d88192 100644 --- a/src/routes/open-interest.ts +++ b/src/routes/open-interest.ts @@ -10,7 +10,7 @@ import { Hono } from "hono"; import { validateSlab } from "../middleware/validateSlab.js"; import { cacheMiddleware } from "../middleware/cache.js"; -import { getSupabase, createLogger, truncateErrorMessage } from "@percolator/shared"; +import { getSupabase, getNetwork, createLogger, truncateErrorMessage } from "@percolator/shared"; /** * GH#1458: Phantom OI guard for history records. @@ -64,6 +64,7 @@ export function openInterestRoutes(): Hono { .from("market_stats") .select("total_open_interest, net_lp_pos, lp_sum_abs, lp_max_abs") .eq("slab_address", slab) + .eq("network", getNetwork()) .single(); if (statsError && statsError.code !== "PGRST116") { diff --git a/tests/routes/insurance.test.ts b/tests/routes/insurance.test.ts index 1e3d7f7..331ef71 100644 --- a/tests/routes/insurance.test.ts +++ b/tests/routes/insurance.test.ts @@ -4,6 +4,7 @@ import { insuranceRoutes } from "../../src/routes/insurance.js"; // Mock @percolator/shared vi.mock("@percolator/shared", () => ({ getSupabase: vi.fn(), + getNetwork: vi.fn(() => "devnet"), getConnection: vi.fn(), createLogger: vi.fn(() => ({ info: vi.fn(), @@ -42,6 +43,36 @@ describe("insurance routes", () => { vi.mocked(getSupabase).mockReturnValue(mockSupabase); }); + function mockInsuranceQueries( + mockStats: unknown, + mockHistory: unknown[] = [], + statsError: unknown = null, + historyError: unknown = null + ) { + const statsBuilder: any = {}; + statsBuilder.select = vi.fn(() => statsBuilder); + statsBuilder.eq = vi.fn(() => statsBuilder); + statsBuilder.single = vi.fn().mockResolvedValue({ + data: mockStats, + error: statsError, + }); + + const historyBuilder: any = {}; + historyBuilder.select = vi.fn(() => historyBuilder); + historyBuilder.eq = vi.fn(() => historyBuilder); + historyBuilder.order = vi.fn(() => historyBuilder); + historyBuilder.limit = vi.fn().mockResolvedValue({ + data: mockHistory, + error: historyError, + }); + + mockSupabase.from.mockImplementation((table: string) => { + if (table === "market_stats") return statsBuilder; + if (table === "insurance_history") return historyBuilder; + return mockSupabase; + }); + } + describe("GET /insurance/:slab", () => { it("should return current insurance balance and history", async () => { const mockStats = { @@ -63,28 +94,7 @@ describe("insurance routes", () => { }, ]; - mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ data: mockStats, error: null }), - })), - })), - }; - } else if (table === "insurance_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn().mockResolvedValue({ data: mockHistory, error: null }), - })), - })), - })), - }; - } - return mockSupabase; - }); + mockInsuranceQueries(mockStats, mockHistory); const app = insuranceRoutes(); const res = await app.request("/insurance/11111111111111111111111111111111"); @@ -99,22 +109,7 @@ describe("insurance routes", () => { }); it("should return 404 when market not found", async () => { - mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ - data: null, - error: { code: "PGRST116" } - }), - })), - })), - }; - } - return mockSupabase; - }); - + mockInsuranceQueries(null, [], { code: "PGRST116" }); const app = insuranceRoutes(); const res = await app.request("/insurance/11111111111111111111111111111111"); @@ -139,28 +134,7 @@ describe("insurance routes", () => { total_open_interest: null, }; - mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ data: mockStats, error: null }), - })), - })), - }; - } else if (table === "insurance_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn().mockResolvedValue({ data: [], error: null }), - })), - })), - })), - }; - } - return mockSupabase; - }); + mockInsuranceQueries(mockStats, []); const app = insuranceRoutes(); const res = await app.request("/insurance/11111111111111111111111111111111"); @@ -173,21 +147,7 @@ describe("insurance routes", () => { }); it("should handle database errors", async () => { - mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ - data: null, - error: new Error("Database error") - }), - })), - })), - }; - } - return mockSupabase; - }); + mockInsuranceQueries(null, [], new Error("Database error")); const app = insuranceRoutes(); const res = await app.request("/insurance/11111111111111111111111111111111"); @@ -205,30 +165,31 @@ describe("insurance routes", () => { }; let limitCalled = false; + + const statsBuilder: any = {}; + statsBuilder.select = vi.fn(() => statsBuilder); + statsBuilder.eq = vi.fn(() => statsBuilder); + statsBuilder.single = vi.fn().mockResolvedValue({ + data: mockStats, + error: null, + }); + + const historyBuilder: any = {}; + historyBuilder.select = vi.fn(() => historyBuilder); + historyBuilder.eq = vi.fn(() => historyBuilder); + historyBuilder.order = vi.fn(() => historyBuilder); + historyBuilder.limit = vi.fn((n: number) => { + expect(n).toBe(100); + limitCalled = true; + return Promise.resolve({ + data: [], + error: null, + }); + }); + mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ data: mockStats, error: null }), - })), - })), - }; - } else if (table === "insurance_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn((n: number) => { - expect(n).toBe(100); - limitCalled = true; - return Promise.resolve({ data: [], error: null }); - }), - })), - })), - })), - }; - } + if (table === "market_stats") return statsBuilder; + if (table === "insurance_history") return historyBuilder; return mockSupabase; }); @@ -260,38 +221,18 @@ describe("insurance routes", () => { } it("allows valid non-blocked slabs through to DB layer", async () => { - mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ - data: { - insurance_balance: "1000000000", - insurance_fee_revenue: "50000000", - total_open_interest: "5000000000", - }, - error: null, - }), - })), - })), - }; - } else if (table === "insurance_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn().mockResolvedValue({ data: [], error: null }), - })), - })), - })), - }; - } - return mockSupabase; - }); + mockInsuranceQueries( + { + insurance_balance: "1000000000", + insurance_fee_revenue: "50000000", + total_open_interest: "5000000000", + }, + [] + ); const app = insuranceRoutes(); const res = await app.request("/insurance/11111111111111111111111111111111"); + expect(res.status).toBe(200); }); }); diff --git a/tests/routes/open-interest.test.ts b/tests/routes/open-interest.test.ts index 93172bd..9a9c56f 100644 --- a/tests/routes/open-interest.test.ts +++ b/tests/routes/open-interest.test.ts @@ -5,6 +5,7 @@ import { clearCache } from "../../src/middleware/cache.js"; // Mock @percolator/shared vi.mock("@percolator/shared", () => ({ getSupabase: vi.fn(), + getNetwork: vi.fn(() => "devnet"), getConnection: vi.fn(), createLogger: vi.fn(() => ({ info: vi.fn(), @@ -45,6 +46,36 @@ describe("open-interest routes", () => { vi.mocked(getSupabase).mockReturnValue(mockSupabase); }); + function mockOpenInterestQueries( + mockStats: unknown, + mockHistory: unknown[] = [], + statsError: unknown = null, + historyError: unknown = null + ) { + const statsBuilder: any = {}; + statsBuilder.select = vi.fn(() => statsBuilder); + statsBuilder.eq = vi.fn(() => statsBuilder); + statsBuilder.single = vi.fn().mockResolvedValue({ + data: mockStats, + error: statsError, + }); + + const historyBuilder: any = {}; + historyBuilder.select = vi.fn(() => historyBuilder); + historyBuilder.eq = vi.fn(() => historyBuilder); + historyBuilder.order = vi.fn(() => historyBuilder); + historyBuilder.limit = vi.fn().mockResolvedValue({ + data: mockHistory, + error: historyError, + }); + + mockSupabase.from.mockImplementation((table: string) => { + if (table === "market_stats") return statsBuilder; + if (table === "oi_history") return historyBuilder; + return mockSupabase; + }); + } + describe("GET /open-interest/:slab", () => { it("should return OI data and history", async () => { const mockStats = { @@ -67,26 +98,26 @@ describe("open-interest routes", () => { }, ]; + const statsBuilder: any = {}; + statsBuilder.select = vi.fn(() => statsBuilder); + statsBuilder.eq = vi.fn(() => statsBuilder); + statsBuilder.single = vi.fn().mockResolvedValue({ + data: mockStats, + error: null, + }); + + const historyBuilder: any = {}; + historyBuilder.select = vi.fn(() => historyBuilder); + historyBuilder.eq = vi.fn(() => historyBuilder); + historyBuilder.order = vi.fn(() => historyBuilder); + historyBuilder.limit = vi.fn().mockResolvedValue({ + data: mockHistory, + error: null, + }); + mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ data: mockStats, error: null }), - })), - })), - }; - } else if (table === "oi_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn().mockResolvedValue({ data: mockHistory, error: null }), - })), - })), - })), - }; - } + if (table === "market_stats") return statsBuilder; + if (table === "oi_history") return historyBuilder; return mockSupabase; }); @@ -104,21 +135,7 @@ describe("open-interest routes", () => { }); it("should return 404 when market not found", async () => { - mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ - data: null, - error: { code: "PGRST116" } - }), - })), - })), - }; - } - return mockSupabase; - }); + mockOpenInterestQueries(null, [], { code: "PGRST116" }); const app = openInterestRoutes(); const res = await app.request("/open-interest/11111111111111111111111111111111"); @@ -145,28 +162,7 @@ describe("open-interest routes", () => { lp_max_abs: null, }; - mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ data: mockStats, error: null }), - })), - })), - }; - } else if (table === "oi_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn().mockResolvedValue({ data: [], error: null }), - })), - })), - })), - }; - } - return mockSupabase; - }); + mockOpenInterestQueries(mockStats, []); const app = openInterestRoutes(); const res = await app.request("/open-interest/11111111111111111111111111111111"); @@ -177,22 +173,20 @@ describe("open-interest routes", () => { expect(data.netLpPos).toBe("0"); expect(data.lpSumAbs).toBe("0"); expect(data.lpMaxAbs).toBe("0"); + expect(data.history).toHaveLength(0); }); it("should handle database errors", async () => { + const statsBuilder: any = {}; + statsBuilder.select = vi.fn(() => statsBuilder); + statsBuilder.eq = vi.fn(() => statsBuilder); + statsBuilder.single = vi.fn().mockResolvedValue({ + data: null, + error: new Error("Database error"), + }); + mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ - data: null, - error: new Error("Database error") - }), - })), - })), - }; - } + if (table === "market_stats") return statsBuilder; return mockSupabase; }); @@ -213,30 +207,31 @@ describe("open-interest routes", () => { }; let limitCalled = false; + + const statsBuilder: any = {}; + statsBuilder.select = vi.fn(() => statsBuilder); + statsBuilder.eq = vi.fn(() => statsBuilder); + statsBuilder.single = vi.fn().mockResolvedValue({ + data: mockStats, + error: null, + }); + + const historyBuilder: any = {}; + historyBuilder.select = vi.fn(() => historyBuilder); + historyBuilder.eq = vi.fn(() => historyBuilder); + historyBuilder.order = vi.fn(() => historyBuilder); + historyBuilder.limit = vi.fn((n: number) => { + expect(n).toBe(100); + limitCalled = true; + return Promise.resolve({ + data: [], + error: null, + }); + }); + mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ data: mockStats, error: null }), - })), - })), - }; - } else if (table === "oi_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn((n: number) => { - expect(n).toBe(100); - limitCalled = true; - return Promise.resolve({ data: [], error: null }); - }), - })), - })), - })), - }; - } + if (table === "market_stats") return statsBuilder; + if (table === "oi_history") return historyBuilder; return mockSupabase; }); @@ -254,26 +249,26 @@ describe("open-interest routes", () => { lp_max_abs: "500000", }; + const statsBuilder: any = {}; + statsBuilder.select = vi.fn(() => statsBuilder); + statsBuilder.eq = vi.fn(() => statsBuilder); + statsBuilder.single = vi.fn().mockResolvedValue({ + data: mockStats, + error: null, + }); + + const historyBuilder: any = {}; + historyBuilder.select = vi.fn(() => historyBuilder); + historyBuilder.eq = vi.fn(() => historyBuilder); + historyBuilder.order = vi.fn(() => historyBuilder); + historyBuilder.limit = vi.fn().mockResolvedValue({ + data: [], + error: null, + }); + mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ data: mockStats, error: null }), - })), - })), - }; - } else if (table === "oi_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn().mockResolvedValue({ data: [], error: null }), - })), - })), - })), - }; - } + if (table === "market_stats") return statsBuilder; + if (table === "oi_history") return historyBuilder; return mockSupabase; }); @@ -294,97 +289,70 @@ describe("open-interest routes", () => { "3ZKKwsKoo5UP28cYmMpvGpwoFpWLVgEWLQJCejJnECQn", ]; - for (const addr of BLOCKED) { - it(`returns 404 for blocked slab ${addr.slice(0, 8)}... on /open-interest`, async () => { - const app = openInterestRoutes(); - const res = await app.request(`/open-interest/${addr}`); - expect(res.status).toBe(404); - const data = await res.json(); - expect(data).toEqual({ error: "Market not found" }); - // DB should never be queried for blocked slabs + for (const addr of BLOCKED) { + it(`returns 404 for blocked slab ${addr.slice(0, 8)}... on /open-interest`, async () => { + const app = openInterestRoutes(); + const res = await app.request(`/open-interest/${addr}`); + expect(res.status).toBe(404); + const data = await res.json(); + expect(data).toEqual({ error: "Market not found" }); + // DB should never be queried for blocked slabs expect(mockSupabase.from).not.toHaveBeenCalled(); - }); - } + }); + } it("allows valid non-blocked slabs through to DB layer", async () => { + const statsBuilder: any = {}; + statsBuilder.select = vi.fn(() => statsBuilder); + statsBuilder.eq = vi.fn(() => statsBuilder); + statsBuilder.single = vi.fn().mockResolvedValue({ + data: { + total_open_interest: "1000", + net_lp_pos: "100", + lp_sum_abs: "200", + lp_max_abs: "50", + }, + error: null, + }); + + const historyBuilder: any = {}; + historyBuilder.select = vi.fn(() => historyBuilder); + historyBuilder.eq = vi.fn(() => historyBuilder); + historyBuilder.order = vi.fn(() => historyBuilder); + historyBuilder.limit = vi.fn().mockResolvedValue({ + data: [], + error: null, + }); + mockSupabase.from.mockImplementation((table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ - data: { - total_open_interest: "1000", - net_lp_pos: "100", - lp_sum_abs: "200", - lp_max_abs: "50", - }, - error: null, - }), - })), - })), - }; - } else if (table === "oi_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn().mockResolvedValue({ data: [], error: null }), - })), - })), - })), - }; - } + if (table === "market_stats") return statsBuilder; + if (table === "oi_history") return historyBuilder; return mockSupabase; }); const app = openInterestRoutes(); const res = await app.request("/open-interest/11111111111111111111111111111111"); + expect(res.status).toBe(200); }); - }); + + }); describe("GH#1458: phantom OI history filter — pre-migration values (9.87e+34) excluded", () => { // usdEkK5G (11111111...) and MOLTBOT (22222222...) oi_history contained rows // with total_oi = 9.87e+34 from uninitialized on-chain state. These must be // stripped before the chart data is returned. - const PHANTOM_OI = "987000000000000000000000000000000000"; // ~9.87e+35 + const PHANTOM_OI = "98700000000000000000000000000000000"; const VALID_OI_1 = "5000000000"; const VALID_OI_2 = "4800000000"; - function mockSupabaseWithHistory(history: Array<{ timestamp: string; total_oi: string; net_lp_pos: string }>) { - return (table: string) => { - if (table === "market_stats") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - single: vi.fn().mockResolvedValue({ - data: { - total_open_interest: VALID_OI_1, - net_lp_pos: "1500000", - lp_sum_abs: "2000000", - lp_max_abs: "500000", - }, - error: null, - }), - })), - })), - }; - } else if (table === "oi_history") { - return { - select: vi.fn(() => ({ - eq: vi.fn(() => ({ - order: vi.fn(() => ({ - limit: vi.fn().mockResolvedValue({ data: history, error: null }), - })), - })), - })), - }; - } - return mockSupabase; - }; - } + const mockGh1458Stats = { + total_open_interest: VALID_OI_1, + net_lp_pos: "1500000", + lp_sum_abs: "2000000", + lp_max_abs: "500000", + }; it("strips phantom history records (total_oi >= 1e18) from response", async () => { const history = [ @@ -393,7 +361,7 @@ describe("open-interest routes", () => { { timestamp: "2026-01-03T00:00:00Z", total_oi: VALID_OI_2, net_lp_pos: "1400000" }, // real — included ]; - mockSupabase.from.mockImplementation(mockSupabaseWithHistory(history)); + mockOpenInterestQueries(mockGh1458Stats, history); const app = openInterestRoutes(); const res = await app.request("/open-interest/11111111111111111111111111111111"); @@ -414,7 +382,7 @@ describe("open-interest routes", () => { { timestamp: "2026-01-02T00:00:00Z", total_oi: VALID_OI_2, net_lp_pos: "1500000" }, // real — included ]; - mockSupabase.from.mockImplementation(mockSupabaseWithHistory(history)); + mockOpenInterestQueries(mockGh1458Stats, history); const app = openInterestRoutes(); const res = await app.request("/open-interest/11111111111111111111111111111111"); @@ -431,7 +399,7 @@ describe("open-interest routes", () => { { timestamp: "2026-01-02T00:00:00Z", total_oi: VALID_OI_2, net_lp_pos: "1400000" }, ]; - mockSupabase.from.mockImplementation(mockSupabaseWithHistory(history)); + mockOpenInterestQueries(mockGh1458Stats, history); const app = openInterestRoutes(); const res = await app.request("/open-interest/11111111111111111111111111111111"); @@ -447,7 +415,7 @@ describe("open-interest routes", () => { { timestamp: "2026-01-02T00:00:00Z", total_oi: PHANTOM_OI, net_lp_pos: "0" }, ]; - mockSupabase.from.mockImplementation(mockSupabaseWithHistory(history)); + mockOpenInterestQueries(mockGh1458Stats, history); const app = openInterestRoutes(); const res = await app.request("/open-interest/11111111111111111111111111111111"); @@ -467,7 +435,7 @@ describe("open-interest routes", () => { { timestamp: "2026-01-02T00:00:00Z", total_oi: VALID_OI_1, net_lp_pos: "1500000" }, ]; - mockSupabase.from.mockImplementation(mockSupabaseWithHistory(history)); + mockOpenInterestQueries(mockGh1458Stats, history); const app = openInterestRoutes(); const res = await app.request("/open-interest/11111111111111111111111111111111");