@usherlabs/cex-broker 0.2.20 → 0.2.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -274531,6 +274531,8 @@ function hashMarketMetadata(payload) {
274531
274531
 
274532
274532
  // src/helpers/broker-execution-archive/types.ts
274533
274533
  var BROKER_WRITE_SOURCE = "broker_write";
274534
+ var ARCHIVE_SCHEMA_VERSION = "1";
274535
+ var FILL_EVENT_KIND = "trade_history_fill";
274534
274536
 
274535
274537
  // src/helpers/broker-execution-archive/rows.ts
274536
274538
  function firstString2(...values2) {
@@ -274544,6 +274546,26 @@ function firstString2(...values2) {
274544
274546
  }
274545
274547
  return;
274546
274548
  }
274549
+ function firstNumber2(...values2) {
274550
+ for (const value of values2) {
274551
+ const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
274552
+ if (Number.isFinite(numeric)) {
274553
+ return numeric;
274554
+ }
274555
+ }
274556
+ return;
274557
+ }
274558
+ function normalizeTimestamp2(value) {
274559
+ if (typeof value === "string" && value.trim()) {
274560
+ return value.trim();
274561
+ }
274562
+ const ms = firstNumber2(value);
274563
+ if (ms === undefined) {
274564
+ return;
274565
+ }
274566
+ const date = new Date(ms);
274567
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
274568
+ }
274547
274569
  function compactUndefined2(record) {
274548
274570
  return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
274549
274571
  }
@@ -274608,6 +274630,99 @@ function buildSubscribeStreamArchiveRow(input) {
274608
274630
  })
274609
274631
  };
274610
274632
  }
274633
+ function quantityString(...values2) {
274634
+ return firstString2(...values2);
274635
+ }
274636
+ function buildTransferEventArchiveRow(input) {
274637
+ const { tags, transfer } = input;
274638
+ return {
274639
+ table: "broker_execution.transfer_events",
274640
+ row: compactUndefined2({
274641
+ ...tags,
274642
+ schema_version: ARCHIVE_SCHEMA_VERSION,
274643
+ event_kind: transfer.eventKind,
274644
+ lifecycle_action: transfer.lifecycleAction,
274645
+ status: transfer.status ?? "",
274646
+ asset_symbol: tags.symbol,
274647
+ amount: transfer.amount,
274648
+ address: transfer.address,
274649
+ network: transfer.network,
274650
+ external_id: transfer.externalId ?? "",
274651
+ txid: transfer.txid,
274652
+ result_index: transfer.resultIndex ?? 0,
274653
+ fee_amount: transfer.feeAmount,
274654
+ fee_currency: transfer.feeCurrency,
274655
+ exchange_timestamp: transfer.exchangeTimestamp,
274656
+ error_summary: transfer.errorSummary,
274657
+ payload_json: JSON.stringify(transfer.payload)
274658
+ })
274659
+ };
274660
+ }
274661
+ function normalizeCcxtTransactionForArchive(transaction) {
274662
+ const record = asRecord(transaction);
274663
+ const info = asRecord(record?.info);
274664
+ const fee = asRecord(record?.fee);
274665
+ return compactUndefined2({
274666
+ externalId: firstString2(record?.id, info?.id, record?.txid, info?.txId),
274667
+ txid: firstString2(record?.txid, info?.txId, info?.txid, info?.tx_hash),
274668
+ address: firstString2(record?.address, record?.addressTo, info?.address),
274669
+ network: firstString2(record?.network, info?.network),
274670
+ amount: quantityString(info?.amount, record?.amount),
274671
+ assetSymbol: firstString2(record?.currency, info?.coin, info?.asset),
274672
+ status: firstString2(record?.status, info?.status)?.toLowerCase(),
274673
+ feeAmount: quantityString(fee?.cost, record?.feeCost),
274674
+ feeCurrency: firstString2(fee?.currency, record?.feeCurrency),
274675
+ exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.applyTime))
274676
+ });
274677
+ }
274678
+ function firstValueForTransfer(...values2) {
274679
+ return values2.find((value) => value !== undefined && value !== null);
274680
+ }
274681
+ function buildFillEventArchiveRow(input) {
274682
+ const { tags, fill } = input;
274683
+ return {
274684
+ table: "broker_execution.fill_events",
274685
+ row: compactUndefined2({
274686
+ ...tags,
274687
+ schema_version: ARCHIVE_SCHEMA_VERSION,
274688
+ event_kind: FILL_EVENT_KIND,
274689
+ order_id: fill.orderId ?? "",
274690
+ client_order_id: fill.clientOrderId,
274691
+ fill_id: fill.fillId,
274692
+ fill_index: fill.fillIndex ?? 0,
274693
+ side: fill.side,
274694
+ order_type: fill.orderType,
274695
+ price: fill.price,
274696
+ base_quantity: fill.baseQuantity,
274697
+ quote_quantity: fill.quoteQuantity,
274698
+ fee_amount: fill.feeAmount,
274699
+ fee_currency: fill.feeCurrency,
274700
+ fee_rate: fill.feeRate,
274701
+ exchange_timestamp: fill.exchangeTimestamp,
274702
+ payload_json: JSON.stringify(fill.payload)
274703
+ })
274704
+ };
274705
+ }
274706
+ function normalizeCcxtTradeForArchive(trade) {
274707
+ const record = asRecord(trade);
274708
+ const info = asRecord(record?.info);
274709
+ const fee = asRecord(record?.fee);
274710
+ return {
274711
+ orderId: firstString2(record?.order, info?.orderId, info?.orderID),
274712
+ clientOrderId: firstString2(record?.clientOrderId, info?.clientOrderId, info?.origClientOrderId),
274713
+ fillId: firstString2(record?.id, info?.id, info?.tradeId),
274714
+ side: firstString2(record?.side, info?.side)?.toLowerCase(),
274715
+ orderType: firstString2(record?.type, info?.type)?.toLowerCase(),
274716
+ price: quantityString(info?.price, record?.price),
274717
+ baseQuantity: quantityString(info?.qty, record?.amount),
274718
+ quoteQuantity: quantityString(info?.quoteQty, record?.cost),
274719
+ feeAmount: quantityString(fee?.cost, info?.commission),
274720
+ feeCurrency: firstString2(fee?.currency, info?.commissionAsset),
274721
+ feeRate: quantityString(fee?.rate),
274722
+ exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.time)),
274723
+ payload: trade
274724
+ };
274725
+ }
274611
274726
  function buildMarketMetadataSnapshotRow(input) {
274612
274727
  const redactedSnapshot = redactStreamPayload(input.marketSnapshot);
274613
274728
  const metadataHash = hashMarketMetadata(redactedSnapshot);
@@ -274676,6 +274791,25 @@ function archiveSubscribeStreamInBackground(archiver, input) {
274676
274791
  }
274677
274792
  });
