@usherlabs/cex-broker 0.2.19 → 0.2.21

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.
package/dist/index.js CHANGED
@@ -274531,6 +274531,8 @@ function hashMarketMetadata(payload) {
274531
274531
 
274532
274532
  // src/helpers/broker-execution-archive/types.ts
274533
274533
  var BROKER_WRITE_SOURCE = "broker_write";
274534
+ var ARCHIVE_SCHEMA_VERSION = "1";
274535
+ var FILL_EVENT_KIND = "trade_history_fill";
274534
274536
 
274535
274537
  // src/helpers/broker-execution-archive/rows.ts
274536
274538
  function firstString2(...values2) {
@@ -274544,6 +274546,26 @@ function firstString2(...values2) {
274544
274546
  }
274545
274547
  return;
274546
274548
  }
274549
+ function firstNumber2(...values2) {
274550
+ for (const value of values2) {
274551
+ const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
274552
+ if (Number.isFinite(numeric)) {
274553
+ return numeric;
274554
+ }
274555
+ }
274556
+ return;
274557
+ }
274558
+ function normalizeTimestamp2(value) {
274559
+ if (typeof value === "string" && value.trim()) {
274560
+ return value.trim();
274561
+ }
274562
+ const ms = firstNumber2(value);
274563
+ if (ms === undefined) {
274564
+ return;
274565
+ }
274566
+ const date = new Date(ms);
274567
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
274568
+ }
274547
274569
  function compactUndefined2(record) {
274548
274570
  return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
274549
274571
  }
@@ -274608,6 +274630,99 @@ function buildSubscribeStreamArchiveRow(input) {
274608
274630
  })
274609
274631
  };
274610
274632
  }
274633
+ function quantityString(...values2) {
274634
+ return firstString2(...values2);
274635
+ }
274636
+ function buildTransferEventArchiveRow(input) {
274637
+ const { tags, transfer } = input;
274638
+ return {
274639
+ table: "broker_execution.transfer_events",
274640
+ row: compactUndefined2({
274641
+ ...tags,
274642
+ schema_version: ARCHIVE_SCHEMA_VERSION,
274643
+ event_kind: transfer.eventKind,
274644
+ lifecycle_action: transfer.lifecycleAction,
274645
+ status: transfer.status ?? "",
274646
+ asset_symbol: tags.symbol,
274647
+ amount: transfer.amount,
274648
+ address: transfer.address,
274649
+ network: transfer.network,
274650
+ external_id: transfer.externalId ?? "",
274651
+ txid: transfer.txid,
274652
+ result_index: transfer.resultIndex ?? 0,
274653
+ fee_amount: transfer.feeAmount,
274654
+ fee_currency: transfer.feeCurrency,
274655
+ exchange_timestamp: transfer.exchangeTimestamp,
274656
+ error_summary: transfer.errorSummary,
274657
+ payload_json: JSON.stringify(transfer.payload)
274658
+ })
274659
+ };
274660
+ }
274661
+ function normalizeCcxtTransactionForArchive(transaction) {
274662
+ const record = asRecord(transaction);
274663
+ const info = asRecord(record?.info);
274664
+ const fee = asRecord(record?.fee);
274665
+ return compactUndefined2({
274666
+ externalId: firstString2(record?.id, info?.id, record?.txid, info?.txId),
274667
+ txid: firstString2(record?.txid, info?.txId, info?.txid, info?.tx_hash),
274668
+ address: firstString2(record?.address, record?.addressTo, info?.address),
274669
+ network: firstString2(record?.network, info?.network),
274670
+ amount: quantityString(info?.amount, record?.amount),
274671
+ assetSymbol: firstString2(record?.currency, info?.coin, info?.asset),
274672
+ status: firstString2(record?.status, info?.status)?.toLowerCase(),
274673
+ feeAmount: quantityString(fee?.cost, record?.feeCost),
274674
+ feeCurrency: firstString2(fee?.currency, record?.feeCurrency),
274675
+ exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.applyTime))
274676
+ });
274677
+ }
274678
+ function firstValueForTransfer(...values2) {
274679
+ return values2.find((value) => value !== undefined && value !== null);
274680
+ }
274681
+ function buildFillEventArchiveRow(input) {
274682
+ const { tags, fill } = input;
274683
+ return {
274684
+ table: "broker_execution.fill_events",
274685
+ row: compactUndefined2({
274686
+ ...tags,
274687
+ schema_version: ARCHIVE_SCHEMA_VERSION,
274688
+ event_kind: FILL_EVENT_KIND,
274689
+ order_id: fill.orderId ?? "",
274690
+ client_order_id: fill.clientOrderId,
274691
+ fill_id: fill.fillId,
274692
+ fill_index: fill.fillIndex ?? 0,
274693
+ side: fill.side,
274694
+ order_type: fill.orderType,
274695
+ price: fill.price,
274696
+ base_quantity: fill.baseQuantity,
274697
+ quote_quantity: fill.quoteQuantity,
274698
+ fee_amount: fill.feeAmount,
274699
+ fee_currency: fill.feeCurrency,
274700
+ fee_rate: fill.feeRate,
274701
+ exchange_timestamp: fill.exchangeTimestamp,
274702
+ payload_json: JSON.stringify(fill.payload)
274703
+ })
274704
+ };
274705
+ }
274706
+ function normalizeCcxtTradeForArchive(trade) {
274707
+ const record = asRecord(trade);
274708
+ const info = asRecord(record?.info);
274709
+ const fee = asRecord(record?.fee);
274710
+ return {
274711
+ orderId: firstString2(record?.order, info?.orderId, info?.orderID),
274712
+ clientOrderId: firstString2(record?.clientOrderId, info?.clientOrderId, info?.origClientOrderId),
274713
+ fillId: firstString2(record?.id, info?.id, info?.tradeId),
274714
+ side: firstString2(record?.side, info?.side)?.toLowerCase(),
274715
+ orderType: firstString2(record?.type, info?.type)?.toLowerCase(),
274716
+ price: quantityString(info?.price, record?.price),
274717
+ baseQuantity: quantityString(info?.qty, record?.amount),
274718
+ quoteQuantity: quantityString(info?.quoteQty, record?.cost),
274719
+ feeAmount: quantityString(fee?.cost, info?.commission),
274720
+ feeCurrency: firstString2(fee?.currency, info?.commissionAsset),
274721
+ feeRate: quantityString(fee?.rate),
274722
+ exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.time)),
274723
+ payload: trade
274724
+ };
274725
+ }
274611
274726
  function buildMarketMetadataSnapshotRow(input) {
274612
274727
  const redactedSnapshot = redactStreamPayload(input.marketSnapshot);
274613
274728
  const metadataHash = hashMarketMetadata(redactedSnapshot);
@@ -274676,6 +274791,25 @@ function archiveSubscribeStreamInBackground(archiver, input) {
274676
274791
  }
274677
274792
  });
