@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.
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;
@@ -275058,11 +275192,27 @@ class BrokerExecutionArchiver {
275058
275192
  }
275059
275193
  }
275060
275194
  this.stats.flushed += batch.length;
275195
+ this.recordFlushHealth(batch);
275061
275196
  return true;
275062
275197
  }
275063
- 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) {
275064
275209
  try {
275065
- 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, {});
275066
275216
  } catch {}
275067
275217
  }
275068
275218
  emitOtelLog(entry) {
@@ -275159,6 +275309,159 @@ function parsePositiveInt(value, fallback) {
275159
275309
  const parsed = Number.parseInt(value, 10);
275160
275310
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
275161
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
+
275162
275465
  // src/helpers/otel.ts
275163
275466
  var import_api14 = __toESM(require_src2(), 1);
275164
275467
 
@@ -279052,9 +279355,23 @@ import * as grpc14 from "@grpc/grpc-js";
279052
279355
  import * as grpc3 from "@grpc/grpc-js";
279053
279356
 
279054
279357
  // src/helpers/shared/errors.ts
279358
+ var MAX_ERROR_DETAIL_LENGTH = 512;
279055
279359
  function getErrorMessage(error) {
279056
279360
  return error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
279057
279361
  }
279362
+ function errorClassName(error) {
279363
+ if (!(error instanceof Error)) {
279364
+ return;
279365
+ }
279366
+ const name = error.constructor?.name || error.name;
279367
+ return name && name !== "Error" ? name : undefined;
279368
+ }
279369
+ function sanitizeErrorDetail(error) {
279370
+ const className = errorClassName(error);
279371
+ const message = getErrorMessage(error);
279372
+ const detail = className ? `${className}: ${message}` : message;
279373
+ return detail.replace(/\s+/g, " ").trim().slice(0, MAX_ERROR_DETAIL_LENGTH);
279374
+ }
279058
279375
  function safeLogError(context3, error) {
279059
279376
  try {
279060
279377
  log.error(context3, { error });
@@ -292902,12 +293219,24 @@ function rejectWithGrpcError(ctx, error48, options) {
292902
293219
  } else {
292903
293220
  finalMessage = message;
292904
293221
  }
293222
+ if (options?.appendClassName) {
293223
+ const className = errorClassName(error48);
293224
+ if (className && !finalMessage.includes(className)) {
293225
+ finalMessage = `${finalMessage} (${className})`;
293226
+ }
293227
+ }
292905
293228
  ctx.wrappedCallback({ code, message: finalMessage }, null);
292906
293229
  }
292907
293230
 
292908
293231
  // src/handlers/execute-action/deposit.ts
292909
293232
  async function handleDeposit(ctx) {
292910
- const { normalizedCex, symbol: symbol2, selectedBrokerAccount, broker } = ctx;
293233
+ const {
293234
+ normalizedCex,
293235
+ symbol: symbol2,
293236
+ selectedBrokerAccount,
293237
+ broker,
293238
+ brokerArchiver
293239
+ } = ctx;
292911
293240
  if (!symbol2) {
292912
293241
  return ctx.wrappedCallback({
292913
293242
  code: grpc3.status.INVALID_ARGUMENT,
@@ -292970,6 +293299,32 @@ async function handleDeposit(ctx) {
292970
293299
  }, null);
292971
293300
  }
292972
293301
  const status4 = normalizeDepositStatus(depositField(deposit, ["status", "state"]));
293302
+ const depositTxid = String(depositField(deposit, ["txid", "txId", "tx_hash", "txHash"]) ?? value.transactionHash);
293303
+ const creditedAt = depositField(deposit, [
293304
+ "creditedAt",
293305
+ "credited_at",
293306
+ "updated",
293307
+ "updatedAt",
293308
+ "timestamp",
293309
+ "datetime"
293310
+ ]);
293311
+ archiveTransferEventInBackground(brokerArchiver, {
293312
+ exchange: normalizedCex,
293313
+ accountSelector: selectedBrokerAccount?.label,
293314
+ assetSymbol: symbol2,
293315
+ transfer: {
293316
+ eventKind: "deposit",
293317
+ lifecycleAction: "observe_deposit",
293318
+ status: status4,
293319
+ amount: observedAmount !== undefined ? String(observedAmount) : undefined,
293320
+ address: String(observedAddress ?? value.recipientAddress),
293321
+ network: depositNetwork?.exchangeNetworkId,
293322
+ externalId: depositTxid,
293323
+ txid: depositTxid,
293324
+ exchangeTimestamp: typeof creditedAt === "string" ? creditedAt : undefined,
293325
+ payload: deposit
293326
+ }
293327
+ });
292973
293328
  log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
292974
293329
  return ctx.wrappedCallback(null, {
292975
293330
  proof: ctx.verity.proof,
@@ -293042,7 +293397,8 @@ async function handleDeposit(ctx) {
293042
293397
  }
293043
293398
  rejectWithGrpcError(ctx, error48, {
293044
293399
  message,
293045
- prefix: "deposit_observation_unavailable: "
293400
+ prefix: "deposit_observation_unavailable: ",
293401
+ appendClassName: true
293046
293402
  });
293047
293403
  }
293048
293404
  }
@@ -293361,10 +293717,9 @@ async function handleOrderBookCall(ctx) {
293361
293717
  });
293362
293718
  } catch (error48) {
293363
293719
  safeLogError("Order-book Call failed", error48);
293364
- const message = getErrorMessage(error48);
293365
293720
  ctx.wrappedCallback({
293366
293721
  code: mapCcxtErrorToGrpcStatus(error48) ?? grpc4.status.INTERNAL,
293367
- message: `Order-book Call failed: ${message}`
293722
+ message: `Order-book Call failed: ${sanitizeErrorDetail(error48)}`
293368
293723
  }, null);
293369
293724
  }
293370
293725
  return true;
@@ -293383,7 +293738,8 @@ async function handleInternalTransfer(ctx) {
293383
293738
  symbol: symbol2,
293384
293739
  verity,
293385
293740
  useVerity,
293386
- verityProverUrl
293741
+ verityProverUrl,
293742
+ brokerArchiver
293387
293743
  } = ctx;
293388
293744
  if (!symbol2) {
293389
293745
  return ctx.wrappedCallback({
@@ -293431,6 +293787,21 @@ async function handleInternalTransfer(ctx) {
293431
293787
  }), verityHttpClientOverridePredicate);
293432
293788
  }
293433
293789
  const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
293790
+ const normalized = normalizeCcxtTransactionForArchive(result);
293791
+ archiveTransferEventInBackground(brokerArchiver, {
293792
+ exchange: normalizedCex,
293793
+ accountSelector: fromSelector,
293794
+ assetSymbol: symbol2,
293795
+ transfer: {
293796
+ eventKind: "internal_transfer",
293797
+ lifecycleAction: "submit_internal_transfer",
293798
+ status: normalized.status ?? "ok",
293799
+ amount: normalized.amount ?? String(transferPayload.amount),
293800
+ network: "internal",
293801
+ externalId: normalized.externalId,
293802
+ payload: { from: fromSelector, to: toSelector, result }
293803
+ }
293804
+ });
293434
293805
  ctx.wrappedCallback(null, {
293435
293806
  proof: verity.proof,
293436
293807
  result: JSON.stringify(result)
@@ -293454,7 +293825,7 @@ async function handleInternalTransfer(ctx) {
293454
293825
  }
293455
293826
  ctx.wrappedCallback({
293456
293827
  code,
293457
- message: `InternalTransfer failed: ${msg}`
293828
+ message: `InternalTransfer failed: ${sanitizeErrorDetail(error48)}`
293458
293829
  }, null);
293459
293830
  }
293460
293831
  }
@@ -293478,7 +293849,8 @@ async function handleCreateOrder(ctx) {
293478
293849
  useVerity,
293479
293850
  verityProverUrl,
293480
293851
  otelMetrics,
293481
- brokerArchiver
293852
+ brokerArchiver,
293853
+ orderActivityTracker
293482
293854
  } = ctx;
293483
293855
  const verityProof = verity.proof;
293484
293856
  const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
@@ -293504,6 +293876,9 @@ async function handleCreateOrder(ctx) {
293504
293876
  side: resolution.side,
293505
293877
  requestedQuantity: resolution.amountBase ?? orderValue.amount
293506
293878
  };
293879
+ if (selectedBrokerAccount?.label) {
293880
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
293881
+ }
293507
293882
  const telemetryIds = extractOrderTelemetryIds(orderValue.params);
293508
293883
  const submissionTimestamp = new Date().toISOString();
293509
293884
  const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
@@ -293547,7 +293922,7 @@ async function handleCreateOrder(ctx) {
293547
293922
  archiveOrderExecutionInBackground(brokerArchiver, failedCreateContext, undefined, error48);
293548
293923
  ctx.wrappedCallback({
293549
293924
  code: grpc6.status.INTERNAL,
293550
- message: "Order Creation failed"
293925
+ message: `Order Creation failed: ${sanitizeErrorDetail(error48)}`
293551
293926
  }, null);
293552
293927
  }
293553
293928
  }
@@ -293568,7 +293943,8 @@ async function handleGetOrderDetails(ctx) {
293568
293943
  useVerity,
293569
293944
  verityProverUrl,
293570
293945
  otelMetrics,
293571
- brokerArchiver
293946
+ brokerArchiver,
293947
+ orderActivityTracker
293572
293948
  } = ctx;
293573
293949
  const verityProof = verity.proof;
293574
293950
  const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
@@ -293582,6 +293958,9 @@ async function handleGetOrderDetails(ctx) {
293582
293958
  }, null);
293583
293959
  }
