@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.
@@ -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;
@@ -315678,11 +315812,27 @@ class BrokerExecutionArchiver {
315678
315812
  }
315679
315813
  }
315680
315814
  this.stats.flushed += batch.length;
315815
+ this.recordFlushHealth(batch);
315681
315816
  return true;
315682
315817
  }
315683
- 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) {
315684
315829
  try {
315685
- await this.otelMetrics?.recordCounter(metricName, 1, labels);
315830
+ await this.otelMetrics?.recordCounter(metricName, value, labels);
315831
+ } catch {}
315832
+ }
315833
+ async recordArchiveGauge(metricName, value) {
315834
+ try {
315835
+ await this.otelMetrics?.recordGauge(metricName, value, {});
315686
315836
  } catch {}
315687
315837
  }
315688
315838
  emitOtelLog(entry) {
@@ -315779,6 +315929,159 @@ function parsePositiveInt(value, fallback) {
315779
315929
  const parsed = Number.parseInt(value, 10);
315780
315930
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
315781
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
+
315782
316085
  // src/helpers/otel.ts
315783
316086
  var import_api2 = __toESM(require_src5(), 1);
315784
316087
  var import_api_logs3 = __toESM(require_src7(), 1);
@@ -316100,9 +316403,23 @@ var grpc14 = __toESM(require_src3(), 1);
316100
316403
  var grpc3 = __toESM(require_src3(), 1);
316101
316404
 
316102
316405
  // src/helpers/shared/errors.ts
316406
+ var MAX_ERROR_DETAIL_LENGTH = 512;
316103
316407
  function getErrorMessage(error) {
316104
316408
  return error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
316105
316409
  }
316410
+ function errorClassName(error) {
316411
+ if (!(error instanceof Error)) {
316412
+ return;
316413
+ }
316414
+ const name = error.constructor?.name || error.name;
316415
+ return name && name !== "Error" ? name : undefined;
316416
+ }
316417
+ function sanitizeErrorDetail(error) {
316418
+ const className = errorClassName(error);
316419
+ const message = getErrorMessage(error);
316420
+ const detail = className ? `${className}: ${message}` : message;
316421
+ return detail.replace(/\s+/g, " ").trim().slice(0, MAX_ERROR_DETAIL_LENGTH);
316422
+ }
316106
316423
  function safeLogError(context2, error) {
316107
316424
  try {
316108
316425
  log.error(context2, { error });
@@ -329950,12 +330267,24 @@ function rejectWithGrpcError(ctx, error48, options) {
329950
330267
  } else {
329951
330268
  finalMessage = message;
329952
330269
  }
330270
+ if (options?.appendClassName) {
330271
+ const className = errorClassName(error48);
330272
+ if (className && !finalMessage.includes(className)) {
330273
+ finalMessage = `${finalMessage} (${className})`;
330274
+ }
330275
+ }
329953
330276
  ctx.wrappedCallback({ code, message: finalMessage }, null);
329954
330277
  }
329955
330278
 
329956
330279
  // src/handlers/execute-action/deposit.ts
329957
330280
  async function handleDeposit(ctx) {
329958
- const { normalizedCex, symbol: symbol2, selectedBrokerAccount, broker } = ctx;
330281
+ const {
330282
+ normalizedCex,
330283
+ symbol: symbol2,
330284
+ selectedBrokerAccount,
330285
+ broker,
330286
+ brokerArchiver
330287
+ } = ctx;
329959
330288
  if (!symbol2) {
329960
330289
  return ctx.wrappedCallback({
329961
330290
  code: grpc3.status.INVALID_ARGUMENT,
@@ -330018,6 +330347,32 @@ async function handleDeposit(ctx) {
330018
330347
  }, null);
330019
330348
  }
330020
330349
  const status4 = normalizeDepositStatus(depositField(deposit, ["status", "state"]));
330350
+ const depositTxid = String(depositField(deposit, ["txid", "txId", "tx_hash", "txHash"]) ?? value.transactionHash);
330351
+ const creditedAt = depositField(deposit, [
330352
+ "creditedAt",
330353
+ "credited_at",
330354
+ "updated",
330355
+ "updatedAt",
330356
+ "timestamp",
330357
+ "datetime"
330358
+ ]);
330359
+ archiveTransferEventInBackground(brokerArchiver, {
330360
+ exchange: normalizedCex,
330361
+ accountSelector: selectedBrokerAccount?.label,
330362
+ assetSymbol: symbol2,
330363
+ transfer: {
330364
+ eventKind: "deposit",
330365
+ lifecycleAction: "observe_deposit",
330366
+ status: status4,
330367
+ amount: observedAmount !== undefined ? String(observedAmount) : undefined,
330368
+ address: String(observedAddress ?? value.recipientAddress),
330369
+ network: depositNetwork?.exchangeNetworkId,
330370
+ externalId: depositTxid,
330371
+ txid: depositTxid,
330372
+ exchangeTimestamp: typeof creditedAt === "string" ? creditedAt : undefined,
330373
+ payload: deposit
330374
+ }
330375
+ });
330021
330376
  log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
330022
330377
  return ctx.wrappedCallback(null, {
330023
330378
  proof: ctx.verity.proof,
@@ -330090,7 +330445,8 @@ async function handleDeposit(ctx) {
330090
330445
  }
330091
330446
  rejectWithGrpcError(ctx, error48, {
330092
330447
  message,
330093
- prefix: "deposit_observation_unavailable: "
330448
+ prefix: "deposit_observation_unavailable: ",
330449
+ appendClassName: true
330094
330450
  });
330095
330451
  }
330096
330452
  }
@@ -330409,10 +330765,9 @@ async function handleOrderBookCall(ctx) {
330409
330765
  });
330410
330766
  } catch (error48) {
330411
330767
  safeLogError("Order-book Call failed", error48);
330412
- const message = getErrorMessage(error48);
330413
330768
  ctx.wrappedCallback({
330414
330769
  code: mapCcxtErrorToGrpcStatus(error48) ?? grpc4.status.INTERNAL,
330415
- message: `Order-book Call failed: ${message}`
330770
+ message: `Order-book Call failed: ${sanitizeErrorDetail(error48)}`
330416
330771
  }, null);
330417
330772
  }
330418
330773
  return true;
@@ -330431,7 +330786,8 @@ async function handleInternalTransfer(ctx) {
330431
330786
  symbol: symbol2,
330432
330787
  verity,
330433
330788
  useVerity,
330434
- verityProverUrl
330789
+ verityProverUrl,
330790
+ brokerArchiver
330435
330791
  } = ctx;
330436
330792
  if (!symbol2) {
330437
330793
  return ctx.wrappedCallback({
@@ -330479,6 +330835,21 @@ async function handleInternalTransfer(ctx) {
330479
330835
  }), verityHttpClientOverridePredicate);
330480
330836
  }
330481
330837
  const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
330838
+ const normalized = normalizeCcxtTransactionForArchive(result);
330839
+ archiveTransferEventInBackground(brokerArchiver, {
330840
+ exchange: normalizedCex,
330841
+ accountSelector: fromSelector,
330842
+ assetSymbol: symbol2,
330843
+ transfer: {
330844
+ eventKind: "internal_transfer",
330845
+ lifecycleAction: "submit_internal_transfer",
330846
+ status: normalized.status ?? "ok",
330847
+ amount: normalized.amount ?? String(transferPayload.amount),
330848
+ network: "internal",
330849
+ externalId: normalized.externalId,
330850
+ payload: { from: fromSelector, to: toSelector, result }
330851
+ }
330852
+ });
330482
330853
  ctx.wrappedCallback(null, {
330483
330854
  proof: verity.proof,
330484
330855
  result: JSON.stringify(result)
@@ -330502,7 +330873,7 @@ async function handleInternalTransfer(ctx) {
330502
330873
  }
330503
330874
  ctx.wrappedCallback({
330504
330875
  code,
330505
- message: `InternalTransfer failed: ${msg}`
330876
+ message: `InternalTransfer failed: ${sanitizeErrorDetail(error48)}`
330506
330877
  }, null);
330507
330878
  }
330508
330879
  }
@@ -330526,7 +330897,8 @@ async function handleCreateOrder(ctx) {
330526
330897
  useVerity,
330527
330898
  verityProverUrl,
330528
330899
  otelMetrics,
330529
- brokerArchiver
330900
+ brokerArchiver,
330901
+ orderActivityTracker
330530
330902
  } = ctx;
330531
330903
  const verityProof = verity.proof;
330532
330904
  const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
@@ -330552,6 +330924,9 @@ async function handleCreateOrder(ctx) {
330552
330924
  side: resolution.side,
330553
330925
  requestedQuantity: resolution.amountBase ?? orderValue.amount
330554
330926
  };
330927
+ if (selectedBrokerAccount?.label) {
330928
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
330929
+ }
330555
330930
  const telemetryIds = extractOrderTelemetryIds(orderValue.params);
330556
330931
  const submissionTimestamp = new Date().toISOString();
330557
330932
  const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
@@ -330595,7 +330970,7 @@ async function handleCreateOrder(ctx) {
330595
330970
  archiveOrderExecutionInBackground(brokerArchiver, failedCreateContext, undefined, error48);
330596
330971
  ctx.wrappedCallback({
330597
330972
  code: grpc6.status.INTERNAL,
330598
- message: "Order Creation failed"
330973
+ message: `Order Creation failed: ${sanitizeErrorDetail(error48)}`
330599
330974
  }, null);
330600
330975
  }
330601
330976
  }
@@ -330616,7 +330991,8 @@ async function handleGetOrderDetails(ctx) {
330616
330991
  useVerity,
330617
330992
  verityProverUrl,
330618
330993
  otelMetrics,
330619
- brokerArchiver
330994
+ brokerArchiver,
330995
+ orderActivityTracker
330620
330996
  } = ctx;
330621
330997
  const verityProof = verity.proof;
330622
330998
  const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
@@ -330630,6 +331006,9 @@ async function handleGetOrderDetails(ctx) {
330630
331006
  }, null);
330631
331007
  }
