@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.
@@ -315278,6 +315278,8 @@ function hashMarketMetadata(payload) {
315278
315278
 
315279
315279
  // src/helpers/broker-execution-archive/types.ts
315280
315280
  var BROKER_WRITE_SOURCE = "broker_write";
315281
+ var ARCHIVE_SCHEMA_VERSION = "1";
315282
+ var FILL_EVENT_KIND = "trade_history_fill";
315281
315283
 
315282
315284
  // src/helpers/broker-execution-archive/rows.ts
315283
315285
  function firstString2(...values2) {
@@ -315291,6 +315293,26 @@ function firstString2(...values2) {
315291
315293
  }
315292
315294
  return;
315293
315295
  }
315296
+ function firstNumber2(...values2) {
315297
+ for (const value of values2) {
315298
+ const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
315299
+ if (Number.isFinite(numeric)) {
315300
+ return numeric;
315301
+ }
315302
+ }
315303
+ return;
315304
+ }
315305
+ function normalizeTimestamp2(value) {
315306
+ if (typeof value === "string" && value.trim()) {
315307
+ return value.trim();
315308
+ }
315309
+ const ms = firstNumber2(value);
315310
+ if (ms === undefined) {
315311
+ return;
315312
+ }
315313
+ const date = new Date(ms);
315314
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
315315
+ }
315294
315316
  function compactUndefined2(record) {
315295
315317
  return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
315296
315318
  }
@@ -315355,6 +315377,99 @@ function buildSubscribeStreamArchiveRow(input) {
315355
315377
  })
315356
315378
  };
315357
315379
  }