293584
293960
  const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
293961
+ if (selectedBrokerAccount?.label && symbol2) {
293962
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
293963
+ }
293585
293964
  const getOrderContext = {
293586
293965
  action: "GetOrderDetails",
293587
293966
  cex: cex3,
@@ -293616,7 +293995,7 @@ async function handleGetOrderDetails(ctx) {
293616
293995
  archiveOrderExecutionInBackground(brokerArchiver, failedGetOrderContext, undefined, error48);
293617
293996
  ctx.wrappedCallback({
293618
293997
  code: grpc6.status.INTERNAL,
293619
- message: `Failed to fetch order details from ${cex3}`
293998
+ message: `Failed to fetch order details from ${cex3}: ${sanitizeErrorDetail(error48)}`
293620
293999
  }, null);
293621
294000
  }
293622
294001
  }
@@ -293637,7 +294016,8 @@ async function handleCancelOrder(ctx) {
293637
294016
  useVerity,
293638
294017
  verityProverUrl,
293639
294018
  otelMetrics,
293640
- brokerArchiver
294019
+ brokerArchiver,
294020
+ orderActivityTracker
293641
294021
  } = ctx;
293642
294022
  const verityProof = verity.proof;
293643
294023
  const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
@@ -293658,6 +294038,9 @@ async function handleCancelOrder(ctx) {
293658
294038
  ...extractOrderTelemetryIds(cancelOrderValue.params)
293659
294039
  };