330632
331008
  const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
331009
+ if (selectedBrokerAccount?.label && symbol2) {
331010
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
331011
+ }
330633
331012
  const getOrderContext = {
330634
331013
  action: "GetOrderDetails",
330635
331014
  cex: cex3,
@@ -330664,7 +331043,7 @@ async function handleGetOrderDetails(ctx) {
330664
331043
  archiveOrderExecutionInBackground(brokerArchiver, failedGetOrderContext, undefined, error48);
330665
331044
  ctx.wrappedCallback({
330666
331045
  code: grpc6.status.INTERNAL,
330667
- message: `Failed to fetch order details from ${cex3}`
331046
+ message: `Failed to fetch order details from ${cex3}: ${sanitizeErrorDetail(error48)}`
330668
331047
  }, null);
330669
331048
  }
330670
331049
  }
@@ -330685,7 +331064,8 @@ async function handleCancelOrder(ctx) {
330685
331064
  useVerity,
330686
331065
  verityProverUrl,
330687
331066
  otelMetrics,
330688
- brokerArchiver
331067
+ brokerArchiver,
331068
+ orderActivityTracker
330689
331069
  } = ctx;
330690
331070
  const verityProof = verity.proof;
330691
331071
  const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
@@ -330706,6 +331086,9 @@ async function handleCancelOrder(ctx) {
330706
331086
  ...extractOrderTelemetryIds(cancelOrderValue.params)
330707
331087
  };
