@usherlabs/cex-broker 0.2.23 → 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.
- package/dist/commands/cli.js +161 -4
- package/dist/handlers/execute-action/context.d.ts +2 -1
- package/dist/handlers/execute-action/handler.d.ts +2 -1
- package/dist/helpers/broker-execution-archive/capture.d.ts +6 -0
- package/dist/helpers/broker-execution-archive/index.d.ts +3 -2
- package/dist/helpers/broker-execution-archive/types.d.ts +1 -1
- package/dist/helpers/broker-execution-archive/withdrawal-observation-tracker.d.ts +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +162 -5
- package/dist/index.js.map +11 -10
- package/dist/server.d.ts +2 -2
- package/package.json +1 -1
package/dist/commands/cli.js
CHANGED
|
@@ -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
|
|
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
|
|
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=
|
|
297008
|
+
//# debugId=61BAB4069A0158B064756E2164756E21
|