274678
274793
  }
274794
+ function archiveTransferEventInBackground(archiver, input) {
274795
+ if (!archiver?.isEnabled()) {
274796
+ return;
274797
+ }
274798
+ queueMicrotask(() => {
274799
+ try {
274800
+ const tags = buildCommonArchiveTags({
274801
+ deploymentId: archiver.getDeploymentId(),
274802
+ accountSelector: input.accountSelector,
274803
+ exchange: input.exchange,
274804
+ symbol: input.assetSymbol,
274805
+ brokerObservedTimestamp: input.brokerObservedTimestamp
274806
+ });
274807
+ archiver.enqueue(buildTransferEventArchiveRow({ tags, transfer: input.transfer }));
274808
+ } catch (archiveError) {
274809
+ log.warn("Failed to archive transfer event", { error: archiveError });
274810
+ }
274811
+ });
274812
+ }
274679
274813
  async function captureMarketMetadataSnapshot(archiver, broker, input) {
274680
274814
  if (!archiver?.canPersistMarketMetadataSnapshot()) {
274681
274815
  return;
@@ -274721,6 +274855,10 @@ async function captureMarketMetadataSnapshot(archiver, broker, input) {
274721
274855
  return;
274722
274856
  }
274723
274857
  }
274858
+ // src/helpers/broker-execution-archive/writer.ts
274859
+ import { request as httpRequest2 } from "http";
274860
+ import { request as httpsRequest2 } from "https";
274861
+
274724
274862
  // node_modules/@opentelemetry/api-logs/build/esm/types/LogRecord.js
274725
274863
  var SeverityNumber2;
274726
274864
  (function(SeverityNumber3) {
@@ -275054,11 +275192,27 @@ class BrokerExecutionArchiver {
275054
275192
  }
275055
275193
  }
275056
275194
  this.stats.flushed += batch.length;
275195
+ this.recordFlushHealth(batch);
275057
275196
  return true;
275058
275197
  }
275059
- async recordArchiveMetric(metricName, labels) {
275198
+ recordFlushHealth(batch) {
275199
+ const countByTable = new Map;
275200
+ for (const entry of batch) {
275201
+ countByTable.set(entry.table, (countByTable.get(entry.table) ?? 0) + 1);
275202
+ }
275203
+ for (const [table, count2] of countByTable) {
275204
+ this.recordArchiveMetric("cex_archive_rows_flushed_total", { table }, count2);
275205
+ }
275206
+ this.recordArchiveGauge("cex_archive_last_flush_success", Math.floor(Date.now() / 1000));
275207
+ }
275208
+ async recordArchiveMetric(metricName, labels, value = 1) {
275060
275209
  try {
275061
- await this.otelMetrics?.recordCounter(metricName, 1, labels);
275210
+ await this.otelMetrics?.recordCounter(metricName, value, labels);
275211
+ } catch {}
275212
+ }
275213
+ async recordArchiveGauge(metricName, value) {
275214
+ try {
275215
+ await this.otelMetrics?.recordGauge(metricName, value, {});
275062
275216
  } catch {}
275063
275217
  }
275064
275218
  emitOtelLog(entry) {
@@ -275076,35 +275230,47 @@ class BrokerExecutionArchiver {
275076
275230
  log.warn("Broker execution archive OTLP emit failed", { error });
275077
275231
  }
275078
275232
  }
275079
- async postToForwarder(batch) {
275233
+ postToForwarder(batch) {
275080
275234
  if (!this.forwarderUrl || batch.length === 0) {
275081
- return;
275235
+ return Promise.resolve();
275082
275236
  }
275083
- const controller = new AbortController;
275084
- const timeout2 = setTimeout(() => controller.abort(), this.forwarderTimeoutMs);
275237
+ const body = JSON.stringify({
275238
+ source: "broker_write",
275239
+ deployment_id: this.deploymentId,
275240
+ rows: batch
275241
+ });
275242
+ const url2 = new URL(this.forwarderUrl);
275243
+ const doRequest = url2.protocol === "http:" ? httpRequest2 : httpsRequest2;
275085
275244
  const headers = {
275086
- "content-type": "application/json"
275245
+ "content-type": "application/json",
275246
+ "content-length": Buffer.byteLength(body)
275087
275247
  };
275088
275248
  if (this.forwarderAuthToken) {
275089
275249
  headers.authorization = `Bearer ${this.forwarderAuthToken}`;
275090
275250
  }
275091
- try {
275092
- const response = await fetch(this.forwarderUrl, {
275251
+ return new Promise((resolve, reject) => {
275252
+ const req = doRequest(url2, {
275093
275253
  method: "POST",
275094
275254
  headers,
275095
- body: JSON.stringify({
275096
- source: "broker_write",
275097
- deployment_id: this.deploymentId,
275098
- rows: batch
275099
- }),
275100
- signal: controller.signal
275255
+ timeout: this.forwarderTimeoutMs
275256
+ }, (res) => {
275257
+ res.on("data", () => {});
275258
+ res.on("end", () => {
275259
+ const status = res.statusCode ?? 0;
275260
+ if (status < 200 || status >= 300) {
275261
+ reject(new Error(`Archive forwarder returned ${status} ${res.statusMessage ?? ""}`));
275262
+ return;
275263
+ }
275264
+ resolve();
275265
+ });
275101
275266
  });
275102
- if (!response.ok) {
275103
- throw new Error(`Archive forwarder returned ${response.status} ${response.statusText}`);
275104
- }
275105
- } finally {
275106
- clearTimeout(timeout2);
275107
- }
275267
+ req.on("error", reject);
275268
+ req.on("timeout", () => {
275269
+ req.destroy(new Error("Archive forwarder request timed out"));
275270
+ });
275271
+ req.write(body);
275272
+ req.end();
275273
+ });
275108
275274
  }
275109
275275
  }
275110
275276
  function flattenArchiveAttributes(entry) {
@@ -275143,6 +275309,159 @@ function parsePositiveInt(value, fallback) {
275143
275309
  const parsed = Number.parseInt(value, 10);
275144
275310
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
275145
275311
  }
275312
+ // src/helpers/fill-archive-poller.ts
275313
+ var DEFAULT_CONFIG = {
275314
+ pollIntervalMs: 60000,
275315
+ lookbackMs: 24 * 60 * 60 * 1000,
275316
+ tradesLimit: 500
275317
+ };
275318
+ function nextFillCursor(trades, currentSince) {
275319
+ let next = currentSince;
275320
+ for (const trade of trades) {
275321
+ const ts = asRecord(trade)?.timestamp;
275322
+ if (typeof ts === "number" && Number.isFinite(ts) && ts + 1 > next) {
275323
+ next = ts + 1;
275324
+ }
275325
+ }
275326
+ return next;
275327
+ }
275328
+
275329
+ class FillArchivePoller {
275330
+ params;
275331
+ #timer = null;
275332
+ #stopped = false;
275333
+ #running = false;
275334
+ #cursors = new Map;
275335
+ #config;
275336
+ constructor(params) {
275337
+ this.params = params;
275338
+ this.#config = { ...DEFAULT_CONFIG, ...params.config };
275339
+ }
275340
+ start() {
275341
+ if (this.#timer || this.#stopped) {
275342
+ return;
275343
+ }
275344
+ if (!this.params.archiver.isEnabled()) {
275345
+ return;
275346
+ }
275347
+ log.info("\uD83E\uDDFE Fill archive poller started");
275348
+ this.#timer = setTimeout(() => void this.#tick(), 0);
275349
+ this.#timer.unref?.();
275350
+ }
275351
+ stop() {
275352
+ this.#stopped = true;
275353
+ if (this.#timer) {
275354
+ clearTimeout(this.#timer);
275355
+ this.#timer = null;
275356
+ }
275357
+ }
275358
+ async#tick() {
275359
+ if (this.#stopped || this.#running) {
275360
+ return;
275361
+ }
275362
+ this.#running = true;
275363
+ try {
275364
+ await this.pollTrackedOnce();
275365
+ } catch (error) {
275366
+ log.error("Fill archive poller tick failed", error);
275367
+ } finally {
275368
+ this.#running = false;
275369
+ if (!this.#stopped) {
275370
+ this.#timer = setTimeout(() => void this.#tick(), this.#config.pollIntervalMs);
275371
+ this.#timer.unref?.();
275372
+ }
275373
+ }
275374
+ }
275375
+ async pollTrackedOnce() {
275376
+ for (const entry of this.params.tracker.list()) {
275377
+ if (this.#stopped) {
275378
+ break;
275379
+ }
275380
+ await this.#pollOne(entry.exchangeId, entry.accountLabel, entry.symbol);
275381
+ }
275382
+ }
275383
+ async#pollOne(exchangeId, accountLabel, symbol) {
275384
+ const account = resolveBrokerAccount(this.params.brokers[exchangeId], accountLabel);
275385
+ if (!account) {
275386
+ return;
275387
+ }
275388
+ const exchange = account.exchange;
275389
+ if (typeof exchange.fetchMyTrades !== "function" || exchange.has?.fetchMyTrades === false) {
275390
+ return;
275391
+ }
275392
+ const key = `${exchangeId}|${accountLabel}|${symbol}`;
275393
+ const since = this.#cursors.get(key) ?? Date.now() - this.#config.lookbackMs;
275394
+ let trades;
275395
+ try {
275396
+ trades = await exchange.fetchMyTrades(symbol, since, this.#config.tradesLimit);
275397
+ } catch (error) {
275398
+ this.params.metrics?.recordCounter("cex_fill_poller_errors_total", 1, {
275399
+ exchange: exchangeId
275400
+ });
275401
+ log.warn("Fill archive poll failed", {
275402
+ exchange: exchangeId,
275403
+ account: accountLabel,
275404
+ symbol,
275405
+ error
275406
+ });
275407
+ return;
275408
+ }
275409
+ if (!Array.isArray(trades) || trades.length === 0) {
275410
+ return;
275411
+ }
275412
+ trades.forEach((trade, fillIndex) => {
275413
+ const tags = buildCommonArchiveTags({
275414
+ deploymentId: this.params.archiver.getDeploymentId(),
275415
+ accountSelector: accountLabel,
275416
+ exchange: exchangeId,
275417
+ symbol
275418
+ });
275419
+ this.params.archiver.enqueue(buildFillEventArchiveRow({
275420
+ tags,
275421
+ fill: { ...normalizeCcxtTradeForArchive(trade), fillIndex }
275422
+ }));
275423
+ });
275424
+ this.params.metrics?.recordCounter("cex_fill_poller_trades_archived_total", trades.length, { exchange: exchangeId });
275425
+ this.#cursors.set(key, nextFillCursor(trades, since));
275426
+ }
275427
+ }
275428
+
275429
+ // src/helpers/order-activity-tracker.ts
275430
+ var DEFAULT_MAX_AGE_MS = 6 * 60 * 60 * 1000;
275431
+
275432
+ class OrderActivityTracker {
275433
+ #entries = new Map;
275434
+ #maxAgeMs;
275435
+ constructor(options) {
275436
+ this.#maxAgeMs = options?.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
275437
+ }
275438
+ record(exchangeId, accountLabel, symbol, now3 = Date.now()) {
275439
+ const exchange = exchangeId.trim().toLowerCase();
275440
+ const trimmedSymbol = symbol.trim();
275441
+ if (!exchange || !accountLabel.trim() || !trimmedSymbol) {
275442
+ return;
275443
+ }
275444
+ const key = `${exchange}|${accountLabel}|${trimmedSymbol}`;
275445
+ this.#entries.set(key, {
275446
+ exchangeId: exchange,
275447
+ accountLabel,
275448
+ symbol: trimmedSymbol,
275449
+ lastActivityAt: now3
275450
+ });
275451
+ }
275452
+ list(now3 = Date.now()) {
275453
+ const active = [];
275454
+ for (const [key, entry] of this.#entries) {
275455
+ if (now3 - entry.lastActivityAt > this.#maxAgeMs) {
275456
+ this.#entries.delete(key);
275457
+ continue;
275458
+ }
275459
+ active.push(entry);
275460
+ }
275461
+ return active;
275462
+ }
275463
+ }
275464
+
275146
275465
  // src/helpers/otel.ts
275147
275466
  var import_api14 = __toESM(require_src2(), 1);
275148
275467
 
@@ -278110,11 +278429,11 @@ var JsonMetricsSerializer = {
278110
278429
  }
278111
278430
  };
278112
278431
  // node_modules/@opentelemetry/exporter-logs-otlp-http/build/esm/platform/node/OTLPLogExporter.js
278113
- var import_node_http2 = __toESM(require_index_node_http(), 1);
278432
+ var import_node_http3 = __toESM(require_index_node_http(), 1);
278114
278433
 
278115
278434
  class OTLPLogExporter extends import_otlp_exporter_base.OTLPExporterBase {
278116
278435
  constructor(config = {}) {
278117
- super(import_node_http2.createOtlpHttpExportDelegate(import_node_http2.convertLegacyHttpOptions(config, "LOGS", "v1/logs", {
278436
+ super(import_node_http3.createOtlpHttpExportDelegate(import_node_http3.convertLegacyHttpOptions(config, "LOGS", "v1/logs", {
278118
278437
  "Content-Type": "application/json"
278119
278438
  }), JsonLogsSerializer));
278120
278439
  }
@@ -278209,11 +278528,11 @@ class OTLPMetricExporterBase extends import_otlp_exporter_base2.OTLPExporterBase
278209
278528
  }
278210
278529
 
278211
278530
  // node_modules/@opentelemetry/exporter-metrics-otlp-http/build/esm/platform/node/OTLPMetricExporter.js
278212
- var import_node_http3 = __toESM(require_index_node_http(), 1);
278531
+ var import_node_http4 = __toESM(require_index_node_http(), 1);
278213
278532
 
278214
278533
  class OTLPMetricExporter extends OTLPMetricExporterBase {
278215
278534
  constructor(config) {
278216
- super(import_node_http3.createOtlpHttpExportDelegate(import_node_http3.convertLegacyHttpOptions(config ?? {}, "METRICS", "v1/metrics", {
278535
+ super(import_node_http4.createOtlpHttpExportDelegate(import_node_http4.convertLegacyHttpOptions(config ?? {}, "METRICS", "v1/metrics", {
278217
278536
  "Content-Type": "application/json"
278218
278537
  }), JsonMetricsSerializer), config);
278219
278538
  }
@@ -292891,7 +293210,13 @@ function rejectWithGrpcError(ctx, error48, options) {
292891
293210
 
292892
293211
  // src/handlers/execute-action/deposit.ts
292893
293212
  async function handleDeposit(ctx) {
292894
- const { normalizedCex, symbol: symbol2, selectedBrokerAccount, broker } = ctx;
293213
+ const {
293214
+ normalizedCex,
293215
+ symbol: symbol2,
293216
+ selectedBrokerAccount,
293217
+ broker,
293218
+ brokerArchiver
293219
+ } = ctx;
292895
293220
  if (!symbol2) {
292896
293221
  return ctx.wrappedCallback({
292897
293222
  code: grpc3.status.INVALID_ARGUMENT,
@@ -292954,6 +293279,32 @@ async function handleDeposit(ctx) {
292954
293279
  }, null);
292955
293280
  }
292956
293281
  const status4 = normalizeDepositStatus(depositField(deposit, ["status", "state"]));
293282
+ const depositTxid = String(depositField(deposit, ["txid", "txId", "tx_hash", "txHash"]) ?? value.transactionHash);
293283
+ const creditedAt = depositField(deposit, [
293284
+ "creditedAt",
293285
+ "credited_at",
293286
+ "updated",
293287
+ "updatedAt",
293288
+ "timestamp",
293289
+ "datetime"
293290
+ ]);
293291
+ archiveTransferEventInBackground(brokerArchiver, {
293292
+ exchange: normalizedCex,
293293
+ accountSelector: selectedBrokerAccount?.label,
293294
+ assetSymbol: symbol2,
293295
+ transfer: {
293296
+ eventKind: "deposit",
293297
+ lifecycleAction: "observe_deposit",
293298
+ status: status4,
293299
+ amount: observedAmount !== undefined ? String(observedAmount) : undefined,
293300
+ address: String(observedAddress ?? value.recipientAddress),
293301
+ network: depositNetwork?.exchangeNetworkId,
293302
+ externalId: depositTxid,
293303
+ txid: depositTxid,
293304
+ exchangeTimestamp: typeof creditedAt === "string" ? creditedAt : undefined,
293305
+ payload: deposit
293306
+ }
293307
+ });
292957
293308
  log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
292958
293309
  return ctx.wrappedCallback(null, {
292959
293310
  proof: ctx.verity.proof,
@@ -293367,7 +293718,8 @@ async function handleInternalTransfer(ctx) {
293367
293718
  symbol: symbol2,
293368
293719
  verity,
293369
293720
  useVerity,
293370
- verityProverUrl
293721
+ verityProverUrl,
293722
+ brokerArchiver
293371
293723
  } = ctx;
293372
293724
  if (!symbol2) {
293373
293725
  return ctx.wrappedCallback({
@@ -293415,6 +293767,21 @@ async function handleInternalTransfer(ctx) {
293415
293767
  }), verityHttpClientOverridePredicate);
293416
293768
  }
293417
293769
  const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
293770
+ const normalized = normalizeCcxtTransactionForArchive(result);
293771
+ archiveTransferEventInBackground(brokerArchiver, {
293772
+ exchange: normalizedCex,
293773
+ accountSelector: fromSelector,
293774
+ assetSymbol: symbol2,
293775
+ transfer: {
293776
+ eventKind: "internal_transfer",
293777
+ lifecycleAction: "submit_internal_transfer",
293778
+ status: normalized.status ?? "ok",
293779
+ amount: normalized.amount ?? String(transferPayload.amount),
293780
+ network: "internal",
293781
+ externalId: normalized.externalId,
293782
+ payload: { from: fromSelector, to: toSelector, result }
293783
+ }
293784
+ });
293418
293785
  ctx.wrappedCallback(null, {
293419
293786
  proof: verity.proof,
293420
293787
  result: JSON.stringify(result)
@@ -293462,7 +293829,8 @@ async function handleCreateOrder(ctx) {
293462
293829
  useVerity,
293463
293830
  verityProverUrl,
293464
293831
  otelMetrics,
293465
- brokerArchiver
293832
+ brokerArchiver,
293833
+ orderActivityTracker
293466
293834
  } = ctx;
293467
293835
  const verityProof = verity.proof;
293468
293836
  const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
@@ -293488,6 +293856,9 @@ async function handleCreateOrder(ctx) {
293488
293856
  side: resolution.side,
293489
293857
  requestedQuantity: resolution.amountBase ?? orderValue.amount
293490
293858
  };
293859
+ if (selectedBrokerAccount?.label) {
293860
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
293861
+ }
293491
293862
  const telemetryIds = extractOrderTelemetryIds(orderValue.params);
293492
293863
  const submissionTimestamp = new Date().toISOString();
293493
293864
  const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
@@ -293552,7 +293923,8 @@ async function handleGetOrderDetails(ctx) {
293552
293923
  useVerity,
293553
293924
  verityProverUrl,
293554
293925
  otelMetrics,
293555
- brokerArchiver
293926
+ brokerArchiver,
293927
+ orderActivityTracker
293556
293928
  } = ctx;
293557
293929
  const verityProof = verity.proof;
293558
293930
  const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
@@ -293566,6 +293938,9 @@ async function handleGetOrderDetails(ctx) {
293566
293938
  }, null);
293567
293939
  }
293568
293940
  const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
293941
+ if (selectedBrokerAccount?.label && symbol2) {
293942
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
293943
+ }
293569
293944
  const getOrderContext = {
293570
293945
  action: "GetOrderDetails",
293571
293946
  cex: cex3,
@@ -293621,7 +293996,8 @@ async function handleCancelOrder(ctx) {
293621
293996
  useVerity,
293622
293997
  verityProverUrl,
293623
293998
  otelMetrics,
293624
- brokerArchiver
293999
+ brokerArchiver,
294000
+ orderActivityTracker
293625
294001
  } = ctx;
293626
294002
  const verityProof = verity.proof;
293627
294003
  const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
@@ -293642,6 +294018,9 @@ async function handleCancelOrder(ctx) {
293642
294018
  ...extractOrderTelemetryIds(cancelOrderValue.params)
293643
294019
  };
293644
294020
  const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
294021
+ if (selectedBrokerAccount?.label && symbol2) {
294022
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
294023
+ }
293645
294024
  emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
293646
294025
  archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
293647
294026
  ctx.wrappedCallback(null, {
@@ -294272,7 +294651,8 @@ async function handleWithdraw(ctx) {
294272
294651
  applyVerityToBroker,
294273
294652
  useVerity,
294274
294653
  verityProverUrl,
294275
- otelMetrics
294654
+ otelMetrics,
294655
+ brokerArchiver
294276
294656
  } = ctx;
294277
294657
  const verityProof = verity.proof;
294278
294658
  if (!symbol2) {
@@ -294321,6 +294701,26 @@ async function handleWithdraw(ctx) {
294321
294701
  network: withdrawNetwork.exchangeNetworkId
294322
294702
  });
294323
294703
  log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
294704
+ const normalized = normalizeCcxtTransactionForArchive(transaction);
294705
+ archiveTransferEventInBackground(brokerArchiver, {
294706
+ exchange: cex3,
294707
+ accountSelector: selectedBrokerAccount?.label,
294708
+ assetSymbol: normalized.assetSymbol ?? symbol2,
294709
+ transfer: {
294710
+ eventKind: "withdrawal",
294711
+ lifecycleAction: "submit_withdrawal",
294712
+ status: normalized.status,
294713
+ amount: normalized.amount ?? String(transferValue.amount),
294714
+ address: normalized.address ?? transferValue.recipientAddress,
294715
+ network: normalized.network ?? withdrawNetwork.exchangeNetworkId,
294716
+ externalId: normalized.externalId,
294717
+ txid: normalized.txid,
294718
+ feeAmount: normalized.feeAmount,
294719
+ feeCurrency: normalized.feeCurrency,
294720
+ exchangeTimestamp: normalized.exchangeTimestamp,
294721
+ payload: transaction
294722
+ }
294723
+ });
294324
294724
  ctx.wrappedCallback(null, {
294325
294725
  proof: ctx.verity.proof,
294326
294726
  result: JSON.stringify({
@@ -294332,6 +294732,21 @@ async function handleWithdraw(ctx) {
294332
294732
  });
294333
294733
  } catch (error48) {
294334
294734
  safeLogError("Withdraw failed", error48);
294735
+ archiveTransferEventInBackground(brokerArchiver, {
294736
+ exchange: cex3,
294737
+ accountSelector: selectedBrokerAccount?.label,
294738
+ assetSymbol: symbol2,
294739
+ transfer: {
294740
+ eventKind: "withdrawal",
294741
+ lifecycleAction: "submit_withdrawal",
294742
+ status: "failed",
294743
+ amount: String(transferValue.amount),
294744
+ address: transferValue.recipientAddress,
294745
+ network: withdrawNetwork.exchangeNetworkId,
294746
+ errorSummary: getErrorMessage(error48),
294747
+ payload: { recipientAddress: transferValue.recipientAddress }
294748
+ }
294749
+ });
294335
294750
  const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
294336
294751
  ctx.wrappedCallback({
294337
294752
  code,
@@ -294379,7 +294794,8 @@ function createExecuteActionHandler(deps) {
294379
294794
  useVerity,
294380
294795
  verityProverUrl,
294381
294796
  otelMetrics,
294382
- brokerArchiver
294797
+ brokerArchiver,
294798
+ orderActivityTracker
294383
294799
  } = deps;
294384
294800
  return async (call, callback) => {
294385
294801
  const startTime = Date.now();
@@ -294458,7 +294874,8 @@ function createExecuteActionHandler(deps) {
294458
294874
  useVerity,
294459
294875
  verityProverUrl,
294460
294876
  otelMetrics,
294461
- brokerArchiver
294877
+ brokerArchiver,
294878
+ orderActivityTracker
294462
294879
  };
294463
294880
  if (action === Action.Call) {
294464
294881
  const handled = await handleOrderBookCall(preludeCtx);
@@ -296048,7 +296465,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
296048
296465
  // src/server.ts
296049
296466
  var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
296050
296467
  var cexNode = grpcObj.cex_broker;
296051
- function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
296468
+ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
296052
296469
  const server = new grpc14.Server;
296053
296470
  server.addService(cexNode.cex_service.service, {
296054
296471
  ExecuteAction: createExecuteActionHandler({
@@ -296058,7 +296475,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
296058
296475
  useVerity,
296059
296476
  verityProverUrl,
296060
296477
  otelMetrics,
296061
- brokerArchiver
296478
+ brokerArchiver,
296479
+ orderActivityTracker
296062
296480
  }),
296063
296481
  Subscribe: createSubscribeHandler({
296064
296482
  brokers,
@@ -296203,6 +296621,8 @@ class CEXBroker {
296203
296621
  otelLogs;
296204
296622
  brokerArchiver;
296205
296623
  depositReconciler;
296624
+ orderActivityTracker = new OrderActivityTracker;
296625
+ fillArchivePoller;
296206
296626
  loadEnvConfig() {
296207
296627
  log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
296208
296628
  const configMap = {};
@@ -296343,6 +296763,10 @@ class CEXBroker {
296343
296763
  this.depositReconciler.stop();
296344
296764
  this.depositReconciler = undefined;
296345
296765
  }
296766
+ if (this.fillArchivePoller) {
296767
+ this.fillArchivePoller.stop();
296768
+ this.fillArchivePoller = undefined;
296769
+ }
296346
296770
  if (this.server) {
296347
296771
  await this.server.forceShutdown();
296348
296772
  }
@@ -296364,11 +296788,15 @@ class CEXBroker {
296364
296788
  this.depositReconciler.stop();
296365
296789
  this.depositReconciler = undefined;
296366
296790
  }
296791
+ if (this.fillArchivePoller) {
296792
+ this.fillArchivePoller.stop();
296793
+ this.fillArchivePoller = undefined;
296794
+ }
296367
296795
  log.info(`Running CEXBroker at ${new Date().toISOString()}`);
296368
296796
  if (this.otelMetrics?.isOtelEnabled()) {
296369
296797
  await this.otelMetrics.initialize();
296370
296798
  }
296371
- this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver);
296799
+ this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
296372
296800
  this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
296373
296801
  if (err2) {
296374
296802
  log.error(err2);
@@ -296383,6 +296811,15 @@ class CEXBroker {
296383
296811
  metrics: this.otelMetrics
296384
296812
  });
296385
296813
  this.depositReconciler.start();
296814
+ if (this.brokerArchiver?.isEnabled()) {
296815
+ this.fillArchivePoller = new FillArchivePoller({
296816
+ brokers: this.brokers,
296817
+ archiver: this.brokerArchiver,
296818
+ tracker: this.orderActivityTracker,
296819
+ metrics: this.otelMetrics
296820
+ });
296821
+ this.fillArchivePoller.start();
296822
+ }
296386
296823
  return this;
296387
296824
  }
296388
296825
  }
@@ -296390,4 +296827,4 @@ export {
296390
296827
  CEXBroker as default
296391
296828
  };
296392
296829
 
296393
- //# debugId=FD2894A2A920C90B64756E2164756E21
296830
+ //# debugId=72B06323144A22CC64756E2164756E21