330708
331088
  const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
331089
+ if (selectedBrokerAccount?.label && symbol2) {
331090
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
331091
+ }
330709
331092
  emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
330710
331093
  archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
330711
331094
  ctx.wrappedCallback(null, {
@@ -330724,7 +331107,7 @@ async function handleCancelOrder(ctx) {
330724
331107
  archiveOrderExecutionInBackground(brokerArchiver, failedCancelContext, undefined, error48);
330725
331108
  ctx.wrappedCallback({
330726
331109
  code: grpc6.status.INTERNAL,
330727
- message: `Failed to cancel order from ${cex3}`
331110
+ message: `Failed to cancel order from ${cex3}: ${sanitizeErrorDetail(error48)}`
330728
331111
  }, null);
330729
331112
  }
330730
331113
  }
@@ -331221,7 +331604,7 @@ async function handleGetPerpConfigState(ctx) {
331221
331604
  safeLogError(`GetPerpConfigState failed for ${cex3}`, error48);
331222
331605
  ctx.wrappedCallback({
331223
331606
  code: grpc8.status.INTERNAL,
331224
- message: getErrorMessage(error48)
331607
+ message: `GetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
331225
331608
  }, null);
331226
331609
  }
331227
331610
  }
@@ -331262,7 +331645,7 @@ async function handleSetPerpConfigState(ctx) {
331262
331645
  safeLogError(`SetPerpConfigState failed for ${cex3}`, error48);
331263
331646
  ctx.wrappedCallback({
331264
331647
  code: grpc8.status.INTERNAL,
331265
- message: getErrorMessage(error48)
331648
+ message: `SetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
331266
331649
  }, null);
331267
331650
  }
331268
331651
  }
