@usherlabs/cex-broker 0.2.22 → 0.2.24

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.
@@ -315557,6 +315557,52 @@ function archiveTransferEventInBackground(archiver, input) {
315557
315557
  }
315558
315558
  });
315559
315559
  }
315560
+ function archiveWithdrawalObservationsInBackground(archiver, tracker, input) {
315561
+ try {
315562
+ if (!archiver?.isEnabled() || !Array.isArray(input.transactions)) {
315563
+ return;
315564
+ }
315565
+ const brokerObservedTimestamp = new Date().toISOString();
315566
+ for (const [resultIndex, transaction] of input.transactions.entries()) {
315567
+ const normalized = normalizeCcxtTransactionForArchive(transaction);
315568
+ const assetSymbol = normalized.assetSymbol;
315569
+ if (!tracker.shouldArchive({
315570
+ exchange: input.exchange,
315571
+ accountSelector: input.accountSelector,
315572
+ assetSymbol,
315573
+ transaction,
315574
+ normalized
315575
+ })) {
315576
+ continue;
315577
+ }
315578
+ archiveTransferEventInBackground(archiver, {
315579
+ exchange: input.exchange,
315580
+ accountSelector: input.accountSelector,
315581
+ assetSymbol,
315582
+ brokerObservedTimestamp,
315583
+ transfer: {
315584
+ eventKind: "withdrawal",
315585
+ lifecycleAction: "observe_withdrawal",
315586
+ status: normalized.status,
315587
+ amount: normalized.amount,
315588
+ address: normalized.address,
315589
+ network: normalized.network,
315590
+ externalId: normalized.externalId,
315591
+ txid: normalized.txid,
315592
+ resultIndex,
315593
+ feeAmount: normalized.feeAmount,
315594
+ feeCurrency: normalized.feeCurrency,
315595
+ exchangeTimestamp: normalized.exchangeTimestamp,
315596
+ payload: transaction
315597
+ }
315598
+ });
315599
+ }
315600
+ } catch (archiveError) {
315601
+ log.warn("Failed to archive withdrawal observations", {
315602
+ error: archiveError
315603
+ });
315604
+ }
315605
+ }
315560
315606
  async function captureMarketMetadataSnapshot(archiver, broker, input) {
315561
315607
  if (!archiver?.canPersistMarketMetadataSnapshot()) {
315562
315608
  return;
@@ -315602,6 +315648,106 @@ async function captureMarketMetadataSnapshot(archiver, broker, input) {
315602
315648
  return;
315603
315649
  }
315604
315650
  }
315651
+ // src/helpers/broker-execution-archive/withdrawal-observation-tracker.ts
315652
+ var DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES = 1e4;
315653
+ var VENUE_LIFECYCLE_EVIDENCE_FIELDS = [
315654
+ "updated",
315655
+ "updatedAt",
315656
+ "updated_at",
315657
+ "updateTime",
315658
+ "update_time",
315659
+ "updateTimestamp",
315660
+ "lastUpdateTimestamp",
315661
+ "completed",
315662
+ "completedAt",
315663
+ "completed_at",
315664
+ "completeTime",
315665
+ "complete_time",
315666
+ "completionTime",
315667
+ "successTime"
315668
+ ];
315669
+ function fingerprintEvidenceValue(value) {
315670
+ if (value === undefined || value === null) {
315671
+ return value;
315672
+ }
315673
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
315674
+ return value;
315675
+ }
315676
+ if (typeof value === "bigint") {
315677
+ return value.toString();
315678
+ }
315679
+ try {
315680
+ return JSON.stringify(value);
315681
+ } catch {
315682
+ return String(value);
315683
+ }
315684
+ }
315685
+ function venueLifecycleEvidence(transaction) {
315686
+ const record = asRecord(transaction);
315687
+ const info = asRecord(record?.info);
315688
+ const evidence = [];
315689
+ for (const [scope, source] of [
315690
+ ["transaction", record],
315691
+ ["info", info]
315692
+ ]) {
315693
+ for (const field of VENUE_LIFECYCLE_EVIDENCE_FIELDS) {
315694
+ const value = source?.[field];
315695
+ if (value !== undefined && value !== null) {
315696
+ evidence.push([scope, field, fingerprintEvidenceValue(value)]);
315697
+ }
315698
+ }
315699
+ }
315700
+ return evidence;
315701
+ }
315702
+ function observationFingerprint(observation) {
315703
+ const { normalized, transaction } = observation;
315704
+ return JSON.stringify({
315705
+ status: normalized.status ?? null,
315706
+ txid: normalized.txid ?? null,
315707
+ amount: normalized.amount ?? null,
315708
+ feeAmount: normalized.feeAmount ?? null,
315709
+ feeCurrency: normalized.feeCurrency ?? null,
315710
+ address: normalized.address ?? null,
315711
+ network: normalized.network ?? null,
315712
+ exchangeTimestamp: normalized.exchangeTimestamp ?? null,
315713
+ venueLifecycleEvidence: venueLifecycleEvidence(transaction)
315714
+ });
315715
+ }
315716
+
315717
+ class WithdrawalObservationTracker {
315718
+ #fingerprints = new Map;
315719
+ #maxEntries;
315720
+ #missingIdSequence = 0n;
315721
+ constructor(options) {
315722
+ const maxEntries = options?.maxEntries ?? DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
315723
+ this.#maxEntries = Number.isFinite(maxEntries) ? Math.max(1, Math.floor(maxEntries)) : DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
315724
+ }
315725
+ shouldArchive(observation) {
315726
+ const externalId = observation.normalized.externalId?.trim();
315727
+ const identity = JSON.stringify([
315728
+ observation.exchange.trim().toLowerCase() || "unknown",
315729
+ observation.accountSelector?.trim() || "unknown",
315730
+ observation.assetSymbol?.trim().toUpperCase() || "unknown",
315731
+ externalId || `missing:${++this.#missingIdSequence}`
315732
+ ]);
315733
+ const fingerprint = observationFingerprint(observation);
315734
+ if (this.#fingerprints.get(identity) === fingerprint) {
315735
+ return false;
315736
+ }
315737
+ this.#fingerprints.delete(identity);
315738
+ this.#fingerprints.set(identity, fingerprint);
315739
+ while (this.#fingerprints.size > this.#maxEntries) {
315740
+ const oldest = this.#fingerprints.keys().next().value;
315741
+ if (oldest === undefined)
315742
+ break;
315743
+ this.#fingerprints.delete(oldest);
315744
+ }
315745
+ return true;
315746
+ }
315747
+ getSize() {
315748
+ return this.#fingerprints.size;
315749
+ }
315750
+ }
315605
315751
  // src/helpers/broker-execution-archive/writer.ts