315380
+ function quantityString(...values2) {
315381
+ return firstString2(...values2);
315382
+ }
315383
+ function buildTransferEventArchiveRow(input) {
315384
+ const { tags, transfer } = input;
315385
+ return {
315386
+ table: "broker_execution.transfer_events",
315387
+ row: compactUndefined2({
315388
+ ...tags,
315389
+ schema_version: ARCHIVE_SCHEMA_VERSION,
315390
+ event_kind: transfer.eventKind,
315391
+ lifecycle_action: transfer.lifecycleAction,
315392
+ status: transfer.status ?? "",
315393
+ asset_symbol: tags.symbol,
315394
+ amount: transfer.amount,
315395
+ address: transfer.address,
315396
+ network: transfer.network,
315397
+ external_id: transfer.externalId ?? "",
315398
+ txid: transfer.txid,
315399
+ result_index: transfer.resultIndex ?? 0,
315400
+ fee_amount: transfer.feeAmount,
315401
+ fee_currency: transfer.feeCurrency,
315402
+ exchange_timestamp: transfer.exchangeTimestamp,
315403
+ error_summary: transfer.errorSummary,
315404
+ payload_json: JSON.stringify(transfer.payload)
315405
+ })
315406
+ };
315407
+ }
315408
+ function normalizeCcxtTransactionForArchive(transaction) {
315409
+ const record = asRecord(transaction);
315410
+ const info = asRecord(record?.info);
315411
+ const fee = asRecord(record?.fee);
315412
+ return compactUndefined2({
315413
+ externalId: firstString2(record?.id, info?.id, record?.txid, info?.txId),
315414
+ txid: firstString2(record?.txid, info?.txId, info?.txid, info?.tx_hash),
315415
+ address: firstString2(record?.address, record?.addressTo, info?.address),
315416
+ network: firstString2(record?.network, info?.network),
315417
+ amount: quantityString(info?.amount, record?.amount),
315418
+ assetSymbol: firstString2(record?.currency, info?.coin, info?.asset),
315419
+ status: firstString2(record?.status, info?.status)?.toLowerCase(),
315420
+ feeAmount: quantityString(fee?.cost, record?.feeCost),
315421
+ feeCurrency: firstString2(fee?.currency, record?.feeCurrency),
315422
+ exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.applyTime))
315423
+ });
315424
+ }
315425
+ function firstValueForTransfer(...values2) {
315426
+ return values2.find((value) => value !== undefined && value !== null);
315427
+ }
315428
+ function buildFillEventArchiveRow(input) {
315429
+ const { tags, fill } = input;
315430
+ return {
315431
+ table: "broker_execution.fill_events",
315432
+ row: compactUndefined2({
315433
+ ...tags,
315434
+ schema_version: ARCHIVE_SCHEMA_VERSION,
315435
+ event_kind: FILL_EVENT_KIND,
315436
+ order_id: fill.orderId ?? "",
315437
+ client_order_id: fill.clientOrderId,
315438
+ fill_id: fill.fillId,
315439
+ fill_index: fill.fillIndex ?? 0,
315440
+ side: fill.side,
315441
+ order_type: fill.orderType,
315442
+ price: fill.price,
315443
+ base_quantity: fill.baseQuantity,
315444
+ quote_quantity: fill.quoteQuantity,
315445
+ fee_amount: fill.feeAmount,
315446
+ fee_currency: fill.feeCurrency,
315447
+ fee_rate: fill.feeRate,
315448
+ exchange_timestamp: fill.exchangeTimestamp,
315449
+ payload_json: JSON.stringify(fill.payload)
315450
+ })
315451
+ };
315452
+ }
315453
+ function normalizeCcxtTradeForArchive(trade) {
315454
+ const record = asRecord(trade);
315455
+ const info = asRecord(record?.info);
315456
+ const fee = asRecord(record?.fee);
315457
+ return {
315458
+ orderId: firstString2(record?.order, info?.orderId, info?.orderID),
315459
+ clientOrderId: firstString2(record?.clientOrderId, info?.clientOrderId, info?.origClientOrderId),
315460
+ fillId: firstString2(record?.id, info?.id, info?.tradeId),
315461
+ side: firstString2(record?.side, info?.side)?.toLowerCase(),
315462
+ orderType: firstString2(record?.type, info?.type)?.toLowerCase(),
315463
+ price: quantityString(info?.price, record?.price),
315464
+ baseQuantity: quantityString(info?.qty, record?.amount),
315465
+ quoteQuantity: quantityString(info?.quoteQty, record?.cost),
315466
+ feeAmount: quantityString(fee?.cost, info?.commission),
315467
+ feeCurrency: firstString2(fee?.currency, info?.commissionAsset),
315468
+ feeRate: quantityString(fee?.rate),
315469
+ exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.time)),
315470
+ payload: trade
315471
+ };
315472
+ }
315358
315473
  function buildMarketMetadataSnapshotRow(input) {
315359
315474
  const redactedSnapshot = redactStreamPayload(input.marketSnapshot);
315360
315475
  const metadataHash = hashMarketMetadata(redactedSnapshot);
@@ -315423,6 +315538,25 @@ function archiveSubscribeStreamInBackground(archiver, input) {
315423
315538
  }
315424
315539
  });
315425
315540
  }