274678
274793
  }
274794
+ function archiveTransferEventInBackground(archiver, input) {
274795
+ if (!archiver?.isEnabled()) {
274796
+ return;
274797
+ }
274798
+ queueMicrotask(() => {
274799
+ try {
274800
+ const tags = buildCommonArchiveTags({
274801
+ deploymentId: archiver.getDeploymentId(),
274802
+ accountSelector: input.accountSelector,
274803
+ exchange: input.exchange,
274804
+ symbol: input.assetSymbol,
274805
+ brokerObservedTimestamp: input.brokerObservedTimestamp
274806
+ });
274807
+ archiver.enqueue(buildTransferEventArchiveRow({ tags, transfer: input.transfer }));
274808
+ } catch (archiveError) {
274809
+ log.warn("Failed to archive transfer event", { error: archiveError });
274810
+ }
274811
+ });
274812
+ }
274679
274813
  async function captureMarketMetadataSnapshot(archiver, broker, input) {
274680
274814
  if (!archiver?.canPersistMarketMetadataSnapshot()) {
274681
274815
  return;
@@ -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) {
275209
+ try {
275210
+ await this.otelMetrics?.recordCounter(metricName, value, labels);
275211
+ } catch {}
275212
+ }
275213
+ async recordArchiveGauge(metricName, value) {
275064
275214
  try {
275065
- await this.otelMetrics?.recordCounter(metricName, 1, labels);
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
 
@@ -292907,7 +293210,13 @@ function rejectWithGrpcError(ctx, error48, options) {
292907
293210
 
292908
293211
  // src/handlers/execute-action/deposit.ts
292909
293212
  async function handleDeposit(ctx) {
292910
- const { normalizedCex, symbol: symbol2, selectedBrokerAccount, broker } = ctx;
293213
+ const {
293214
+ normalizedCex,
293215
+ symbol: symbol2,
293216
+ selectedBrokerAccount,
293217
+ broker,
293218
+ brokerArchiver
293219
+ } = ctx;
292911
293220
  if (!symbol2) {
292912
293221
  return ctx.wrappedCallback({
292913
293222
  code: grpc3.status.INVALID_ARGUMENT,
@@ -292970,6 +293279,32 @@ async function handleDeposit(ctx) {
292970
293279
  }, null);
292971
293280
  }
292972
293281
  const status4 = normalizeDepositStatus(depositField(deposit, ["status", "state"]));
293282
+ const depositTxid = String(depositField(deposit, ["txid", "txId", "tx_hash", "txHash"]) ?? value.transactionHash);
293283
+ const creditedAt = depositField(deposit, [
293284
+ "creditedAt",
293285
+ "credited_at",
293286
+ "updated",
293287
+ "updatedAt",
293288
+ "timestamp",
293289
+ "datetime"
293290
+ ]);
293291
+ archiveTransferEventInBackground(brokerArchiver, {
293292
+ exchange: normalizedCex,
293293
+ accountSelector: selectedBrokerAccount?.label,
293294
+ assetSymbol: symbol2,
293295
+ transfer: {
293296
+ eventKind: "deposit",
293297
+ lifecycleAction: "observe_deposit",
293298
+ status: status4,
293299
+ amount: observedAmount !== undefined ? String(observedAmount) : undefined,
293300
+ address: String(observedAddress ?? value.recipientAddress),
293301
+ network: depositNetwork?.exchangeNetworkId,
293302
+ externalId: depositTxid,
293303
+ txid: depositTxid,
293304
+ exchangeTimestamp: typeof creditedAt === "string" ? creditedAt : undefined,
293305
+ payload: deposit
293306
+ }
293307
+ });
292973
293308
  log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
292974
293309
  return ctx.wrappedCallback(null, {
292975
293310
  proof: ctx.verity.proof,
@@ -293383,7 +293718,8 @@ async function handleInternalTransfer(ctx) {
293383
293718
  symbol: symbol2,
293384
293719
  verity,
293385
293720
  useVerity,
293386
- verityProverUrl
293721
+ verityProverUrl,
293722
+ brokerArchiver
293387
293723
  } = ctx;
293388
293724
  if (!symbol2) {
293389
293725
  return ctx.wrappedCallback({
@@ -293431,6 +293767,21 @@ async function handleInternalTransfer(ctx) {
293431
293767
  }), verityHttpClientOverridePredicate);
293432
293768
  }
293433
293769
  const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
293770
+ const normalized = normalizeCcxtTransactionForArchive(result);
293771
+ archiveTransferEventInBackground(brokerArchiver, {
293772
+ exchange: normalizedCex,
293773
+ accountSelector: fromSelector,
293774
+ assetSymbol: symbol2,
293775
+ transfer: {
293776
+ eventKind: "internal_transfer",
293777
+ lifecycleAction: "submit_internal_transfer",
293778
+ status: normalized.status ?? "ok",
293779
+ amount: normalized.amount ?? String(transferPayload.amount),
293780
+ network: "internal",
293781
+ externalId: normalized.externalId,
293782
+ payload: { from: fromSelector, to: toSelector, result }
293783
+ }
293784
+ });
293434
293785
  ctx.wrappedCallback(null, {
293435
293786
  proof: verity.proof,
293436
293787
  result: JSON.stringify(result)
@@ -293478,7 +293829,8 @@ async function handleCreateOrder(ctx) {
293478
293829
  useVerity,
293479
293830
  verityProverUrl,
293480
293831
  otelMetrics,
293481
- brokerArchiver
293832
+ brokerArchiver,
293833
+ orderActivityTracker
293482
293834
  } = ctx;
293483
293835
  const verityProof = verity.proof;
293484
293836
  const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
@@ -293504,6 +293856,9 @@ async function handleCreateOrder(ctx) {
293504
293856
  side: resolution.side,
293505
293857
  requestedQuantity: resolution.amountBase ?? orderValue.amount
293506
293858
  };
293859
+ if (selectedBrokerAccount?.label) {
293860
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
293861
+ }
293507
293862
  const telemetryIds = extractOrderTelemetryIds(orderValue.params);
293508
293863
  const submissionTimestamp = new Date().toISOString();
293509
293864
  const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
@@ -293568,7 +293923,8 @@ async function handleGetOrderDetails(ctx) {
293568
293923
  useVerity,
293569
293924
  verityProverUrl,
293570
293925
  otelMetrics,
293571
- brokerArchiver
293926
+ brokerArchiver,
293927
+ orderActivityTracker
293572
293928
  } = ctx;
293573
293929
  const verityProof = verity.proof;
293574
293930
  const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
@@ -293582,6 +293938,9 @@ async function handleGetOrderDetails(ctx) {
293582
293938
  }, null);
293583
293939
  }
293584
293940
  const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
293941
+ if (selectedBrokerAccount?.label && symbol2) {
293942
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
293943
+ }
293585
293944
  const getOrderContext = {
293586
293945
  action: "GetOrderDetails",
293587
293946
  cex: cex3,
@@ -293637,7 +293996,8 @@ async function handleCancelOrder(ctx) {
293637
293996
  useVerity,
293638
293997
  verityProverUrl,
293639
293998
  otelMetrics,
293640
- brokerArchiver
293999
+ brokerArchiver,
294000
+ orderActivityTracker
293641
294001
  } = ctx;
293642
294002
  const verityProof = verity.proof;
293643
294003
  const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
@@ -293658,6 +294018,9 @@ async function handleCancelOrder(ctx) {
293658
294018
  ...extractOrderTelemetryIds(cancelOrderValue.params)
293659
294019
  };
293660
294020
  const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
294021
+ if (selectedBrokerAccount?.label && symbol2) {
294022
+ orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
294023
+ }
293661
294024
  emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
293662
294025
  archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
293663
294026
  ctx.wrappedCallback(null, {
@@ -294288,7 +294651,8 @@ async function handleWithdraw(ctx) {
294288
294651
  applyVerityToBroker,
294289
294652
  useVerity,
294290
294653
  verityProverUrl,
294291
- otelMetrics
294654
+ otelMetrics,
294655
+ brokerArchiver
294292
294656
  } = ctx;
294293
294657
  const verityProof = verity.proof;
294294
294658
  if (!symbol2) {
@@ -294337,6 +294701,26 @@ async function handleWithdraw(ctx) {
294337
294701
  network: withdrawNetwork.exchangeNetworkId
294338
294702
  });
294339
294703
  log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
294704
+ const normalized = normalizeCcxtTransactionForArchive(transaction);
294705
+ archiveTransferEventInBackground(brokerArchiver, {
294706
+ exchange: cex3,
294707
+ accountSelector: selectedBrokerAccount?.label,
294708
+ assetSymbol: normalized.assetSymbol ?? symbol2,
294709
+ transfer: {
294710
+ eventKind: "withdrawal",
294711
+ lifecycleAction: "submit_withdrawal",
294712
+ status: normalized.status,
294713
+ amount: normalized.amount ?? String(transferValue.amount),
294714
+ address: normalized.address ?? transferValue.recipientAddress,
294715
+ network: normalized.network ?? withdrawNetwork.exchangeNetworkId,
294716
+ externalId: normalized.externalId,
294717
+ txid: normalized.txid,
294718
+ feeAmount: normalized.feeAmount,
294719
+ feeCurrency: normalized.feeCurrency,
294720
+ exchangeTimestamp: normalized.exchangeTimestamp,
294721
+ payload: transaction
294722
+ }
294723
+ });
294340
294724
  ctx.wrappedCallback(null, {
294341
294725
  proof: ctx.verity.proof,
294342
294726
  result: JSON.stringify({
@@ -294348,6 +294732,21 @@ async function handleWithdraw(ctx) {
294348
294732
  });
294349
294733
  } catch (error48) {
294350
294734
  safeLogError("Withdraw failed", error48);
294735
+ archiveTransferEventInBackground(brokerArchiver, {
294736
+ exchange: cex3,
294737
+ accountSelector: selectedBrokerAccount?.label,
294738
+ assetSymbol: symbol2,
294739
+ transfer: {
294740
+ eventKind: "withdrawal",
294741
+ lifecycleAction: "submit_withdrawal",
294742
+ status: "failed",
294743
+ amount: String(transferValue.amount),
294744
+ address: transferValue.recipientAddress,
294745
+ network: withdrawNetwork.exchangeNetworkId,
294746
+ errorSummary: getErrorMessage(error48),
294747
+ payload: { recipientAddress: transferValue.recipientAddress }
294748
+ }
294749
+ });
294351
294750
  const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
294352
294751
  ctx.wrappedCallback({
294353
294752
  code,
@@ -294395,7 +294794,8 @@ function createExecuteActionHandler(deps) {
294395
294794
  useVerity,
294396
294795
  verityProverUrl,
294397
294796
  otelMetrics,
294398
- brokerArchiver
294797
+ brokerArchiver,
294798
+ orderActivityTracker
294399
294799
  } = deps;
294400
294800
  return async (call, callback) => {
294401
294801
  const startTime = Date.now();
@@ -294474,7 +294874,8 @@ function createExecuteActionHandler(deps) {
294474
294874
  useVerity,
294475
294875
  verityProverUrl,
294476
294876
  otelMetrics,
294477
- brokerArchiver
294877
+ brokerArchiver,
294878
+ orderActivityTracker
294478
294879
  };
294479
294880
  if (action === Action.Call) {
294480
294881
  const handled = await handleOrderBookCall(preludeCtx);
@@ -296064,7 +296465,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
296064
296465
  // src/server.ts
296065
296466
  var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
296066
296467
  var cexNode = grpcObj.cex_broker;
296067
- function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
296468
+ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
296068
296469
  const server = new grpc14.Server;
296069
296470
  server.addService(cexNode.cex_service.service, {
296070
296471
  ExecuteAction: createExecuteActionHandler({
@@ -296074,7 +296475,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
296074
296475
  useVerity,
296075
296476
  verityProverUrl,
296076
296477
  otelMetrics,
296077
- brokerArchiver
296478
+ brokerArchiver,
296479
+ orderActivityTracker
296078
296480
  }),
296079
296481
  Subscribe: createSubscribeHandler({
296080
296482
  brokers,
@@ -296219,6 +296621,8 @@ class CEXBroker {
296219
296621
  otelLogs;
296220
296622
  brokerArchiver;
296221
296623
  depositReconciler;
296624
+ orderActivityTracker = new OrderActivityTracker;
296625
+ fillArchivePoller;
296222
296626
  loadEnvConfig() {
296223
296627
  log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
296224
296628
  const configMap = {};
@@ -296359,6 +296763,10 @@ class CEXBroker {
296359
296763
  this.depositReconciler.stop();
296360
296764
  this.depositReconciler = undefined;
296361
296765
  }
296766
+ if (this.fillArchivePoller) {
296767
+ this.fillArchivePoller.stop();
296768
+ this.fillArchivePoller = undefined;
296769
+ }
296362
296770
  if (this.server) {
296363
296771
  await this.server.forceShutdown();
296364
296772
  }
@@ -296380,11 +296788,15 @@ class CEXBroker {
296380
296788
  this.depositReconciler.stop();
296381
296789
  this.depositReconciler = undefined;
296382
296790
  }
296791
+ if (this.fillArchivePoller) {
296792
+ this.fillArchivePoller.stop();
296793
+ this.fillArchivePoller = undefined;
296794
+ }
296383
296795
  log.info(`Running CEXBroker at ${new Date().toISOString()}`);
296384
296796
  if (this.otelMetrics?.isOtelEnabled()) {
296385
296797
  await this.otelMetrics.initialize();
296386
296798
  }
296387
- this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver);
296799
+ this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
296388
296800
  this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
296389
296801
  if (err2) {
296390
296802
  log.error(err2);
@@ -296399,6 +296811,15 @@ class CEXBroker {
296399
296811
  metrics: this.otelMetrics
296400
296812
  });
296401
296813
  this.depositReconciler.start();
296814
+ if (this.brokerArchiver?.isEnabled()) {
296815
+ this.fillArchivePoller = new FillArchivePoller({
296816
+ brokers: this.brokers,
296817
+ archiver: this.brokerArchiver,
296818
+ tracker: this.orderActivityTracker,
296819
+ metrics: this.otelMetrics
296820
+ });
296821
+ this.fillArchivePoller.start();
296822
+ }
296402
296823
  return this;
296403
296824
  }
296404
296825
  }
@@ -296406,4 +296827,4 @@ export {
296406
296827
  CEXBroker as default
296407
296828
  };
296408
296829
 
296409
- //# debugId=2BAA8D1C40735DFB64756E2164756E21
296830
+ //# debugId=72B06323144A22CC64756E2164756E21