293660
294040
  const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
294041
+ if (selectedBrokerAccount?.label && symbol2) {
294042
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
294043
+ }
293661
294044
  emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
293662
294045
  archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
293663
294046
  ctx.wrappedCallback(null, {
@@ -293676,7 +294059,7 @@ async function handleCancelOrder(ctx) {
293676
294059
  archiveOrderExecutionInBackground(brokerArchiver, failedCancelContext, undefined, error48);
293677
294060
  ctx.wrappedCallback({
293678
294061
  code: grpc6.status.INTERNAL,
293679
- message: `Failed to cancel order from ${cex3}`
294062
+ message: `Failed to cancel order from ${cex3}: ${sanitizeErrorDetail(error48)}`
293680
294063
  }, null);
293681
294064
  }
293682
294065
  }
@@ -294173,7 +294556,7 @@ async function handleGetPerpConfigState(ctx) {
294173
294556
  safeLogError(`GetPerpConfigState failed for ${cex3}`, error48);
294174
294557
  ctx.wrappedCallback({
294175
294558
  code: grpc8.status.INTERNAL,
294176
- message: getErrorMessage(error48)
294559
+ message: `GetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
294177
294560
  }, null);
294178
294561
  }
294179
294562
  }
@@ -294214,7 +294597,7 @@ async function handleSetPerpConfigState(ctx) {
294214
294597
  safeLogError(`SetPerpConfigState failed for ${cex3}`, error48);
294215
294598
  ctx.wrappedCallback({
294216
294599
  code: grpc8.status.INTERNAL,
294217
- message: getErrorMessage(error48)
294600
+ message: `SetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
294218
294601
  }, null);
294219
294602
  }
294220
294603
  }