315541
+ function archiveTransferEventInBackground(archiver, input) {
315542
+ if (!archiver?.isEnabled()) {
315543
+ return;
315544
+ }
315545
+ queueMicrotask(() => {
315546
+ try {
315547
+ const tags = buildCommonArchiveTags({
315548
+ deploymentId: archiver.getDeploymentId(),
315549
+ accountSelector: input.accountSelector,
315550
+ exchange: input.exchange,
315551
+ symbol: input.assetSymbol,
315552
+ brokerObservedTimestamp: input.brokerObservedTimestamp
315553
+ });
315554
+ archiver.enqueue(buildTransferEventArchiveRow({ tags, transfer: input.transfer }));
315555
+ } catch (archiveError) {
315556
+ log.warn("Failed to archive transfer event", { error: archiveError });
315557
+ }
315558
+ });
315559
+ }
315426
315560
  async function captureMarketMetadataSnapshot(archiver, broker, input) {
315427
315561
  if (!archiver?.canPersistMarketMetadataSnapshot()) {
315428
315562
  return;
@@ -315470,6 +315604,8 @@ async function captureMarketMetadataSnapshot(archiver, broker, input) {
315470
315604
  }
315471
315605
  // src/helpers/broker-execution-archive/writer.ts
315472
315606
  var import_api_logs2 = __toESM(require_src7(), 1);
315607
+ import { request as httpRequest2 } from "node:http";
315608
+ import { request as httpsRequest2 } from "node:https";
315473
315609
 
315474
315610
  // src/helpers/market-data-archive/types.ts
315475
315611
  var MARKET_ARCHIVE_TABLES = new Set([
@@ -315676,11 +315812,27 @@ class BrokerExecutionArchiver {
315676
315812
  }
315677
315813
  }
315678
315814
  this.stats.flushed += batch.length;
315815
+ this.recordFlushHealth(batch);
315679
315816
  return true;
315680
315817
  }
315681
- async recordArchiveMetric(metricName, labels) {
315818
+ recordFlushHealth(batch) {
315819
+ const countByTable = new Map;
315820
+ for (const entry of batch) {
315821
+ countByTable.set(entry.table, (countByTable.get(entry.table) ?? 0) + 1);
315822
+ }
315823
+ for (const [table, count2] of countByTable) {
315824
+ this.recordArchiveMetric("cex_archive_rows_flushed_total", { table }, count2);
315825
+ }
315826
+ this.recordArchiveGauge("cex_archive_last_flush_success", Math.floor(Date.now() / 1000));
315827
+ }
315828
+ async recordArchiveMetric(metricName, labels, value = 1) {
315829
+ try {
315830
+ await this.otelMetrics?.recordCounter(metricName, value, labels);
315831
+ } catch {}
315832
+ }
315833
+ async recordArchiveGauge(metricName, value) {
315682
315834
  try {
315683
- await this.otelMetrics?.recordCounter(metricName, 1, labels);
315835
+ await this.otelMetrics?.recordGauge(metricName, value, {});
315684
315836
  } catch {}
315685
315837
  }
315686
315838
  emitOtelLog(entry) {
@@ -315698,35 +315850,47 @@ class BrokerExecutionArchiver {
315698
315850
  log.warn("Broker execution archive OTLP emit failed", { error });
315699
315851
  }
315700
315852
  }
315701
- async postToForwarder(batch) {
315853
+ postToForwarder(batch) {
315702
315854
  if (!this.forwarderUrl || batch.length === 0) {
315703
- return;
315855
+ return Promise.resolve();
315704
315856
  }
315705
- const controller = new AbortController;
315706
- const timeout2 = setTimeout(() => controller.abort(), this.forwarderTimeoutMs);
315857
+ const body = JSON.stringify({
315858
+ source: "broker_write",
315859
+ deployment_id: this.deploymentId,
315860
+ rows: batch
315861
+ });
315862
+ const url2 = new URL(this.forwarderUrl);
315863
+ const doRequest = url2.protocol === "http:" ? httpRequest2 : httpsRequest2;
315707
315864
  const headers = {
315708
- "content-type": "application/json"
315865
+ "content-type": "application/json",
315866
+ "content-length": Buffer.byteLength(body)
315709
315867
  };
315710
315868
  if (this.forwarderAuthToken) {
315711
315869
  headers.authorization = `Bearer ${this.forwarderAuthToken}`;
315712
315870
  }
315713
- try {
315714
- const response = await fetch(this.forwarderUrl, {
315871
+ return new Promise((resolve, reject) => {
315872
+ const req = doRequest(url2, {
315715
315873
  method: "POST",
315716
315874
  headers,
315717
- body: JSON.stringify({
315718
- source: "broker_write",
315719
- deployment_id: this.deploymentId,
315720
- rows: batch
315721
- }),
315722
- signal: controller.signal
315875
+ timeout: this.forwarderTimeoutMs
315876
+ }, (res) => {
315877
+ res.on("data", () => {});
315878
+ res.on("end", () => {
315879
+ const status = res.statusCode ?? 0;
315880
+ if (status < 200 || status >= 300) {
315881
+ reject(new Error(`Archive forwarder returned ${status} ${res.statusMessage ?? ""}`));
315882
+ return;
315883
+ }
315884
+ resolve();
315885
+ });
315723
315886
  });
315724
- if (!response.ok) {
315725
- throw new Error(`Archive forwarder returned ${response.status} ${response.statusText}`);
315726
- }
315727
- } finally {
315728
- clearTimeout(timeout2);
315729
- }
315887
+ req.on("error", reject);
315888
+ req.on("timeout", () => {
315889
+ req.destroy(new Error("Archive forwarder request timed out"));
315890
+ });
315891
+ req.write(body);
315892
+ req.end();
315893
+ });
315730
315894
  }
315731
315895
  }
315732
315896
  function flattenArchiveAttributes(entry) {
@@ -315765,6 +315929,159 @@ function parsePositiveInt(value, fallback) {
315765
315929
  const parsed = Number.parseInt(value, 10);
315766
315930
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
315767
315931
  }
315932
+ // src/helpers/fill-archive-poller.ts
315933
+ var DEFAULT_CONFIG = {
315934
+ pollIntervalMs: 60000,
315935
+ lookbackMs: 24 * 60 * 60 * 1000,
315936
+ tradesLimit: 500
315937
+ };
315938
+ function nextFillCursor(trades, currentSince) {
315939
+ let next = currentSince;
315940
+ for (const trade of trades) {
315941
+ const ts = asRecord(trade)?.timestamp;
315942
+ if (typeof ts === "number" && Number.isFinite(ts) && ts + 1 > next) {
315943
+ next = ts + 1;
315944
+ }
315945
+ }
315946
+ return next;
315947
+ }
315948
+
315949
+ class FillArchivePoller {
315950
+ params;
315951
+ #timer = null;
315952
+ #stopped = false;
315953
+ #running = false;
315954
+ #cursors = new Map;
315955
+ #config;
315956
+ constructor(params) {
315957
+ this.params = params;
315958
+ this.#config = { ...DEFAULT_CONFIG, ...params.config };
315959
+ }
315960
+ start() {
315961
+ if (this.#timer || this.#stopped) {
315962
+ return;
315963
+ }
315964
+ if (!this.params.archiver.isEnabled()) {
315965
+ return;
315966
+ }
315967
+ log.info("\uD83E\uDDFE Fill archive poller started");
315968
+ this.#timer = setTimeout(() => void this.#tick(), 0);
315969
+ this.#timer.unref?.();
315970
+ }
315971
+ stop() {
315972
+ this.#stopped = true;
315973
+ if (this.#timer) {
315974
+ clearTimeout(this.#timer);
315975
+ this.#timer = null;
315976
+ }
315977
+ }
315978
+ async#tick() {
315979
+ if (this.#stopped || this.#running) {
315980
+ return;
315981
+ }
315982
+ this.#running = true;
315983
+ try {
315984
+ await this.pollTrackedOnce();
315985
+ } catch (error) {
315986
+ log.error("Fill archive poller tick failed", error);
315987
+ } finally {
315988
+ this.#running = false;
315989
+ if (!this.#stopped) {
315990
+ this.#timer = setTimeout(() => void this.#tick(), this.#config.pollIntervalMs);
315991
+ this.#timer.unref?.();
315992
+ }
315993
+ }
315994
+ }
315995
+ async pollTrackedOnce() {
315996
+ for (const entry of this.params.tracker.list()) {
315997
+ if (this.#stopped) {
315998
+ break;
315999
+ }
316000
+ await this.#pollOne(entry.exchangeId, entry.accountLabel, entry.symbol);
316001
+ }
316002
+ }
316003
+ async#pollOne(exchangeId, accountLabel, symbol) {
316004
+ const account = resolveBrokerAccount(this.params.brokers[exchangeId], accountLabel);
316005
+ if (!account) {
316006
+ return;
316007
+ }
316008
+ const exchange = account.exchange;
316009
+ if (typeof exchange.fetchMyTrades !== "function" || exchange.has?.fetchMyTrades === false) {
316010
+ return;
316011
+ }
316012
+ const key = `${exchangeId}|${accountLabel}|${symbol}`;
316013
+ const since = this.#cursors.get(key) ?? Date.now() - this.#config.lookbackMs;
316014
+ let trades;
316015
+ try {
316016
+ trades = await exchange.fetchMyTrades(symbol, since, this.#config.tradesLimit);
316017
+ } catch (error) {
316018
+ this.params.metrics?.recordCounter("cex_fill_poller_errors_total", 1, {
316019
+ exchange: exchangeId
316020
+ });
316021
+ log.warn("Fill archive poll failed", {
316022
+ exchange: exchangeId,
316023
+ account: accountLabel,
316024
+ symbol,
316025
+ error
316026
+ });
316027
+ return;
316028
+ }
316029
+ if (!Array.isArray(trades) || trades.length === 0) {
316030
+ return;
316031
+ }
316032
+ trades.forEach((trade, fillIndex) => {
316033
+ const tags = buildCommonArchiveTags({
316034
+ deploymentId: this.params.archiver.getDeploymentId(),
316035
+ accountSelector: accountLabel,
316036
+ exchange: exchangeId,
316037
+ symbol
316038
+ });
316039
+ this.params.archiver.enqueue(buildFillEventArchiveRow({
316040
+ tags,
316041
+ fill: { ...normalizeCcxtTradeForArchive(trade), fillIndex }
316042
+ }));
316043
+ });
316044
+ this.params.metrics?.recordCounter("cex_fill_poller_trades_archived_total", trades.length, { exchange: exchangeId });
316045
+ this.#cursors.set(key, nextFillCursor(trades, since));
316046
+ }
316047
+ }
316048
+
316049
+ // src/helpers/order-activity-tracker.ts
316050
+ var DEFAULT_MAX_AGE_MS = 6 * 60 * 60 * 1000;
316051
+
316052
+ class OrderActivityTracker {
316053
+ #entries = new Map;
316054
+ #maxAgeMs;
316055
+ constructor(options) {
316056
+ this.#maxAgeMs = options?.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
316057
+ }
316058
+ record(exchangeId, accountLabel, symbol, now3 = Date.now()) {
316059
+ const exchange = exchangeId.trim().toLowerCase();
316060
+ const trimmedSymbol = symbol.trim();
316061
+ if (!exchange || !accountLabel.trim() || !trimmedSymbol) {
316062
+ return;
316063
+ }
316064
+ const key = `${exchange}|${accountLabel}|${trimmedSymbol}`;
316065
+ this.#entries.set(key, {
316066
+ exchangeId: exchange,
316067
+ accountLabel,
316068
+ symbol: trimmedSymbol,
316069
+ lastActivityAt: now3
316070
+ });
316071
+ }
316072
+ list(now3 = Date.now()) {
316073
+ const active = [];
316074
+ for (const [key, entry] of this.#entries) {
316075
+ if (now3 - entry.lastActivityAt > this.#maxAgeMs) {
316076
+ this.#entries.delete(key);
316077
+ continue;
316078
+ }
316079
+ active.push(entry);
316080
+ }
316081
+ return active;
316082
+ }
316083
+ }
316084
+
315768
316085
  // src/helpers/otel.ts
315769
316086
  var import_api2 = __toESM(require_src5(), 1);
315770
316087
  var import_api_logs3 = __toESM(require_src7(), 1);
@@ -329941,7 +330258,13 @@ function rejectWithGrpcError(ctx, error48, options) {
329941
330258
 
329942
330259
  // src/handlers/execute-action/deposit.ts
329943
330260
  async function handleDeposit(ctx) {
329944
- const { normalizedCex, symbol: symbol2, selectedBrokerAccount, broker } = ctx;
330261
+ const {
330262
+ normalizedCex,
330263
+ symbol: symbol2,
330264
+ selectedBrokerAccount,
330265
+ broker,
330266
+ brokerArchiver
330267
+ } = ctx;
329945
330268
  if (!symbol2) {
329946
330269
  return ctx.wrappedCallback({
329947
330270
  code: grpc3.status.INVALID_ARGUMENT,
@@ -330004,6 +330327,32 @@ async function handleDeposit(ctx) {
330004
330327
  }, null);
330005
330328
  }
330006
330329
  const status4 = normalizeDepositStatus(depositField(deposit, ["status", "state"]));
330330
+ const depositTxid = String(depositField(deposit, ["txid", "txId", "tx_hash", "txHash"]) ?? value.transactionHash);
330331
+ const creditedAt = depositField(deposit, [
330332
+ "creditedAt",
330333
+ "credited_at",
330334
+ "updated",
330335
+ "updatedAt",
330336
+ "timestamp",
330337
+ "datetime"
330338
+ ]);
330339
+ archiveTransferEventInBackground(brokerArchiver, {
330340
+ exchange: normalizedCex,
330341
+ accountSelector: selectedBrokerAccount?.label,
330342
+ assetSymbol: symbol2,
330343
+ transfer: {
330344
+ eventKind: "deposit",
330345
+ lifecycleAction: "observe_deposit",
330346
+ status: status4,
330347
+ amount: observedAmount !== undefined ? String(observedAmount) : undefined,
330348
+ address: String(observedAddress ?? value.recipientAddress),
330349
+ network: depositNetwork?.exchangeNetworkId,
330350
+ externalId: depositTxid,
330351
+ txid: depositTxid,
330352
+ exchangeTimestamp: typeof creditedAt === "string" ? creditedAt : undefined,
330353
+ payload: deposit
330354
+ }
330355
+ });
330007
330356
  log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
330008
330357
  return ctx.wrappedCallback(null, {
330009
330358
  proof: ctx.verity.proof,
@@ -330417,7 +330766,8 @@ async function handleInternalTransfer(ctx) {
330417
330766
  symbol: symbol2,
330418
330767
  verity,
330419
330768
  useVerity,
330420
- verityProverUrl
330769
+ verityProverUrl,
330770
+ brokerArchiver
330421
330771
  } = ctx;
330422
330772
  if (!symbol2) {
330423
330773
  return ctx.wrappedCallback({
@@ -330465,6 +330815,21 @@ async function handleInternalTransfer(ctx) {
330465
330815
  }), verityHttpClientOverridePredicate);
330466
330816
  }
330467
330817
  const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
330818
+ const normalized = normalizeCcxtTransactionForArchive(result);
330819
+ archiveTransferEventInBackground(brokerArchiver, {
330820
+ exchange: normalizedCex,
330821
+ accountSelector: fromSelector,
330822
+ assetSymbol: symbol2,
330823
+ transfer: {
330824
+ eventKind: "internal_transfer",
330825
+ lifecycleAction: "submit_internal_transfer",
330826
+ status: normalized.status ?? "ok",
330827
+ amount: normalized.amount ?? String(transferPayload.amount),
330828
+ network: "internal",
330829
+ externalId: normalized.externalId,
330830
+ payload: { from: fromSelector, to: toSelector, result }
330831
+ }
330832
+ });
330468
330833
  ctx.wrappedCallback(null, {
330469
330834
  proof: verity.proof,
330470
330835
  result: JSON.stringify(result)
@@ -330512,7 +330877,8 @@ async function handleCreateOrder(ctx) {
330512
330877
  useVerity,
330513
330878
  verityProverUrl,
330514
330879
  otelMetrics,
330515
- brokerArchiver
330880
+ brokerArchiver,
330881
+ orderActivityTracker
330516
330882
  } = ctx;
330517
330883
  const verityProof = verity.proof;
330518
330884
  const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
@@ -330538,6 +330904,9 @@ async function handleCreateOrder(ctx) {
330538
330904
  side: resolution.side,
330539
330905
  requestedQuantity: resolution.amountBase ?? orderValue.amount
330540
330906
  };
330907
+ if (selectedBrokerAccount?.label) {
330908
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
330909
+ }
330541
330910
  const telemetryIds = extractOrderTelemetryIds(orderValue.params);
330542
330911
  const submissionTimestamp = new Date().toISOString();
330543
330912
  const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
@@ -330602,7 +330971,8 @@ async function handleGetOrderDetails(ctx) {
330602
330971
  useVerity,
330603
330972
  verityProverUrl,
330604
330973
  otelMetrics,
330605
- brokerArchiver
330974
+ brokerArchiver,
330975
+ orderActivityTracker
330606
330976
  } = ctx;
330607
330977
  const verityProof = verity.proof;
330608
330978
  const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
@@ -330616,6 +330986,9 @@ async function handleGetOrderDetails(ctx) {
330616
330986
  }, null);
330617
330987
  }
330618
330988
  const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
330989
+ if (selectedBrokerAccount?.label && symbol2) {
330990
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
330991
+ }
330619
330992
  const getOrderContext = {
330620
330993
  action: "GetOrderDetails",
330621
330994
  cex: cex3,
@@ -330671,7 +331044,8 @@ async function handleCancelOrder(ctx) {
330671
331044
  useVerity,
330672
331045
  verityProverUrl,
330673
331046
  otelMetrics,
330674
- brokerArchiver
331047
+ brokerArchiver,
331048
+ orderActivityTracker
330675
331049
  } = ctx;
330676
331050
  const verityProof = verity.proof;
330677
331051
  const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
@@ -330692,6 +331066,9 @@ async function handleCancelOrder(ctx) {
330692
331066
  ...extractOrderTelemetryIds(cancelOrderValue.params)
330693
331067
  };
330694
331068
  const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
331069
+ if (selectedBrokerAccount?.label && symbol2) {
331070
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
331071
+ }
330695
331072
  emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
330696
331073
  archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
330697
331074
  ctx.wrappedCallback(null, {
@@ -331322,7 +331699,8 @@ async function handleWithdraw(ctx) {
331322
331699
  applyVerityToBroker,
331323
331700
  useVerity,
331324
331701
  verityProverUrl,
331325
- otelMetrics
331702
+ otelMetrics,
331703
+ brokerArchiver
331326
331704
  } = ctx;
331327
331705
  const verityProof = verity.proof;
331328
331706
  if (!symbol2) {
@@ -331371,6 +331749,26 @@ async function handleWithdraw(ctx) {
331371
331749
  network: withdrawNetwork.exchangeNetworkId
331372
331750
  });
331373
331751
  log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
331752
+ const normalized = normalizeCcxtTransactionForArchive(transaction);
331753
+ archiveTransferEventInBackground(brokerArchiver, {
331754
+ exchange: cex3,
331755
+ accountSelector: selectedBrokerAccount?.label,
331756
+ assetSymbol: normalized.assetSymbol ?? symbol2,
331757
+ transfer: {
331758
+ eventKind: "withdrawal",
331759
+ lifecycleAction: "submit_withdrawal",
331760
+ status: normalized.status,
331761
+ amount: normalized.amount ?? String(transferValue.amount),
331762
+ address: normalized.address ?? transferValue.recipientAddress,
331763
+ network: normalized.network ?? withdrawNetwork.exchangeNetworkId,
331764
+ externalId: normalized.externalId,
331765
+ txid: normalized.txid,
331766
+ feeAmount: normalized.feeAmount,
331767
+ feeCurrency: normalized.feeCurrency,
331768
+ exchangeTimestamp: normalized.exchangeTimestamp,
331769
+ payload: transaction
331770
+ }
331771
+ });
331374
331772
  ctx.wrappedCallback(null, {
331375
331773
  proof: ctx.verity.proof,
331376
331774
  result: JSON.stringify({
@@ -331382,6 +331780,21 @@ async function handleWithdraw(ctx) {
331382
331780
  });
331383
331781
  } catch (error48) {
331384
331782
  safeLogError("Withdraw failed", error48);
331783
+ archiveTransferEventInBackground(brokerArchiver, {
331784
+ exchange: cex3,
331785
+ accountSelector: selectedBrokerAccount?.label,
331786
+ assetSymbol: symbol2,
331787
+ transfer: {
331788
+ eventKind: "withdrawal",
331789
+ lifecycleAction: "submit_withdrawal",
331790
+ status: "failed",
331791
+ amount: String(transferValue.amount),
331792
+ address: transferValue.recipientAddress,
331793
+ network: withdrawNetwork.exchangeNetworkId,
331794
+ errorSummary: getErrorMessage(error48),
331795
+ payload: { recipientAddress: transferValue.recipientAddress }
331796
+ }
331797
+ });
331385
331798
  const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
331386
331799
  ctx.wrappedCallback({
331387
331800
  code,
@@ -331429,7 +331842,8 @@ function createExecuteActionHandler(deps) {
331429
331842
  useVerity,
331430
331843
  verityProverUrl,
331431
331844
  otelMetrics,
331432
- brokerArchiver
331845
+ brokerArchiver,
331846
+ orderActivityTracker
331433
331847
  } = deps;
331434
331848
  return async (call, callback) => {
331435
331849
  const startTime = Date.now();
@@ -331508,7 +331922,8 @@ function createExecuteActionHandler(deps) {
331508
331922
  useVerity,
331509
331923
  verityProverUrl,
331510
331924
  otelMetrics,
331511
- brokerArchiver
331925
+ brokerArchiver,
331926
+ orderActivityTracker
331512
331927
  };
331513
331928
  if (action === Action.Call) {
331514
331929
  const handled = await handleOrderBookCall(preludeCtx);
@@ -333097,7 +333512,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
333097
333512
  // src/server.ts
333098
333513
  var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
333099
333514
  var cexNode = grpcObj.cex_broker;
333100
- function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
333515
+ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
333101
333516
  const server = new grpc14.Server;
333102
333517
  server.addService(cexNode.cex_service.service, {
333103
333518
  ExecuteAction: createExecuteActionHandler({
@@ -333107,7 +333522,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
333107
333522
  useVerity,
333108
333523
  verityProverUrl,
333109
333524
  otelMetrics,
333110
- brokerArchiver
333525
+ brokerArchiver,
333526
+ orderActivityTracker
333111
333527
  }),
333112
333528
  Subscribe: createSubscribeHandler({
333113
333529
  brokers,
@@ -333252,6 +333668,8 @@ class CEXBroker {
333252
333668
  otelLogs;
333253
333669
  brokerArchiver;
333254
333670
  depositReconciler;
333671
+ orderActivityTracker = new OrderActivityTracker;
333672
+ fillArchivePoller;
333255
333673
  loadEnvConfig() {
333256
333674
  log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
333257
333675
  const configMap = {};
@@ -333392,6 +333810,10 @@ class CEXBroker {
333392
333810
  this.depositReconciler.stop();
333393
333811
  this.depositReconciler = undefined;
333394
333812
  }
333813
+ if (this.fillArchivePoller) {
333814
+ this.fillArchivePoller.stop();
333815
+ this.fillArchivePoller = undefined;
333816
+ }
333395
333817
  if (this.server) {
333396
333818
  await this.server.forceShutdown();
333397
333819
  }
@@ -333413,11 +333835,15 @@ class CEXBroker {
333413
333835
  this.depositReconciler.stop();
333414
333836
  this.depositReconciler = undefined;
333415
333837
  }
333838
+ if (this.fillArchivePoller) {
333839
+ this.fillArchivePoller.stop();
333840
+ this.fillArchivePoller = undefined;
333841
+ }
333416
333842
  log.info(`Running CEXBroker at ${new Date().toISOString()}`);
333417
333843
  if (this.otelMetrics?.isOtelEnabled()) {
333418
333844
  await this.otelMetrics.initialize();
333419
333845
  }
333420
- this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver);
333846
+ this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
333421
333847
  this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
333422
333848
  if (err2) {
333423
333849
  log.error(err2);
@@ -333432,6 +333858,15 @@ class CEXBroker {
333432
333858
  metrics: this.otelMetrics
333433
333859
  });
333434
333860
  this.depositReconciler.start();
333861
+ if (this.brokerArchiver?.isEnabled()) {
333862
+ this.fillArchivePoller = new FillArchivePoller({
333863
+ brokers: this.brokers,
333864
+ archiver: this.brokerArchiver,
333865
+ tracker: this.orderActivityTracker,
333866
+ metrics: this.otelMetrics
333867
+ });
333868
+ this.fillArchivePoller.start();
333869
+ }
333435
333870
  return this;
333436
333871
  }
333437
333872
  }