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
4 changes: 4 additions & 0 deletions src/contracts/providers/adaptive.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ export type UrlHealth = {
circuitOpen: boolean;
/** Epoch millis until which the circuit stays open. Zero when closed. */
openUntil: number;
/** Number of timeout-specific failures recorded. Used by adaptive provider
* to weigh timeout frequency into health scoring independently from
* other transient failures. */
timeoutCount?: number;
};

/**
Expand Down
19 changes: 19 additions & 0 deletions src/contracts/providers/healthTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@ export class HealthTracker {
}
}

/**
* Records a timeout failure. Delegates to recordFailure for circuit-breaker
* logic and additionally increments a timeout-specific counter so the
* adaptive provider can factor timeout frequency into health scoring.
*/
public recordTimeout(url: string, now: number = Date.now()): void {
const record = this.get(url);
record.timeoutCount = (record.timeoutCount ?? 0) + 1;
this.recordFailure(url, now);
}

/**
* Returns the number of timeout failures recorded for a URL.
*/
public timeoutCount(url: string): number {
const record = this.health.get(url);
return record?.timeoutCount ?? 0;
}

/** Current smoothed latency for a URL, or Infinity if never measured. */
public latencyOf(url: string): number {
const record = this.health.get(url);
Expand Down
55 changes: 55 additions & 0 deletions src/contracts/providers/provider.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,58 @@ export interface ContractProvider {
*/
batchEthCall(requests: EthCallRequest[], options?: RequestOptions): Promise<BatchItemResult[]>;
}

// ─── WebSocket provider types ─────────────────────────────────────────────

/** A decoded ERC-20 Transfer event log. */
export type TransferEvent = {
from: string;
to: string;
value: bigint;
transactionHash: string;
blockNumber: number;
};

/** Callback invoked for each Transfer event received via subscription. */
export type TransferCallback = (event: TransferEvent) => void;

/** Configuration for the WebSocket-based contract event provider. */
export type WebSocketProviderConfig = {
/** WebSocket endpoint URL (ws:// or wss://). */
wssUrl: string;

/** Maximum number of reconnection attempts before giving up. Default: 10. */
maxReconnects?: number;

/** Initial backoff delay in milliseconds for reconnection. Default: 1000. */
baseDelayMs?: number;

/** Maximum backoff delay in milliseconds. Default: 30_000. */
maxDelayMs?: number;

/**
* Timeout in milliseconds for individual `eth_subscribe` confirmations.
* Default: 15_000.
*/
subscribeTimeoutMs?: number;

/**
* Per-request timeout in milliseconds for JSON-RPC calls over the
* WebSocket transport. When exceeded the request is rejected with a
* TIMEOUT error but the underlying persistent socket is NOT closed.
* Default: 10_000.
*/
requestTimeoutMs?: number;
};

/**
* Extension of {@link ContractProvider} that adds real-time event
* subscriptions via WebSocket.
*/
export interface SubscribableContractProvider extends ContractProvider {
/** Subscribe to Transfer events for a contract address. */
subscribe(contractAddress: string, callback: TransferCallback): Promise<() => void>;

/** Cleanly tear down the provider and all active subscriptions. */
destroy(): void;
}
4 changes: 3 additions & 1 deletion src/contracts/providers/webSocketProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export class WebSocketContractProvider implements SubscribableContractProvider {
private readonly baseDelayMs: number;
private readonly maxDelayMs: number;
private readonly subscribeTimeoutMs: number;
private readonly requestTimeoutMs: number;

// -----------------------------------------------------------------------
// State
Expand Down Expand Up @@ -149,6 +150,7 @@ export class WebSocketContractProvider implements SubscribableContractProvider {
this.baseDelayMs = config.baseDelayMs ?? 1000;
this.maxDelayMs = config.maxDelayMs ?? 30_000;
this.subscribeTimeoutMs = config.subscribeTimeoutMs ?? 15_000;
this.requestTimeoutMs = config.requestTimeoutMs ?? 10_000;

this.connect();
}
Expand Down Expand Up @@ -185,7 +187,7 @@ export class WebSocketContractProvider implements SubscribableContractProvider {
GuildPassErrorCode.TIMEOUT,
),
);
}, 30_000);
}, this.requestTimeoutMs);

this.pending.set(id, { resolve, reject, timer });

Expand Down
Loading