@@ -331313,7 +331696,8 @@ async function handleTreasuryCall(ctx) {
331313
331696
  safeLogError("Call failed", error48);
331314
331697
  rejectWithGrpcError(ctx, error48, {
331315
331698
  message: getErrorMessage(error48),
331316
- preferStableMessageOnly: true
331699
+ preferStableMessageOnly: true,
331700
+ appendClassName: true
331317
331701
  });
331318
331702
  }
331319
331703
  }
@@ -331336,7 +331720,8 @@ async function handleWithdraw(ctx) {
331336
331720
  applyVerityToBroker,
331337
331721
  useVerity,
331338
331722
  verityProverUrl,
331339
- otelMetrics
331723
+ otelMetrics,
331724
+ brokerArchiver
331340
331725
  } = ctx;
331341
331726
  const verityProof = verity.proof;
331342
331727
  if (!symbol2) {
@@ -331385,6 +331770,26 @@ async function handleWithdraw(ctx) {
331385
331770
  network: withdrawNetwork.exchangeNetworkId
331386
331771
  });
331387
331772
  log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
331773
+ const normalized = normalizeCcxtTransactionForArchive(transaction);
331774
+ archiveTransferEventInBackground(brokerArchiver, {
331775
+ exchange: cex3,
331776
+ accountSelector: selectedBrokerAccount?.label,
331777
+ assetSymbol: normalized.assetSymbol ?? symbol2,
331778
+ transfer: {
331779
+ eventKind: "withdrawal",
331780
+ lifecycleAction: "submit_withdrawal",
331781
+ status: normalized.status,
331782
+ amount: normalized.amount ?? String(transferValue.amount),
331783
+ address: normalized.address ?? transferValue.recipientAddress,
331784
+ network: normalized.network ?? withdrawNetwork.exchangeNetworkId,
331785
+ externalId: normalized.externalId,
331786
+ txid: normalized.txid,
331787
+ feeAmount: normalized.feeAmount,
331788
+ feeCurrency: normalized.feeCurrency,
331789
+ exchangeTimestamp: normalized.exchangeTimestamp,
331790
+ payload: transaction
331791
+ }
331792
+ });
331388
331793
  ctx.wrappedCallback(null, {
331389
331794
  proof: ctx.verity.proof,
331390
331795
  result: JSON.stringify({
@@ -331396,10 +331801,25 @@ async function handleWithdraw(ctx) {
331396
331801
  });
331397
331802
  } catch (error48) {
331398
331803
  safeLogError("Withdraw failed", error48);
331804
+ archiveTransferEventInBackground(brokerArchiver, {
331805
+ exchange: cex3,
331806
+ accountSelector: selectedBrokerAccount?.label,
331807
+ assetSymbol: symbol2,
331808
+ transfer: {
331809
+ eventKind: "withdrawal",
331810
+ lifecycleAction: "submit_withdrawal",
331811
+ status: "failed",
331812
+ amount: String(transferValue.amount),
331813
+ address: transferValue.recipientAddress,
331814
+ network: withdrawNetwork.exchangeNetworkId,
331815
+ errorSummary: getErrorMessage(error48),
331816
+ payload: { recipientAddress: transferValue.recipientAddress }
331817
+ }
331818
+ });
331399
331819
  const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
331400
331820
  ctx.wrappedCallback({
331401
331821
  code,
331402
- message: `Withdraw failed: ${getErrorMessage(error48)}`
331822
+ message: `Withdraw failed: ${sanitizeErrorDetail(error48)}`
331403
331823
  }, null);
331404
331824
  }
331405
331825
  }
@@ -331443,7 +331863,8 @@ function createExecuteActionHandler(deps) {
331443
331863
  useVerity,
331444
331864
  verityProverUrl,
331445
331865
  otelMetrics,
331446
- brokerArchiver
331866
+ brokerArchiver,
331867
+ orderActivityTracker
331447
331868
  } = deps;
331448
331869
  return async (call, callback) => {
331449
331870
  const startTime = Date.now();
@@ -331522,7 +331943,8 @@ function createExecuteActionHandler(deps) {
331522
331943
  useVerity,
331523
331944
  verityProverUrl,
331524
331945
  otelMetrics,
331525
- brokerArchiver
331946
+ brokerArchiver,
331947
+ orderActivityTracker
331526
331948
  };
331527
331949
  if (action === Action.Call) {
331528
331950
  const handled = await handleOrderBookCall(preludeCtx);
@@ -333111,7 +333533,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
333111
333533
  // src/server.ts
333112
333534
  var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
333113
333535
  var cexNode = grpcObj.cex_broker;
333114
- function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
333536
+ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
333115
333537
  const server = new grpc14.Server;
333116
333538
  server.addService(cexNode.cex_service.service, {
333117
333539
  ExecuteAction: createExecuteActionHandler({
@@ -333121,7 +333543,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
333121
333543
  useVerity,
333122
333544
  verityProverUrl,
333123
333545
  otelMetrics,
333124
- brokerArchiver
333546
+ brokerArchiver,
333547
+ orderActivityTracker
333125
333548
  }),
333126
333549
  Subscribe: createSubscribeHandler({
333127
333550
  brokers,
@@ -333266,6 +333689,8 @@ class CEXBroker {
333266
333689
  otelLogs;
333267
333690
  brokerArchiver;
333268
333691
  depositReconciler;
333692
+ orderActivityTracker = new OrderActivityTracker;
333693
+ fillArchivePoller;
333269
333694
  loadEnvConfig() {
333270
333695
  log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
333271
333696
  const configMap = {};
@@ -333406,6 +333831,10 @@ class CEXBroker {
333406
333831
  this.depositReconciler.stop();
333407
333832
  this.depositReconciler = undefined;
333408
333833
  }
333834
+ if (this.fillArchivePoller) {
333835
+ this.fillArchivePoller.stop();
333836
+ this.fillArchivePoller = undefined;
333837
+ }
333409
333838
  if (this.server) {
333410
333839
  await this.server.forceShutdown();
333411
333840
  }
@@ -333427,11 +333856,15 @@ class CEXBroker {
333427
333856
  this.depositReconciler.stop();
333428
333857
  this.depositReconciler = undefined;
333429
333858
  }
333859
+ if (this.fillArchivePoller) {
333860
+ this.fillArchivePoller.stop();
333861
+ this.fillArchivePoller = undefined;
333862
+ }
333430
333863
  log.info(`Running CEXBroker at ${new Date().toISOString()}`);
333431
333864
  if (this.otelMetrics?.isOtelEnabled()) {
333432
333865
  await this.otelMetrics.initialize();
333433
333866
  }
333434
- this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver);
333867
+ this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
333435
333868
  this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
333436
333869
  if (err2) {
333437
333870
  log.error(err2);
@@ -333446,6 +333879,15 @@ class CEXBroker {
333446
333879
  metrics: this.otelMetrics
333447
333880
  });
333448
333881
  this.depositReconciler.start();
333882
+ if (this.brokerArchiver?.isEnabled()) {
333883
+ this.fillArchivePoller = new FillArchivePoller({
333884
+ brokers: this.brokers,
333885
+ archiver: this.brokerArchiver,
333886
+ tracker: this.orderActivityTracker,
333887
+ metrics: this.otelMetrics
333888
+ });
333889
+ this.fillArchivePoller.start();
333890
+ }
333449
333891
  return this;
333450
333892
  }
333451
333893
  }