@usherlabs/cex-broker 0.2.20 → 0.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@ import type { z } from "zod";
5
5
  import type { BrokerAccount, BrokerPoolEntry } from "../../helpers";
6
6
  import type { BrokerExecutionArchiver } from "../../helpers/broker-execution-archive";
7
7
  import type { Action as ActionType } from "../../helpers/constants";
8
+ import type { OrderActivityTracker } from "../../helpers/order-activity-tracker";
8
9
  import type { OtelMetrics } from "../../helpers/otel";
9
10
  import type { PolicyConfig } from "../../types";
10
11
  import type { ActionRequest, ActionResponse } from "../types";
@@ -29,6 +30,7 @@ export type ExecuteActionContext = {
29
30
  verityProverUrl: string;
30
31
  otelMetrics?: OtelMetrics;
31
32
  brokerArchiver?: BrokerExecutionArchiver;
33
+ orderActivityTracker?: OrderActivityTracker;
32
34
  };
33
35
  export declare function requireSymbol(ctx: ExecuteActionContext, message?: string): ctx is ExecuteActionContext & {
34
36
  symbol: string;
@@ -38,5 +40,10 @@ export declare function rejectWithGrpcError(ctx: ExecuteActionContext, error: un
38
40
  message?: string;
39
41
  prefix?: string;
40
42
  preferStableMessageOnly?: boolean;
43
+ /** Append the caught error's class name (e.g. InsufficientFunds) as a
44
+ * suffix. It goes last, not in front, because the gRPC status code is
45
+ * derived from the raw message via stableGrpcErrorCode; prepending the
46
+ * class name would break that prefix match. */
47
+ appendClassName?: boolean;
41
48
  }): void;
42
49
  export declare function successWithProof(ctx: ExecuteActionContext, result: unknown): void;
@@ -1,6 +1,7 @@
1
1
  import * as grpc from "@grpc/grpc-js";
2
2
  import { type BrokerPoolEntry } from "../../helpers/broker";
3
3
  import type { BrokerExecutionArchiver } from "../../helpers/broker-execution-archive";
4
+ import type { OrderActivityTracker } from "../../helpers/order-activity-tracker";
4
5
  import type { OtelMetrics } from "../../helpers/otel";
5
6
  import type { PolicyConfig } from "../../types";
6
7
  import type { ActionRequest, ActionResponse } from "../types";
@@ -12,5 +13,6 @@ export type ExecuteActionDeps = {
12
13
  verityProverUrl: string;
13
14
  otelMetrics?: OtelMetrics;
14
15
  brokerArchiver?: BrokerExecutionArchiver;
16
+ orderActivityTracker?: OrderActivityTracker;
15
17
  };
16
18
  export declare function createExecuteActionHandler(deps: ExecuteActionDeps): (call: grpc.ServerUnaryCall<ActionRequest, ActionResponse>, callback: grpc.sendUnaryData<ActionResponse>) => Promise<void>;
@@ -1,5 +1,6 @@
1
1
  import type { Exchange } from "@usherlabs/ccxt";
2
2
  import { type OrderTelemetryAction, type OrderTelemetryContext } from "../order-telemetry";
3
+ import { type TransferArchiveFields } from "./rows";
3
4
  import type { SubscribeArchiveType } from "./types";
4
5
  import type { BrokerExecutionArchiver } from "./writer";