@@ -294265,7 +294648,8 @@ async function handleTreasuryCall(ctx) {
294265
294648
  safeLogError("Call failed", error48);
294266
294649
  rejectWithGrpcError(ctx, error48, {
294267
294650
  message: getErrorMessage(error48),
294268
- preferStableMessageOnly: true
294651
+ preferStableMessageOnly: true,
294652
+ appendClassName: true
294269
294653
  });
294270
294654
  }
294271
294655
  }
@@ -294288,7 +294672,8 @@ async function handleWithdraw(ctx) {
294288
294672
  applyVerityToBroker,
294289
294673
  useVerity,
294290
294674
  verityProverUrl,
294291
- otelMetrics
294675
+ otelMetrics,
294676
+ brokerArchiver
294292
294677
  } = ctx;
294293
294678
  const verityProof = verity.proof;
294294
294679
  if (!symbol2) {
@@ -294337,6 +294722,26 @@ async function handleWithdraw(ctx) {
294337
294722
  network: withdrawNetwork.exchangeNetworkId
294338
294723
  });
294339
294724
  log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
294725
+ const normalized = normalizeCcxtTransactionForArchive(transaction);
294726
+ archiveTransferEventInBackground(brokerArchiver, {
294727
+ exchange: cex3,
294728
+ accountSelector: selectedBrokerAccount?.label,
294729
+ assetSymbol: normalized.assetSymbol ?? symbol2,
294730
+ transfer: {
294731
+ eventKind: "withdrawal",
294732
+ lifecycleAction: "submit_withdrawal",
294733
+ status: normalized.status,
294734
+ amount: normalized.amount ?? String(transferValue.amount),
294735
+ address: normalized.address ?? transferValue.recipientAddress,
294736
+ network: normalized.network ?? withdrawNetwork.exchangeNetworkId,
294737
+ externalId: normalized.externalId,
294738
+ txid: normalized.txid,
294739
+ feeAmount: normalized.feeAmount,
294740
+ feeCurrency: normalized.feeCurrency,
294741
+ exchangeTimestamp: normalized.exchangeTimestamp,
294742
+ payload: transaction
294743
+ }
294744
+ });
294340
294745
  ctx.wrappedCallback(null, {
294341
294746
  proof: ctx.verity.proof,
294342
294747
  result: JSON.stringify({
@@ -294348,10 +294753,25 @@ async function handleWithdraw(ctx) {
294348
294753
  });
294349
294754
  } catch (error48) {
294350
294755
  safeLogError("Withdraw failed", error48);
294756
+ archiveTransferEventInBackground(brokerArchiver, {
294757
+ exchange: cex3,
294758
+ accountSelector: selectedBrokerAccount?.label,
294759
+ assetSymbol: symbol2,
294760
+ transfer: {
294761
+ eventKind: "withdrawal",
294762
+ lifecycleAction: "submit_withdrawal",
294763
+ status: "failed",
294764
+ amount: String(transferValue.amount),
294765
+ address: transferValue.recipientAddress,
294766
+ network: withdrawNetwork.exchangeNetworkId,
294767
+ errorSummary: getErrorMessage(error48),
294768
+ payload: { recipientAddress: transferValue.recipientAddress }
294769
+ }
294770
+ });
294351
294771
  const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
294352
294772
  ctx.wrappedCallback({
294353
294773
  code,
294354
- message: `Withdraw failed: ${getErrorMessage(error48)}`
294774
+ message: `Withdraw failed: ${sanitizeErrorDetail(error48)}`
294355
294775
  }, null);
294356
294776
  }
294357
294777
  }
@@ -294395,7 +294815,8 @@ function createExecuteActionHandler(deps) {
294395
294815
  useVerity,
294396
294816
  verityProverUrl,
294397
294817
  otelMetrics,
294398
- brokerArchiver
294818
+ brokerArchiver,
294819
+ orderActivityTracker
294399
294820
  } = deps;
294400
294821
  return async (call, callback) => {
294401
294822
  const startTime = Date.now();
@@ -294474,7 +294895,8 @@ function createExecuteActionHandler(deps) {
294474
294895
  useVerity,
294475
294896
  verityProverUrl,
294476
294897
  otelMetrics,
294477
- brokerArchiver
294898
+ brokerArchiver,
294899
+ orderActivityTracker
294478
294900
  };
294479
294901
  if (action === Action.Call) {
294480
294902
  const handled = await handleOrderBookCall(preludeCtx);
@@ -296064,7 +296486,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
296064
296486
  // src/server.ts
296065
296487
  var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
296066
296488
  var cexNode = grpcObj.cex_broker;
296067
- function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
296489
+ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
296068
296490
  const server = new grpc14.Server;
296069
296491
  server.addService(cexNode.cex_service.service, {
296070
296492
  ExecuteAction: createExecuteActionHandler({
@@ -296074,7 +296496,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
296074
296496
  useVerity,
296075
296497
  verityProverUrl,
296076
296498
  otelMetrics,
296077
- brokerArchiver
296499
+ brokerArchiver,
296500
+ orderActivityTracker
296078
296501
  }),
296079
296502
  Subscribe: createSubscribeHandler({
296080
296503
  brokers,
@@ -296219,6 +296642,8 @@ class CEXBroker {
296219
296642
  otelLogs;
296220
296643
  brokerArchiver;
296221
296644
  depositReconciler;
296645
+ orderActivityTracker = new OrderActivityTracker;
296646
+ fillArchivePoller;
296222
296647
  loadEnvConfig() {
296223
296648
  log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
296224
296649
  const configMap = {};
@@ -296359,6 +296784,10 @@ class CEXBroker {
296359
296784
  this.depositReconciler.stop();
296360
296785
  this.depositReconciler = undefined;
296361
296786
  }
296787
+ if (this.fillArchivePoller) {
296788
+ this.fillArchivePoller.stop();
296789
+ this.fillArchivePoller = undefined;
296790
+ }
296362
296791
  if (this.server) {
296363
296792
  await this.server.forceShutdown();
296364
296793
  }
@@ -296380,11 +296809,15 @@ class CEXBroker {
296380
296809
  this.depositReconciler.stop();
296381
296810
  this.depositReconciler = undefined;
296382
296811
  }
296812
+ if (this.fillArchivePoller) {
296813
+ this.fillArchivePoller.stop();
296814
+ this.fillArchivePoller = undefined;
296815
+ }
296383
296816
  log.info(`Running CEXBroker at ${new Date().toISOString()}`);
296384
296817
  if (this.otelMetrics?.isOtelEnabled()) {
296385
296818
  await this.otelMetrics.initialize();
296386
296819
  }
296387
- this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver);
296820
+ this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
296388
296821
  this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
296389
296822
  if (err2) {
296390
296823
  log.error(err2);
@@ -296399,6 +296832,15 @@ class CEXBroker {
296399
296832
  metrics: this.otelMetrics
296400
296833
  });
296401
296834
  this.depositReconciler.start();
296835
+ if (this.brokerArchiver?.isEnabled()) {
296836
+ this.fillArchivePoller = new FillArchivePoller({
296837
+ brokers: this.brokers,
296838
+ archiver: this.brokerArchiver,
296839
+ tracker: this.orderActivityTracker,
296840
+ metrics: this.otelMetrics
296841
+ });
296842
+ this.fillArchivePoller.start();
296843
+ }
296402
296844
  return this;
296403
296845
  }
296404
296846
  }
@@ -296406,4 +296848,4 @@ export {
296406
296848
  CEXBroker as default
296407
296849
  };
296408
296850
 
296409
- //# debugId=2BAA8D1C40735DFB64756E2164756E21
296851
+ //# debugId=6EFE98438C50664564756E2164756E21