315606
315752
  var import_api_logs2 = __toESM(require_src7(), 1);
315607
315753
  import { request as httpRequest2 } from "node:http";
@@ -331688,6 +331834,13 @@ async function handleTreasuryCall(ctx) {
331688
331834
  }, null);
331689
331835
  }
331690
331836
  const result = await fn.apply(broker, argsArray);
331837
+ if (callValue.functionName === "fetchWithdrawals") {
331838
+ archiveWithdrawalObservationsInBackground(ctx.brokerArchiver, ctx.withdrawalObservationTracker, {
331839
+ exchange: ctx.normalizedCex,
331840
+ accountSelector: ctx.selectedBrokerAccount?.label,
331841
+ transactions: result
331842
+ });
331843
+ }
331691
331844
  ctx.wrappedCallback(null, {
331692
331845
  proof: ctx.verity.proof,
331693
331846
  result: JSON.stringify(result)
@@ -331866,6 +332019,7 @@ function createExecuteActionHandler(deps) {
331866
332019
  brokerArchiver,
331867
332020
  orderActivityTracker
331868
332021
  } = deps;
332022
+ const withdrawalObservationTracker = deps.withdrawalObservationTracker ?? new WithdrawalObservationTracker;
331869
332023
  return async (call, callback) => {
331870
332024
  const startTime = Date.now();
331871
332025
  const { action: rawAction, cex: cex3, symbol: symbol2 } = call.request;
@@ -331944,7 +332098,8 @@ function createExecuteActionHandler(deps) {
331944
332098
  verityProverUrl,
331945
332099
  otelMetrics,
331946
332100
  brokerArchiver,
331947
- orderActivityTracker
332101
+ orderActivityTracker,
332102
+ withdrawalObservationTracker
331948
332103
  };
331949
332104
  if (action === Action.Call) {
331950
332105
  const handled = await handleOrderBookCall(preludeCtx);
@@ -333533,7 +333688,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
333533
333688
  // src/server.ts
333534
333689
  var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
333535
333690
  var cexNode = grpcObj.cex_broker;
333536
- function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
333691
+ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker, withdrawalObservationTracker) {
333537
333692
  const server = new grpc14.Server;
333538
333693
  server.addService(cexNode.cex_service.service, {
333539
333694
  ExecuteAction: createExecuteActionHandler({
@@ -333544,7 +333699,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
333544
333699
  verityProverUrl,
333545
333700
  otelMetrics,
333546
333701
  brokerArchiver,
333547
- orderActivityTracker
333702
+ orderActivityTracker,
333703
+ withdrawalObservationTracker
333548
333704
  }),
333549
333705
  Subscribe: createSubscribeHandler({
333550
333706
  brokers,
@@ -333690,6 +333846,7 @@ class CEXBroker {
333690
333846
  brokerArchiver;
333691
333847
  depositReconciler;
333692
333848
  orderActivityTracker = new OrderActivityTracker;
333849
+ withdrawalObservationTracker = new WithdrawalObservationTracker;
333693
333850
  fillArchivePoller;
333694
333851
  loadEnvConfig() {
333695
333852
  log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
@@ -333864,7 +334021,7 @@ class CEXBroker {
333864
334021
  if (this.otelMetrics?.isOtelEnabled()) {
333865
334022
  await this.otelMetrics.initialize();
333866
334023
  }
333867
- this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
334024
+ this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker, this.withdrawalObservationTracker);
333868
334025
  this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
333869
334026
  if (err2) {
333870
334027
  log.error(err2);
@@ -3,7 +3,7 @@ import type { Metadata } from "@grpc/grpc-js";
3
3
  import type { Exchange } from "@usherlabs/ccxt";
4
4
  import type { z } from "zod";
5
5
  import type { BrokerAccount, BrokerPoolEntry } from "../../helpers";
6
- import type { BrokerExecutionArchiver } from "../../helpers/broker-execution-archive";
6
+ import type { BrokerExecutionArchiver, WithdrawalObservationTracker } from "../../helpers/broker-execution-archive";
7
7
  import type { Action as ActionType } from "../../helpers/constants";
8
8
  import type { OrderActivityTracker } from "../../helpers/order-activity-tracker";
9
9
  import type { OtelMetrics } from "../../helpers/otel";
@@ -31,6 +31,7 @@ export type ExecuteActionContext = {
31
31
  otelMetrics?: OtelMetrics;
32
32
  brokerArchiver?: BrokerExecutionArchiver;
33
33
  orderActivityTracker?: OrderActivityTracker;
34
+ withdrawalObservationTracker: WithdrawalObservationTracker;
34
35
  };
35
36
  export declare function requireSymbol(ctx: ExecuteActionContext, message?: string): ctx is ExecuteActionContext & {
36
37
  symbol: string;
@@ -1,6 +1,6 @@
1
1
  import * as grpc from "@grpc/grpc-js";
2
2
  import { type BrokerPoolEntry } from "../../helpers/broker";
3
- import type { BrokerExecutionArchiver } from "../../helpers/broker-execution-archive";
3
+ import { type BrokerExecutionArchiver, WithdrawalObservationTracker } from "../../helpers/broker-execution-archive";
4
4
  import type { OrderActivityTracker } from "../../helpers/order-activity-tracker";
5
5
  import type { OtelMetrics } from "../../helpers/otel";
6
6
  import type { PolicyConfig } from "../../types";
@@ -14,5 +14,6 @@ export type ExecuteActionDeps = {
14
14
  otelMetrics?: OtelMetrics;
15
15
  brokerArchiver?: BrokerExecutionArchiver;
16
16
  orderActivityTracker?: OrderActivityTracker;
17
+ withdrawalObservationTracker?: WithdrawalObservationTracker;
17
18
  };
18
19
  export declare function createExecuteActionHandler(deps: ExecuteActionDeps): (call: grpc.ServerUnaryCall<ActionRequest, ActionResponse>, callback: grpc.sendUnaryData<ActionResponse>) => Promise<void>;
@@ -2,6 +2,7 @@ import type { Exchange } from "@usherlabs/ccxt";
2
2
  import { type OrderTelemetryAction, type OrderTelemetryContext } from "../order-telemetry";
3
3
  import { type TransferArchiveFields } from "./rows";
4
4
  import type { SubscribeArchiveType } from "./types";
5
+ import type { WithdrawalObservationTracker } from "./withdrawal-observation-tracker";
5
6
  import type { BrokerExecutionArchiver } from "./writer";
6
7
  export declare function archiveOrderExecutionInBackground(archiver: BrokerExecutionArchiver | undefined, context: OrderTelemetryContext, order: unknown, error?: unknown, options?: {
7
8
  marketMetadataHash?: string;
@@ -21,6 +22,11 @@ export declare function archiveTransferEventInBackground(archiver: BrokerExecuti
21
22
  brokerObservedTimestamp?: string;
22
23
  transfer: TransferArchiveFields;
23
24
  }): void;
25
+ export declare function archiveWithdrawalObservationsInBackground(archiver: BrokerExecutionArchiver | undefined, tracker: WithdrawalObservationTracker, input: {
26
+ exchange: string;
27
+ accountSelector?: string;
28
+ transactions: unknown;
29
+ }): void;
24
30
  export declare function captureMarketMetadataSnapshot(archiver: BrokerExecutionArchiver | undefined, broker: Exchange, input: {
25
31
  exchange: string;
26
32
  accountSelector?: string;
@@ -1,5 +1,6 @@
1
- export { archiveOrderExecutionInBackground, archiveSubscribeStreamInBackground, archiveTransferEventInBackground, captureMarketMetadataSnapshot, captureMarketMetadataSnapshotInBackground, } from "./capture";
1
+ export { archiveOrderExecutionInBackground, archiveSubscribeStreamInBackground, archiveTransferEventInBackground, archiveWithdrawalObservationsInBackground, captureMarketMetadataSnapshot, captureMarketMetadataSnapshotInBackground, } from "./capture";
2
2
  export { hashMarketMetadata, redactErrorForArchive, redactSecretLiterals, redactStreamPayload, } from "./redact";
3
- export { buildCommonArchiveTags, buildFillEventArchiveRow, buildMarketMetadataSnapshotRow, buildOrderEventArchiveRow, buildSubscribeStreamArchiveRow, buildTransferEventArchiveRow, type FillArchiveFields, normalizeCcxtTradeForArchive, normalizeCcxtTransactionForArchive, type NormalizedCcxtTransfer, type TransferArchiveFields, } from "./rows";
3
+ export { buildCommonArchiveTags, buildFillEventArchiveRow, buildMarketMetadataSnapshotRow, buildOrderEventArchiveRow, buildSubscribeStreamArchiveRow, buildTransferEventArchiveRow, type FillArchiveFields, type NormalizedCcxtTransfer, normalizeCcxtTradeForArchive, normalizeCcxtTransactionForArchive, type TransferArchiveFields, } from "./rows";
4
4
  export { ARCHIVE_SCHEMA_VERSION, BROKER_WRITE_SOURCE, type BrokerArchiveCommonTags, type BrokerArchiveRow, type BrokerArchiveTable, type OrderArchiveAction, type SubscribeArchiveType, type TransferEventKind, type TransferLifecycleAction, } from "./types";
5
+ export { DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES, WithdrawalObservationTracker, } from "./withdrawal-observation-tracker";
5
6
  export { BrokerExecutionArchiver, type BrokerExecutionArchiverOptions, createBrokerExecutionArchiverFromEnv, isArchiveOtelLogsEnabled, resolveArchiveForwarderUrlFromEnv, } from "./writer";
@@ -16,5 +16,5 @@ export type BrokerArchiveCommonTags = {
16
16
  export type OrderArchiveAction = "CreateOrder" | "CancelOrder" | "GetOrderDetails";
17
17
  export type SubscribeArchiveType = "ORDERS" | "BALANCE";
18
18
  export type TransferEventKind = "withdrawal" | "deposit" | "internal_transfer";
19
- export type TransferLifecycleAction = "submit_withdrawal" | "observe_deposit" | "submit_internal_transfer";
19
+ export type TransferLifecycleAction = "submit_withdrawal" | "observe_withdrawal" | "observe_deposit" | "submit_internal_transfer";
20
20
  export declare const FILL_EVENT_KIND: "trade_history_fill";
@@ -0,0 +1,23 @@
1
+ import type { NormalizedCcxtTransfer } from "./rows";
2
+ export declare const DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES = 10000;
3
+ type WithdrawalObservation = {
4
+ exchange: string;
5
+ accountSelector?: string;
6
+ assetSymbol?: string;
7
+ transaction: unknown;
8
+ normalized: NormalizedCcxtTransfer;
9
+ };
10
+ /**
11
+ * Suppresses unchanged withdrawal-history observations within one broker process.
12
+ * The tracker intentionally persists no state: restart replay is absorbed by
13
+ * archive consumers, while the in-process bound prevents unbounded venue history.
14
+ */
15
+ export declare class WithdrawalObservationTracker {
16
+ #private;
17
+ constructor(options?: {
18
+ maxEntries?: number;
19
+ });
20
+ shouldArchive(observation: WithdrawalObservation): boolean;
21
+ getSize(): number;
22
+ }
23
+ export {};
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export default class CEXBroker {
14
14
  private brokerArchiver?;
15
15
  private depositReconciler?;
16
16
  private readonly orderActivityTracker;
17
+ private readonly withdrawalObservationTracker;
17
18
  private fillArchivePoller?;
18
19
  /**
19
20
  * Loads environment variables prefixed with CEX_BROKER_
package/dist/index.js CHANGED
@@ -274810,6 +274810,52 @@ function archiveTransferEventInBackground(archiver, input) {
274810
274810
  }
274811
274811
  });
274812
274812
  }
274813
+ function archiveWithdrawalObservationsInBackground(archiver, tracker, input) {
274814
+ try {
274815
+ if (!archiver?.isEnabled() || !Array.isArray(input.transactions)) {
274816
+ return;
274817
+ }
274818
+ const brokerObservedTimestamp = new Date().toISOString();
274819
+ for (const [resultIndex, transaction] of input.transactions.entries()) {
274820
+ const normalized = normalizeCcxtTransactionForArchive(transaction);
274821
+ const assetSymbol = normalized.assetSymbol;
274822
+ if (!tracker.shouldArchive({
274823
+ exchange: input.exchange,
274824
+ accountSelector: input.accountSelector,
274825
+ assetSymbol,
274826
+ transaction,
274827
+ normalized
274828
+ })) {
274829
+ continue;
274830
+ }
274831
+ archiveTransferEventInBackground(archiver, {
274832
+ exchange: input.exchange,
274833
+ accountSelector: input.accountSelector,
274834
+ assetSymbol,
274835
+ brokerObservedTimestamp,
274836
+ transfer: {
274837
+ eventKind: "withdrawal",
274838
+ lifecycleAction: "observe_withdrawal",
274839
+ status: normalized.status,
274840
+ amount: normalized.amount,
274841
+ address: normalized.address,
274842
+ network: normalized.network,
274843
+ externalId: normalized.externalId,
274844
+ txid: normalized.txid,
274845
+ resultIndex,
274846
+ feeAmount: normalized.feeAmount,
274847
+ feeCurrency: normalized.feeCurrency,
274848
+ exchangeTimestamp: normalized.exchangeTimestamp,
274849
+ payload: transaction
274850
+ }
274851
+ });
274852
+ }
274853
+ } catch (archiveError) {
274854
+ log.warn("Failed to archive withdrawal observations", {
274855
+ error: archiveError
274856
+ });
274857
+ }
274858
+ }
274813
274859
  async function captureMarketMetadataSnapshot(archiver, broker, input) {
274814
274860
  if (!archiver?.canPersistMarketMetadataSnapshot()) {
274815
274861
  return;
@@ -274855,6 +274901,106 @@ async function captureMarketMetadataSnapshot(archiver, broker, input) {
274855
274901
  return;
274856
274902
  }
274857
274903
  }
274904
+ // src/helpers/broker-execution-archive/withdrawal-observation-tracker.ts
274905
+ var DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES = 1e4;
274906
+ var VENUE_LIFECYCLE_EVIDENCE_FIELDS = [
274907
+ "updated",
274908
+ "updatedAt",
274909
+ "updated_at",
274910
+ "updateTime",
274911
+ "update_time",
274912
+ "updateTimestamp",
274913
+ "lastUpdateTimestamp",
274914
+ "completed",
274915
+ "completedAt",
274916
+ "completed_at",
274917
+ "completeTime",
274918
+ "complete_time",
274919
+ "completionTime",
274920
+ "successTime"
274921
+ ];
274922
+ function fingerprintEvidenceValue(value) {
274923
+ if (value === undefined || value === null) {
274924
+ return value;
274925
+ }
274926
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
274927
+ return value;
274928
+ }
274929
+ if (typeof value === "bigint") {
274930
+ return value.toString();
274931
+ }
274932
+ try {
274933
+ return JSON.stringify(value);
274934
+ } catch {
274935
+ return String(value);
274936
+ }
274937
+ }
274938
+ function venueLifecycleEvidence(transaction) {
274939
+ const record = asRecord(transaction);
274940
+ const info = asRecord(record?.info);
274941
+ const evidence = [];
274942
+ for (const [scope, source] of [
274943
+ ["transaction", record],
274944
+ ["info", info]
274945
+ ]) {
274946
+ for (const field of VENUE_LIFECYCLE_EVIDENCE_FIELDS) {
274947
+ const value = source?.[field];
274948
+ if (value !== undefined && value !== null) {
274949
+ evidence.push([scope, field, fingerprintEvidenceValue(value)]);
274950
+ }
274951
+ }
274952
+ }
274953
+ return evidence;
274954
+ }
274955
+ function observationFingerprint(observation) {
274956
+ const { normalized, transaction } = observation;
274957
+ return JSON.stringify({
274958
+ status: normalized.status ?? null,
274959
+ txid: normalized.txid ?? null,
274960
+ amount: normalized.amount ?? null,
274961
+ feeAmount: normalized.feeAmount ?? null,
274962
+ feeCurrency: normalized.feeCurrency ?? null,
274963
+ address: normalized.address ?? null,
274964
+ network: normalized.network ?? null,
274965
+ exchangeTimestamp: normalized.exchangeTimestamp ?? null,
274966
+ venueLifecycleEvidence: venueLifecycleEvidence(transaction)
274967
+ });
274968
+ }
274969
+
274970
+ class WithdrawalObservationTracker {
274971
+ #fingerprints = new Map;
274972
+ #maxEntries;
274973
+ #missingIdSequence = 0n;
274974
+ constructor(options) {
274975
+ const maxEntries = options?.maxEntries ?? DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
274976
+ this.#maxEntries = Number.isFinite(maxEntries) ? Math.max(1, Math.floor(maxEntries)) : DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
274977
+ }
274978
+ shouldArchive(observation) {
274979
+ const externalId = observation.normalized.externalId?.trim();
274980
+ const identity = JSON.stringify([
274981
+ observation.exchange.trim().toLowerCase() || "unknown",
274982
+ observation.accountSelector?.trim() || "unknown",
274983
+ observation.assetSymbol?.trim().toUpperCase() || "unknown",
274984
+ externalId || `missing:${++this.#missingIdSequence}`
274985
+ ]);
274986
+ const fingerprint = observationFingerprint(observation);
274987
+ if (this.#fingerprints.get(identity) === fingerprint) {
274988
+ return false;
274989
+ }
274990
+ this.#fingerprints.delete(identity);
274991
+ this.#fingerprints.set(identity, fingerprint);
274992
+ while (this.#fingerprints.size > this.#maxEntries) {
274993
+ const oldest = this.#fingerprints.keys().next().value;
274994
+ if (oldest === undefined)
274995
+ break;
274996
+ this.#fingerprints.delete(oldest);
274997
+ }
274998
+ return true;
274999
+ }
275000
+ getSize() {
275001
+ return this.#fingerprints.size;
275002
+ }
275003
+ }
274858
275004
  // src/helpers/broker-execution-archive/writer.ts
274859
275005
  import { request as httpRequest2 } from "http";
274860
275006
  import { request as httpsRequest2 } from "https";
@@ -294640,6 +294786,13 @@ async function handleTreasuryCall(ctx) {
294640
294786
  }, null);
294641
294787
  }
294642
294788
  const result = await fn.apply(broker, argsArray);
294789
+ if (callValue.functionName === "fetchWithdrawals") {
294790
+ archiveWithdrawalObservationsInBackground(ctx.brokerArchiver, ctx.withdrawalObservationTracker, {
294791
+ exchange: ctx.normalizedCex,
294792
+ accountSelector: ctx.selectedBrokerAccount?.label,
294793
+ transactions: result
294794
+ });
294795
+ }
294643
294796
  ctx.wrappedCallback(null, {
294644
294797
  proof: ctx.verity.proof,
294645
294798
  result: JSON.stringify(result)
@@ -294818,6 +294971,7 @@ function createExecuteActionHandler(deps) {
294818
294971
  brokerArchiver,
294819
294972
  orderActivityTracker
294820
294973
  } = deps;
294974
+ const withdrawalObservationTracker = deps.withdrawalObservationTracker ?? new WithdrawalObservationTracker;
294821
294975
  return async (call, callback) => {
294822
294976
  const startTime = Date.now();
294823
294977
  const { action: rawAction, cex: cex3, symbol: symbol2 } = call.request;
@@ -294896,7 +295050,8 @@ function createExecuteActionHandler(deps) {
294896
295050
  verityProverUrl,
294897
295051
  otelMetrics,
294898
295052
  brokerArchiver,
294899
- orderActivityTracker
295053
+ orderActivityTracker,
295054
+ withdrawalObservationTracker
294900
295055
  };
294901
295056
  if (action === Action.Call) {
294902
295057
  const handled = await handleOrderBookCall(preludeCtx);
@@ -296486,7 +296641,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
296486
296641
  // src/server.ts
296487
296642
  var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
296488
296643
  var cexNode = grpcObj.cex_broker;
296489
- function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
296644
+ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker, withdrawalObservationTracker) {
296490
296645
  const server = new grpc14.Server;
296491
296646
  server.addService(cexNode.cex_service.service, {
296492
296647
  ExecuteAction: createExecuteActionHandler({
@@ -296497,7 +296652,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
296497
296652
  verityProverUrl,
296498
296653
  otelMetrics,
296499
296654
  brokerArchiver,
296500
- orderActivityTracker
296655
+ orderActivityTracker,
296656
+ withdrawalObservationTracker
296501
296657
  }),
296502
296658
  Subscribe: createSubscribeHandler({
296503
296659
  brokers,
@@ -296643,6 +296799,7 @@ class CEXBroker {
296643
296799
  brokerArchiver;
296644
296800
  depositReconciler;
296645
296801
  orderActivityTracker = new OrderActivityTracker;
296802
+ withdrawalObservationTracker = new WithdrawalObservationTracker;
296646
296803
  fillArchivePoller;
296647
296804
  loadEnvConfig() {
296648
296805
  log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
@@ -296817,7 +296974,7 @@ class CEXBroker {
296817
296974
  if (this.otelMetrics?.isOtelEnabled()) {
296818
296975
  await this.otelMetrics.initialize();
296819
296976
  }
296820
- this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
296977
+ this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker, this.withdrawalObservationTracker);
296821
296978
  this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
296822
296979
  if (err2) {
296823
296980
  log.error(err2);
@@ -296848,4 +297005,4 @@ export {
296848
297005
  CEXBroker as default
296849
297006
  };
296850
297007
 
296851
- //# debugId=6EFE98438C50664564756E2164756E21
297008
+ //# debugId=61BAB4069A0158B064756E2164756E21