5
6
  export declare function archiveOrderExecutionInBackground(archiver: BrokerExecutionArchiver | undefined, context: OrderTelemetryContext, order: unknown, error?: unknown, options?: {
@@ -13,6 +14,13 @@ export declare function archiveSubscribeStreamInBackground(archiver: BrokerExecu
13
14
  streamPayload: unknown;
14
15
  secretLiterals?: readonly string[];
15
16
  }): void;
17
+ export declare function archiveTransferEventInBackground(archiver: BrokerExecutionArchiver | undefined, input: {
18
+ exchange: string;
19
+ accountSelector?: string;
20
+ assetSymbol?: string;
21
+ brokerObservedTimestamp?: string;
22
+ transfer: TransferArchiveFields;
23
+ }): void;
16
24
  export declare function captureMarketMetadataSnapshot(archiver: BrokerExecutionArchiver | undefined, broker: Exchange, input: {
17
25
  exchange: string;
18
26
  accountSelector?: string;
@@ -1,5 +1,5 @@
1
- export { archiveOrderExecutionInBackground, archiveSubscribeStreamInBackground, captureMarketMetadataSnapshot, captureMarketMetadataSnapshotInBackground, } from "./capture";
1
+ export { archiveOrderExecutionInBackground, archiveSubscribeStreamInBackground, archiveTransferEventInBackground, captureMarketMetadataSnapshot, captureMarketMetadataSnapshotInBackground, } from "./capture";
2
2
  export { hashMarketMetadata, redactErrorForArchive, redactSecretLiterals, redactStreamPayload, } from "./redact";
3
- export { buildCommonArchiveTags, buildMarketMetadataSnapshotRow, buildOrderEventArchiveRow, buildSubscribeStreamArchiveRow, } from "./rows";
4
- export { BROKER_WRITE_SOURCE, type BrokerArchiveCommonTags, type BrokerArchiveRow, type BrokerArchiveTable, type OrderArchiveAction, type SubscribeArchiveType, } from "./types";
3
+ export { buildCommonArchiveTags, buildFillEventArchiveRow, buildMarketMetadataSnapshotRow, buildOrderEventArchiveRow, buildSubscribeStreamArchiveRow, buildTransferEventArchiveRow, type FillArchiveFields, normalizeCcxtTradeForArchive, normalizeCcxtTransactionForArchive, type NormalizedCcxtTransfer, type TransferArchiveFields, } from "./rows";
4
+ export { ARCHIVE_SCHEMA_VERSION, BROKER_WRITE_SOURCE, type BrokerArchiveCommonTags, type BrokerArchiveRow, type BrokerArchiveTable, type OrderArchiveAction, type SubscribeArchiveType, type TransferEventKind, type TransferLifecycleAction, } from "./types";
5
5
  export { BrokerExecutionArchiver, type BrokerExecutionArchiverOptions, createBrokerExecutionArchiverFromEnv, isArchiveOtelLogsEnabled, resolveArchiveForwarderUrlFromEnv, } from "./writer";
@@ -1,5 +1,5 @@
1
1
  import type { OrderExecutionTelemetry } from "../order-telemetry";
2
- import { type BrokerArchiveCommonTags, type BrokerArchiveRow, type OrderArchiveAction, type SubscribeArchiveType } from "./types";
2
+ import { type BrokerArchiveCommonTags, type BrokerArchiveRow, type OrderArchiveAction, type SubscribeArchiveType, type TransferEventKind, type TransferLifecycleAction } from "./types";
3
3
  export declare function buildCommonArchiveTags(input: {
4
4
  deploymentId: string;
5
5
  accountSelector?: string;
@@ -21,6 +21,60 @@ export declare function buildSubscribeStreamArchiveRow(input: {
21
21
  streamPayload: unknown;
22
22
  secretLiterals?: readonly string[];
23
23
  }): BrokerArchiveRow;
24
+ export type TransferArchiveFields = {
25
+ eventKind: TransferEventKind;
26
+ lifecycleAction: TransferLifecycleAction;
27
+ status?: string;
28
+ amount?: string;
29
+ address?: string;
30
+ network?: string;
31
+ externalId?: string;
32
+ txid?: string;
33
+ resultIndex?: number;
34
+ feeAmount?: string;
35
+ feeCurrency?: string;
36
+ exchangeTimestamp?: string;
37
+ errorSummary?: string;
38
+ payload: unknown;
39
+ };
40
+ export declare function buildTransferEventArchiveRow(input: {
41
+ tags: BrokerArchiveCommonTags;
42
+ transfer: TransferArchiveFields;
43
+ }): BrokerArchiveRow;
44
+ export type NormalizedCcxtTransfer = {
45
+ externalId?: string;
46
+ txid?: string;
47
+ address?: string;
48
+ network?: string;
49
+ amount?: string;
50
+ assetSymbol?: string;
51
+ status?: string;
52
+ feeAmount?: string;
53
+ feeCurrency?: string;
54
+ exchangeTimestamp?: string;
55
+ };
56
+ export declare function normalizeCcxtTransactionForArchive(transaction: unknown): NormalizedCcxtTransfer;
57
+ export type FillArchiveFields = {
58
+ orderId?: string;
59
+ clientOrderId?: string;
60
+ fillId?: string;
61
+ fillIndex?: number;
62
+ side?: string;
63
+ orderType?: string;
64
+ price?: string;
65
+ baseQuantity?: string;
66
+ quoteQuantity?: string;
67
+ feeAmount?: string;
68
+ feeCurrency?: string;
69
+ feeRate?: string;
70
+ exchangeTimestamp?: string;
71
+ payload: unknown;
72
+ };
73
+ export declare function buildFillEventArchiveRow(input: {
74
+ tags: BrokerArchiveCommonTags;
75
+ fill: FillArchiveFields;
76
+ }): BrokerArchiveRow;
77
+ export declare function normalizeCcxtTradeForArchive(trade: unknown): FillArchiveFields;
24
78
  export declare function buildMarketMetadataSnapshotRow(input: {
25
79
  tags: BrokerArchiveCommonTags;
26
80
  clientOrderId?: string;
@@ -1,5 +1,6 @@
1
1
  export declare const BROKER_WRITE_SOURCE: "broker_write";
2
- export type BrokerArchiveTable = "broker_execution.order_events" | "broker_execution.market_metadata_snapshots" | "market_data.orderbook_snapshots" | "market_data.candles" | "market_data.cex_stream_events" | "market_data.cex_ticker_events" | "market_data.cex_trades";
2
+ export declare const ARCHIVE_SCHEMA_VERSION: "1";
3
+ export type BrokerArchiveTable = "broker_execution.order_events" | "broker_execution.market_metadata_snapshots" | "broker_execution.transfer_events" | "broker_execution.fill_events" | "market_data.orderbook_snapshots" | "market_data.candles" | "market_data.cex_stream_events" | "market_data.cex_ticker_events" | "market_data.cex_trades";
3
4
  export type BrokerArchiveRow = {
4
5
  table: BrokerArchiveTable;
5
6
  row: Record<string, unknown>;
@@ -14,3 +15,6 @@ export type BrokerArchiveCommonTags = {
14
15
  };
15
16
  export type OrderArchiveAction = "CreateOrder" | "CancelOrder" | "GetOrderDetails";
16
17
  export type SubscribeArchiveType = "ORDERS" | "BALANCE";
18
+ export type TransferEventKind = "withdrawal" | "deposit" | "internal_transfer";
19
+ export type TransferLifecycleAction = "submit_withdrawal" | "observe_deposit" | "submit_internal_transfer";
20
+ export declare const FILL_EVENT_KIND: "trade_history_fill";
@@ -49,7 +49,9 @@ export declare class BrokerExecutionArchiver {
49
49
  getQueueDepth(): number;
50
50
  private enforceQueueBound;
51
51
  private flushBatch;
52
+ private recordFlushHealth;
52
53
  private recordArchiveMetric;
54
+ private recordArchiveGauge;
53
55
  private emitOtelLog;
54
56
  private postToForwarder;
55
57
  }
@@ -0,0 +1,42 @@
1
+ import type { BrokerPoolEntry } from "./broker";
2
+ import { type BrokerExecutionArchiver } from "./broker-execution-archive";
3
+ import type { OrderActivityTracker } from "./order-activity-tracker";
4
+ import type { OtelMetrics } from "./otel";
5
+ export type FillArchivePollerConfig = {
6
+ pollIntervalMs: number;
7
+ lookbackMs: number;
8
+ tradesLimit: number;
9
+ };
10
+ /**
11
+ * Advances a since-cursor past the newest trade in a batch. The next poll asks for
12
+ * trades strictly after the last one seen (+1ms) so the same trade is not refetched
13
+ * every tick, while still tolerating out-of-order timestamps within a batch.
14
+ */
15
+ export declare function nextFillCursor(trades: unknown[], currentSince: number): number;
16
+ /**
17
+ * Broker-internal periodic capture of per-fill facts. For each (account, symbol)
18
+ * that saw recent order activity it calls the venue trade-history endpoint
19
+ * (fetchMyTrades) with a since-cursor and archives each trade to
20
+ * broker_execution.fill_events. Started at bootstrap only when archiving is
21
+ * enabled; symbols are staggered by sequential awaits so one tick never bursts the
22
+ * venue rate limit. Mirrors the TravelRuleDepositReconciler lifecycle.
23
+ */
24
+ export declare class FillArchivePoller {
25
+ #private;
26
+ private readonly params;
27
+ constructor(params: {
28
+ brokers: Record<string, BrokerPoolEntry>;
29
+ archiver: BrokerExecutionArchiver;
30
+ tracker: OrderActivityTracker;
31
+ metrics?: OtelMetrics;
32
+ config?: Partial<FillArchivePollerConfig>;
33
+ });
34
+ start(): void;
35
+ stop(): void;
36
+ /**
37
+ * Runs one poll pass over every tracked (account, symbol), sequentially so the
38
+ * per-symbol awaits stagger venue calls. Exposed for tests (the timer loop calls
39
+ * it every tick).
40
+ */
41
+ pollTrackedOnce(): Promise<void>;
42
+ }
@@ -0,0 +1,22 @@
1
+ export type OrderActivityEntry = {
2
+ exchangeId: string;
3
+ accountLabel: string;
4
+ symbol: string;
5
+ lastActivityAt: number;
6
+ };
7
+ /**
8
+ * Records the (exchange, account, symbol) tuples that saw order activity through
9
+ * the execute-action path, so the fill poller scans only those markets instead of
10
+ * the whole exchange. Process-lifetime, in-memory only (no persistence needed: on
11
+ * restart the poller re-derives the set from fresh order activity and read-time
12
+ * dedup absorbs the lookback re-scan).
13
+ */
14
+ export declare class OrderActivityTracker {
15
+ #private;
16
+ constructor(options?: {
17
+ maxAgeMs?: number;
18
+ });
19
+ record(exchangeId: string, accountLabel: string, symbol: string, now?: number): void;
20
+ /** Active entries (seen within maxAge). Prunes stale entries as a side effect. */
21
+ list(now?: number): OrderActivityEntry[];
22
+ }
@@ -1,2 +1,12 @@
1
1
  export declare function getErrorMessage(error: unknown): string;
2
+ /** Constructor/class name of a caught error (ccxt classes like InsufficientFunds,
3
+ * BadSymbol, OrderNotFound carry the actionable signal). The generic `Error` name
4
+ * adds nothing, so it is dropped; returns undefined for non-Error values. */
5
+ export declare function errorClassName(error: unknown): string | undefined;
6
+ /** Sanitized, single-line, length-capped detail for a caught error: the venue
7
+ * error class name plus its message. Safe to return to gRPC callers — it carries
8
+ * only the exchange's own error text (class + message), never a stack or payload,
9
+ * so downstream consumers can distinguish e.g. InsufficientFunds from BadSymbol
10
+ * instead of an opaque "<action> failed". */
11
+ export declare function sanitizeErrorDetail(error: unknown): string;
2
12
  export declare function safeLogError(context: string, error: unknown): void;
package/dist/index.d.ts CHANGED
@@ -13,6 +13,8 @@ export default class CEXBroker {
13
13
  private otelLogs?;
14
14
  private brokerArchiver?;
15
15
  private depositReconciler?;
16
+ private readonly orderActivityTracker;
17
+ private fillArchivePoller?;
16
18
  /**
17
19
  * Loads environment variables prefixed with CEX_BROKER_
18
20
  * Expected format: