@usherlabs/cex-broker 0.2.27 → 0.2.28
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/README.md +2 -2
- package/dist/commands/cli.js +671 -543
- package/dist/helpers/broker-execution-archive/capture.d.ts +1 -1
- package/dist/helpers/broker-execution-archive/index.d.ts +1 -1
- package/dist/helpers/broker-execution-archive/rows.d.ts +1 -0
- package/dist/helpers/broker-execution-archive/writer.d.ts +10 -2
- package/dist/helpers/market-data-archive/capture.d.ts +1 -1
- package/dist/helpers/market-data-archive/index.d.ts +3 -3
- package/dist/helpers/market-data-archive/ohlcv-history.d.ts +1 -1
- package/dist/helpers/market-data-archive/rows.d.ts +1 -1
- package/dist/helpers/market-data-archive/types.d.ts +1 -1
- package/dist/helpers/shared/errors.d.ts +7 -5
- package/dist/index.js +691 -561
- package/dist/index.js.map +17 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -274246,6 +274246,65 @@ function validateDeposit(policy, exchange, network, ticker) {
|
|
|
274246
274246
|
return { valid: true };
|
|
274247
274247
|
}
|
|
274248
274248
|
|
|
274249
|
+
// src/helpers/shared/errors.ts
|
|
274250
|
+
var MAX_ERROR_DETAIL_LENGTH = 512;
|
|
274251
|
+
var REDACTED_ERROR_MESSAGE = "redacted_error";
|
|
274252
|
+
function getErrorMessage(error) {
|
|
274253
|
+
return error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
|
|
274254
|
+
}
|
|
274255
|
+
function errorClassName(error) {
|
|
274256
|
+
if (!(error instanceof Error)) {
|
|
274257
|
+
return;
|
|
274258
|
+
}
|
|
274259
|
+
const name = error.constructor?.name || error.name;
|
|
274260
|
+
return name && name !== "Error" ? name : undefined;
|
|
274261
|
+
}
|
|
274262
|
+
function errorCode(error) {
|
|
274263
|
+
if (error === null || typeof error !== "object") {
|
|
274264
|
+
return;
|
|
274265
|
+
}
|
|
274266
|
+
const code = error.code;
|
|
274267
|
+
if (typeof code === "string") {
|
|
274268
|
+
return code.trim() || undefined;
|
|
274269
|
+
}
|
|
274270
|
+
if (typeof code === "number" && Number.isFinite(code)) {
|
|
274271
|
+
return String(code);
|
|
274272
|
+
}
|
|
274273
|
+
if (typeof code === "bigint") {
|
|
274274
|
+
return String(code);
|
|
274275
|
+
}
|
|
274276
|
+
return;
|
|
274277
|
+
}
|
|
274278
|
+
function sanitizeErrorDetail(error, options = {}) {
|
|
274279
|
+
const className = errorClassName(error);
|
|
274280
|
+
const message = getErrorMessage(error);
|
|
274281
|
+
const code = options.includeCode ? errorCode(error) : undefined;
|
|
274282
|
+
const prefix = [className, code ? `[code=${code}]` : undefined].filter((part) => Boolean(part)).join(" ");
|
|
274283
|
+
const detail = prefix ? `${prefix}: ${message}` : message;
|
|
274284
|
+
return detail.replace(/\s+/g, " ").trim().slice(0, MAX_ERROR_DETAIL_LENGTH);
|
|
274285
|
+
}
|
|
274286
|
+
function safeLogError(context2, error) {
|
|
274287
|
+
try {
|
|
274288
|
+
log.error(context2, { error });
|
|
274289
|
+
} catch {
|
|
274290
|
+
console.error(context2, error);
|
|
274291
|
+
}
|
|
274292
|
+
}
|
|
274293
|
+
function safeLogRedactedError(context2, error) {
|
|
274294
|
+
const errorType = errorClassName(error) ?? (error instanceof Error ? error.name : typeof error);
|
|
274295
|
+
try {
|
|
274296
|
+
log.error(context2, {
|
|
274297
|
+
error_type: errorType,
|
|
274298
|
+
error_message: REDACTED_ERROR_MESSAGE
|
|
274299
|
+
});
|
|
274300
|
+
} catch {
|
|
274301
|
+
console.error(context2, {
|
|
274302
|
+
error_type: errorType,
|
|
274303
|
+
error_message: REDACTED_ERROR_MESSAGE
|
|
274304
|
+
});
|
|
274305
|
+
}
|
|
274306
|
+
}
|
|
274307
|
+
|
|
274249
274308
|
// src/helpers/shared/guards.ts
|
|
274250
274309
|
function isRecord(value) {
|
|
274251
274310
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
@@ -274266,7 +274325,6 @@ var NUMERIC_METRICS = [
|
|
|
274266
274325
|
["feeAmount", "cex_market_action_fee_amount"],
|
|
274267
274326
|
["feeRate", "cex_market_action_fee_rate"]
|
|
274268
274327
|
];
|
|
274269
|
-
var REDACTED_ERROR_MESSAGE = "redacted_error";
|
|
274270
274328
|
async function emitOrderExecutionTelemetry(otelMetrics, context2, order, error) {
|
|
274271
274329
|
try {
|
|
274272
274330
|
const telemetry = buildOrderExecutionTelemetry(context2, order, error);
|
|
@@ -274758,7 +274816,7 @@ function buildOrderEventArchiveRow(input) {
|
|
|
274758
274816
|
fee_rate: telemetry.feeRate,
|
|
274759
274817
|
exchange_timestamp: telemetry.exchangeTimestamp,
|
|
274760
274818
|
error_type: telemetry.errorType,
|
|
274761
|
-
error_message: telemetry.errorMessage,
|
|
274819
|
+
error_message: input.errorDetail ?? telemetry.errorMessage,
|
|
274762
274820
|
payload_json: JSON.stringify(telemetry)
|
|
274763
274821
|
})
|
|
274764
274822
|
};
|
|
@@ -274890,386 +274948,126 @@ function buildMarketMetadataSnapshotRow(input) {
|
|
|
274890
274948
|
};
|
|
274891
274949
|
}
|
|
274892
274950
|
|
|
274893
|
-
// src/helpers/broker-execution-archive/
|
|
274894
|
-
|
|
274895
|
-
|
|
274896
|
-
|
|
274951
|
+
// src/helpers/broker-execution-archive/writer.ts
|
|
274952
|
+
import { closeSync, fsyncSync, openSync, writeSync } from "fs";
|
|
274953
|
+
import { request as httpRequest2 } from "http";
|
|
274954
|
+
import { request as httpsRequest2 } from "https";
|
|
274955
|
+
|
|
274956
|
+
// node_modules/@opentelemetry/api-logs/build/esm/types/LogRecord.js
|
|
274957
|
+
var SeverityNumber2;
|
|
274958
|
+
(function(SeverityNumber3) {
|
|
274959
|
+
SeverityNumber3[SeverityNumber3["UNSPECIFIED"] = 0] = "UNSPECIFIED";
|
|
274960
|
+
SeverityNumber3[SeverityNumber3["TRACE"] = 1] = "TRACE";
|
|
274961
|
+
SeverityNumber3[SeverityNumber3["TRACE2"] = 2] = "TRACE2";
|
|
274962
|
+
SeverityNumber3[SeverityNumber3["TRACE3"] = 3] = "TRACE3";
|
|
274963
|
+
SeverityNumber3[SeverityNumber3["TRACE4"] = 4] = "TRACE4";
|
|
274964
|
+
SeverityNumber3[SeverityNumber3["DEBUG"] = 5] = "DEBUG";
|
|
274965
|
+
SeverityNumber3[SeverityNumber3["DEBUG2"] = 6] = "DEBUG2";
|
|
274966
|
+
SeverityNumber3[SeverityNumber3["DEBUG3"] = 7] = "DEBUG3";
|
|
274967
|
+
SeverityNumber3[SeverityNumber3["DEBUG4"] = 8] = "DEBUG4";
|
|
274968
|
+
SeverityNumber3[SeverityNumber3["INFO"] = 9] = "INFO";
|
|
274969
|
+
SeverityNumber3[SeverityNumber3["INFO2"] = 10] = "INFO2";
|
|
274970
|
+
SeverityNumber3[SeverityNumber3["INFO3"] = 11] = "INFO3";
|
|
274971
|
+
SeverityNumber3[SeverityNumber3["INFO4"] = 12] = "INFO4";
|
|
274972
|
+
SeverityNumber3[SeverityNumber3["WARN"] = 13] = "WARN";
|
|
274973
|
+
SeverityNumber3[SeverityNumber3["WARN2"] = 14] = "WARN2";
|
|
274974
|
+
SeverityNumber3[SeverityNumber3["WARN3"] = 15] = "WARN3";
|
|
274975
|
+
SeverityNumber3[SeverityNumber3["WARN4"] = 16] = "WARN4";
|
|
274976
|
+
SeverityNumber3[SeverityNumber3["ERROR"] = 17] = "ERROR";
|
|
274977
|
+
SeverityNumber3[SeverityNumber3["ERROR2"] = 18] = "ERROR2";
|
|
274978
|
+
SeverityNumber3[SeverityNumber3["ERROR3"] = 19] = "ERROR3";
|
|
274979
|
+
SeverityNumber3[SeverityNumber3["ERROR4"] = 20] = "ERROR4";
|
|
274980
|
+
SeverityNumber3[SeverityNumber3["FATAL"] = 21] = "FATAL";
|
|
274981
|
+
SeverityNumber3[SeverityNumber3["FATAL2"] = 22] = "FATAL2";
|
|
274982
|
+
SeverityNumber3[SeverityNumber3["FATAL3"] = 23] = "FATAL3";
|
|
274983
|
+
SeverityNumber3[SeverityNumber3["FATAL4"] = 24] = "FATAL4";
|
|
274984
|
+
})(SeverityNumber2 || (SeverityNumber2 = {}));
|
|
274985
|
+
// node_modules/@opentelemetry/api-logs/build/esm/NoopLogger.js
|
|
274986
|
+
class NoopLogger2 {
|
|
274987
|
+
emit(_logRecord) {}
|
|
274988
|
+
}
|
|
274989
|
+
var NOOP_LOGGER2 = new NoopLogger2;
|
|
274990
|
+
// node_modules/@opentelemetry/api-logs/build/esm/internal/global-utils.js
|
|
274991
|
+
var GLOBAL_LOGS_API_KEY2 = Symbol.for("io.opentelemetry.js.api.logs");
|
|
274992
|
+
var _global3 = globalThis;
|
|
274993
|
+
function makeGetter2(requiredVersion, instance, fallback) {
|
|
274994
|
+
return (version3) => version3 === requiredVersion ? instance : fallback;
|
|
274995
|
+
}
|
|
274996
|
+
var API_BACKWARDS_COMPATIBILITY_VERSION2 = 1;
|
|
274997
|
+
|
|
274998
|
+
// node_modules/@opentelemetry/api-logs/build/esm/NoopLoggerProvider.js
|
|
274999
|
+
class NoopLoggerProvider2 {
|
|
275000
|
+
getLogger(_name, _version, _options) {
|
|
275001
|
+
return new NoopLogger2;
|
|
274897
275002
|
}
|
|
274898
|
-
queueMicrotask(() => {
|
|
274899
|
-
try {
|
|
274900
|
-
const telemetry = buildOrderExecutionTelemetry(context2, order, error);
|
|
274901
|
-
const tags = buildCommonArchiveTags({
|
|
274902
|
-
deploymentId: archiver.getDeploymentId(),
|
|
274903
|
-
accountSelector: context2.accountLabel,
|
|
274904
|
-
exchange: context2.cex,
|
|
274905
|
-
symbol: telemetry.symbol,
|
|
274906
|
-
brokerObservedTimestamp: telemetry.brokerObservedTimestamp
|
|
274907
|
-
});
|
|
274908
|
-
archiver.enqueue(buildOrderEventArchiveRow({
|
|
274909
|
-
tags,
|
|
274910
|
-
action: context2.action,
|
|
274911
|
-
telemetry,
|
|
274912
|
-
marketMetadataHash: options?.marketMetadataHash
|
|
274913
|
-
}));
|
|
274914
|
-
} catch (archiveError) {
|
|
274915
|
-
log.warn("Failed to archive order execution", { error: archiveError });
|
|
274916
|
-
}
|
|
274917
|
-
});
|
|
274918
275003
|
}
|
|
274919
|
-
|
|
274920
|
-
|
|
274921
|
-
|
|
275004
|
+
var NOOP_LOGGER_PROVIDER2 = new NoopLoggerProvider2;
|
|
275005
|
+
|
|
275006
|
+
// node_modules/@opentelemetry/api-logs/build/esm/ProxyLogger.js
|
|
275007
|
+
class ProxyLogger2 {
|
|
275008
|
+
constructor(provider, name, version3, options) {
|
|
275009
|
+
this._provider = provider;
|
|
275010
|
+
this.name = name;
|
|
275011
|
+
this.version = version3;
|
|
275012
|
+
this.options = options;
|
|
274922
275013
|
}
|
|
274923
|
-
|
|
274924
|
-
|
|
274925
|
-
|
|
274926
|
-
|
|
274927
|
-
|
|
274928
|
-
|
|
274929
|
-
symbol: input.symbol
|
|
274930
|
-
});
|
|
274931
|
-
archiver.enqueue(buildSubscribeStreamArchiveRow({
|
|
274932
|
-
tags,
|
|
274933
|
-
subscriptionType: input.subscriptionType,
|
|
274934
|
-
streamPayload: input.streamPayload,
|
|
274935
|
-
secretLiterals: input.secretLiterals
|
|
274936
|
-
}));
|
|
274937
|
-
} catch (archiveError) {
|
|
274938
|
-
log.warn("Failed to archive subscribe stream event", {
|
|
274939
|
-
error: archiveError
|
|
274940
|
-
});
|
|
275014
|
+
emit(logRecord) {
|
|
275015
|
+
this._getLogger().emit(logRecord);
|
|
275016
|
+
}
|
|
275017
|
+
_getLogger() {
|
|
275018
|
+
if (this._delegate) {
|
|
275019
|
+
return this._delegate;
|
|
274941
275020
|
}
|
|
274942
|
-
|
|
275021
|
+
const logger = this._provider._getDelegateLogger(this.name, this.version, this.options);
|
|
275022
|
+
if (!logger) {
|
|
275023
|
+
return NOOP_LOGGER2;
|
|
275024
|
+
}
|
|
275025
|
+
this._delegate = logger;
|
|
275026
|
+
return this._delegate;
|
|
275027
|
+
}
|
|
274943
275028
|
}
|
|
274944
|
-
|
|
274945
|
-
|
|
274946
|
-
|
|
275029
|
+
|
|
275030
|
+
// node_modules/@opentelemetry/api-logs/build/esm/ProxyLoggerProvider.js
|
|
275031
|
+
class ProxyLoggerProvider2 {
|
|
275032
|
+
getLogger(name, version3, options) {
|
|
275033
|
+
var _a3;
|
|
275034
|
+
return (_a3 = this._getDelegateLogger(name, version3, options)) !== null && _a3 !== undefined ? _a3 : new ProxyLogger2(this, name, version3, options);
|
|
275035
|
+
}
|
|
275036
|
+
_getDelegate() {
|
|
275037
|
+
var _a3;
|
|
275038
|
+
return (_a3 = this._delegate) !== null && _a3 !== undefined ? _a3 : NOOP_LOGGER_PROVIDER2;
|
|
275039
|
+
}
|
|
275040
|
+
_setDelegate(delegate) {
|
|
275041
|
+
this._delegate = delegate;
|
|
275042
|
+
}
|
|
275043
|
+
_getDelegateLogger(name, version3, options) {
|
|
275044
|
+
var _a3;
|
|
275045
|
+
return (_a3 = this._delegate) === null || _a3 === undefined ? undefined : _a3.getLogger(name, version3, options);
|
|
274947
275046
|
}
|
|
274948
|
-
queueMicrotask(() => {
|
|
274949
|
-
try {
|
|
274950
|
-
const tags = buildCommonArchiveTags({
|
|
274951
|
-
deploymentId: archiver.getDeploymentId(),
|
|
274952
|
-
accountSelector: input.accountSelector,
|
|
274953
|
-
exchange: input.exchange,
|
|
274954
|
-
symbol: input.assetSymbol,
|
|
274955
|
-
brokerObservedTimestamp: input.brokerObservedTimestamp
|
|
274956
|
-
});
|
|
274957
|
-
archiver.enqueue(buildTransferEventArchiveRow({ tags, transfer: input.transfer }));
|
|
274958
|
-
} catch (archiveError) {
|
|
274959
|
-
log.warn("Failed to archive transfer event", { error: archiveError });
|
|
274960
|
-
}
|
|
274961
|
-
});
|
|
274962
275047
|
}
|
|
274963
|
-
|
|
274964
|
-
|
|
274965
|
-
|
|
274966
|
-
|
|
275048
|
+
|
|
275049
|
+
// node_modules/@opentelemetry/api-logs/build/esm/api/logs.js
|
|
275050
|
+
class LogsAPI2 {
|
|
275051
|
+
constructor() {
|
|
275052
|
+
this._proxyLoggerProvider = new ProxyLoggerProvider2;
|
|
275053
|
+
}
|
|
275054
|
+
static getInstance() {
|
|
275055
|
+
if (!this._instance) {
|
|
275056
|
+
this._instance = new LogsAPI2;
|
|
274967
275057
|
}
|
|
274968
|
-
|
|
274969
|
-
|
|
274970
|
-
|
|
274971
|
-
|
|
274972
|
-
|
|
274973
|
-
exchange: input.exchange,
|
|
274974
|
-
accountSelector: input.accountSelector,
|
|
274975
|
-
assetSymbol,
|
|
274976
|
-
transaction,
|
|
274977
|
-
normalized
|
|
274978
|
-
})) {
|
|
274979
|
-
continue;
|
|
274980
|
-
}
|
|
274981
|
-
archiveTransferEventInBackground(archiver, {
|
|
274982
|
-
exchange: input.exchange,
|
|
274983
|
-
accountSelector: input.accountSelector,
|
|
274984
|
-
assetSymbol,
|
|
274985
|
-
brokerObservedTimestamp,
|
|
274986
|
-
transfer: {
|
|
274987
|
-
eventKind: "withdrawal",
|
|
274988
|
-
lifecycleAction: "observe_withdrawal",
|
|
274989
|
-
status: normalized.status,
|
|
274990
|
-
amount: normalized.amount,
|
|
274991
|
-
address: normalized.address,
|
|
274992
|
-
network: normalized.network,
|
|
274993
|
-
externalId: normalized.externalId,
|
|
274994
|
-
txid: normalized.txid,
|
|
274995
|
-
resultIndex,
|
|
274996
|
-
feeAmount: normalized.feeAmount,
|
|
274997
|
-
feeCurrency: normalized.feeCurrency,
|
|
274998
|
-
exchangeTimestamp: normalized.exchangeTimestamp,
|
|
274999
|
-
payload: transaction
|
|
275000
|
-
}
|
|
275001
|
-
});
|
|
275058
|
+
return this._instance;
|
|
275059
|
+
}
|
|
275060
|
+
setGlobalLoggerProvider(provider) {
|
|
275061
|
+
if (_global3[GLOBAL_LOGS_API_KEY2]) {
|
|
275062
|
+
return this.getLoggerProvider();
|
|
275002
275063
|
}
|
|
275003
|
-
|
|
275004
|
-
|
|
275005
|
-
|
|
275006
|
-
});
|
|
275064
|
+
_global3[GLOBAL_LOGS_API_KEY2] = makeGetter2(API_BACKWARDS_COMPATIBILITY_VERSION2, provider, NOOP_LOGGER_PROVIDER2);
|
|
275065
|
+
this._proxyLoggerProvider._setDelegate(provider);
|
|
275066
|
+
return provider;
|
|
275007
275067
|
}
|
|
275008
|
-
|
|
275009
|
-
|
|
275010
|
-
|
|
275011
|
-
return;
|
|
275012
|
-
}
|
|
275013
|
-
try {
|
|
275014
|
-
const fetchOrderBook = broker.fetchOrderBook;
|
|
275015
|
-
if (typeof fetchOrderBook !== "function") {
|
|
275016
|
-
return;
|
|
275017
|
-
}
|
|
275018
|
-
const orderBook = await fetchOrderBook.call(broker, input.symbol, 5);
|
|
275019
|
-
const record = asRecord(orderBook);
|
|
275020
|
-
const snapshot = {
|
|
275021
|
-
action: input.action,
|
|
275022
|
-
symbol: input.symbol,
|
|
275023
|
-
bids: record?.bids,
|
|
275024
|
-
asks: record?.asks,
|
|
275025
|
-
timestamp: record?.timestamp ?? Date.now(),
|
|
275026
|
-
datetime: record?.datetime,
|
|
275027
|
-
nonce: record?.nonce
|
|
275028
|
-
};
|
|
275029
|
-
const tags = buildCommonArchiveTags({
|
|
275030
|
-
deploymentId: archiver.getDeploymentId(),
|
|
275031
|
-
accountSelector: input.accountSelector,
|
|
275032
|
-
exchange: input.exchange,
|
|
275033
|
-
symbol: input.symbol,
|
|
275034
|
-
brokerObservedTimestamp: input.brokerObservedTimestamp
|
|
275035
|
-
});
|
|
275036
|
-
const row = buildMarketMetadataSnapshotRow({
|
|
275037
|
-
tags,
|
|
275038
|
-
clientOrderId: input.clientOrderId,
|
|
275039
|
-
orderId: input.orderId,
|
|
275040
|
-
makerActionId: input.makerActionId,
|
|
275041
|
-
idempotencyId: input.idempotencyId,
|
|
275042
|
-
marketSnapshot: snapshot
|
|
275043
|
-
});
|
|
275044
|
-
archiver.enqueue(row);
|
|
275045
|
-
const hash4 = row.row.market_metadata_hash;
|
|
275046
|
-
return typeof hash4 === "string" ? hash4 : undefined;
|
|
275047
|
-
} catch (archiveError) {
|
|
275048
|
-
log.warn("Failed to capture market metadata snapshot", {
|
|
275049
|
-
error: archiveError
|
|
275050
|
-
});
|
|
275051
|
-
return;
|
|
275052
|
-
}
|
|
275053
|
-
}
|
|
275054
|
-
// src/helpers/broker-execution-archive/withdrawal-observation-tracker.ts
|
|
275055
|
-
var DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES = 1e4;
|
|
275056
|
-
var VENUE_LIFECYCLE_EVIDENCE_FIELDS = [
|
|
275057
|
-
"updated",
|
|
275058
|
-
"updatedAt",
|
|
275059
|
-
"updated_at",
|
|
275060
|
-
"updateTime",
|
|
275061
|
-
"update_time",
|
|
275062
|
-
"updateTimestamp",
|
|
275063
|
-
"lastUpdateTimestamp",
|
|
275064
|
-
"completed",
|
|
275065
|
-
"completedAt",
|
|
275066
|
-
"completed_at",
|
|
275067
|
-
"completeTime",
|
|
275068
|
-
"complete_time",
|
|
275069
|
-
"completionTime",
|
|
275070
|
-
"successTime"
|
|
275071
|
-
];
|
|
275072
|
-
function fingerprintEvidenceValue(value) {
|
|
275073
|
-
if (value === undefined || value === null) {
|
|
275074
|
-
return value;
|
|
275075
|
-
}
|
|
275076
|
-
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
275077
|
-
return value;
|
|
275078
|
-
}
|
|
275079
|
-
if (typeof value === "bigint") {
|
|
275080
|
-
return value.toString();
|
|
275081
|
-
}
|
|
275082
|
-
try {
|
|
275083
|
-
return JSON.stringify(value);
|
|
275084
|
-
} catch {
|
|
275085
|
-
return String(value);
|
|
275086
|
-
}
|
|
275087
|
-
}
|
|
275088
|
-
function venueLifecycleEvidence(transaction) {
|
|
275089
|
-
const record = asRecord(transaction);
|
|
275090
|
-
const info = asRecord(record?.info);
|
|
275091
|
-
const evidence = [];
|
|
275092
|
-
for (const [scope, source] of [
|
|
275093
|
-
["transaction", record],
|
|
275094
|
-
["info", info]
|
|
275095
|
-
]) {
|
|
275096
|
-
for (const field of VENUE_LIFECYCLE_EVIDENCE_FIELDS) {
|
|
275097
|
-
const value = source?.[field];
|
|
275098
|
-
if (value !== undefined && value !== null) {
|
|
275099
|
-
evidence.push([scope, field, fingerprintEvidenceValue(value)]);
|
|
275100
|
-
}
|
|
275101
|
-
}
|
|
275102
|
-
}
|
|
275103
|
-
return evidence;
|
|
275104
|
-
}
|
|
275105
|
-
function observationFingerprint(observation) {
|
|
275106
|
-
const { normalized, transaction } = observation;
|
|
275107
|
-
return JSON.stringify({
|
|
275108
|
-
status: normalized.status ?? null,
|
|
275109
|
-
txid: normalized.txid ?? null,
|
|
275110
|
-
amount: normalized.amount ?? null,
|
|
275111
|
-
feeAmount: normalized.feeAmount ?? null,
|
|
275112
|
-
feeCurrency: normalized.feeCurrency ?? null,
|
|
275113
|
-
address: normalized.address ?? null,
|
|
275114
|
-
network: normalized.network ?? null,
|
|
275115
|
-
exchangeTimestamp: normalized.exchangeTimestamp ?? null,
|
|
275116
|
-
venueLifecycleEvidence: venueLifecycleEvidence(transaction)
|
|
275117
|
-
});
|
|
275118
|
-
}
|
|
275119
|
-
|
|
275120
|
-
class WithdrawalObservationTracker {
|
|
275121
|
-
#fingerprints = new Map;
|
|
275122
|
-
#maxEntries;
|
|
275123
|
-
#missingIdSequence = 0n;
|
|
275124
|
-
constructor(options) {
|
|
275125
|
-
const maxEntries = options?.maxEntries ?? DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
|
|
275126
|
-
this.#maxEntries = Number.isFinite(maxEntries) ? Math.max(1, Math.floor(maxEntries)) : DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
|
|
275127
|
-
}
|
|
275128
|
-
shouldArchive(observation) {
|
|
275129
|
-
const externalId = observation.normalized.externalId?.trim();
|
|
275130
|
-
const identity = JSON.stringify([
|
|
275131
|
-
observation.exchange.trim().toLowerCase() || "unknown",
|
|
275132
|
-
observation.accountSelector?.trim() || "unknown",
|
|
275133
|
-
observation.assetSymbol?.trim().toUpperCase() || "unknown",
|
|
275134
|
-
externalId || `missing:${++this.#missingIdSequence}`
|
|
275135
|
-
]);
|
|
275136
|
-
const fingerprint = observationFingerprint(observation);
|
|
275137
|
-
if (this.#fingerprints.get(identity) === fingerprint) {
|
|
275138
|
-
return false;
|
|
275139
|
-
}
|
|
275140
|
-
this.#fingerprints.delete(identity);
|
|
275141
|
-
this.#fingerprints.set(identity, fingerprint);
|
|
275142
|
-
while (this.#fingerprints.size > this.#maxEntries) {
|
|
275143
|
-
const oldest = this.#fingerprints.keys().next().value;
|
|
275144
|
-
if (oldest === undefined)
|
|
275145
|
-
break;
|
|
275146
|
-
this.#fingerprints.delete(oldest);
|
|
275147
|
-
}
|
|
275148
|
-
return true;
|
|
275149
|
-
}
|
|
275150
|
-
getSize() {
|
|
275151
|
-
return this.#fingerprints.size;
|
|
275152
|
-
}
|
|
275153
|
-
}
|
|
275154
|
-
// src/helpers/broker-execution-archive/writer.ts
|
|
275155
|
-
import { request as httpRequest2 } from "http";
|
|
275156
|
-
import { request as httpsRequest2 } from "https";
|
|
275157
|
-
|
|
275158
|
-
// node_modules/@opentelemetry/api-logs/build/esm/types/LogRecord.js
|
|
275159
|
-
var SeverityNumber2;
|
|
275160
|
-
(function(SeverityNumber3) {
|
|
275161
|
-
SeverityNumber3[SeverityNumber3["UNSPECIFIED"] = 0] = "UNSPECIFIED";
|
|
275162
|
-
SeverityNumber3[SeverityNumber3["TRACE"] = 1] = "TRACE";
|
|
275163
|
-
SeverityNumber3[SeverityNumber3["TRACE2"] = 2] = "TRACE2";
|
|
275164
|
-
SeverityNumber3[SeverityNumber3["TRACE3"] = 3] = "TRACE3";
|
|
275165
|
-
SeverityNumber3[SeverityNumber3["TRACE4"] = 4] = "TRACE4";
|
|
275166
|
-
SeverityNumber3[SeverityNumber3["DEBUG"] = 5] = "DEBUG";
|
|
275167
|
-
SeverityNumber3[SeverityNumber3["DEBUG2"] = 6] = "DEBUG2";
|
|
275168
|
-
SeverityNumber3[SeverityNumber3["DEBUG3"] = 7] = "DEBUG3";
|
|
275169
|
-
SeverityNumber3[SeverityNumber3["DEBUG4"] = 8] = "DEBUG4";
|
|
275170
|
-
SeverityNumber3[SeverityNumber3["INFO"] = 9] = "INFO";
|
|
275171
|
-
SeverityNumber3[SeverityNumber3["INFO2"] = 10] = "INFO2";
|
|
275172
|
-
SeverityNumber3[SeverityNumber3["INFO3"] = 11] = "INFO3";
|
|
275173
|
-
SeverityNumber3[SeverityNumber3["INFO4"] = 12] = "INFO4";
|
|
275174
|
-
SeverityNumber3[SeverityNumber3["WARN"] = 13] = "WARN";
|
|
275175
|
-
SeverityNumber3[SeverityNumber3["WARN2"] = 14] = "WARN2";
|
|
275176
|
-
SeverityNumber3[SeverityNumber3["WARN3"] = 15] = "WARN3";
|
|
275177
|
-
SeverityNumber3[SeverityNumber3["WARN4"] = 16] = "WARN4";
|
|
275178
|
-
SeverityNumber3[SeverityNumber3["ERROR"] = 17] = "ERROR";
|
|
275179
|
-
SeverityNumber3[SeverityNumber3["ERROR2"] = 18] = "ERROR2";
|
|
275180
|
-
SeverityNumber3[SeverityNumber3["ERROR3"] = 19] = "ERROR3";
|
|
275181
|
-
SeverityNumber3[SeverityNumber3["ERROR4"] = 20] = "ERROR4";
|
|
275182
|
-
SeverityNumber3[SeverityNumber3["FATAL"] = 21] = "FATAL";
|
|
275183
|
-
SeverityNumber3[SeverityNumber3["FATAL2"] = 22] = "FATAL2";
|
|
275184
|
-
SeverityNumber3[SeverityNumber3["FATAL3"] = 23] = "FATAL3";
|
|
275185
|
-
SeverityNumber3[SeverityNumber3["FATAL4"] = 24] = "FATAL4";
|
|
275186
|
-
})(SeverityNumber2 || (SeverityNumber2 = {}));
|
|
275187
|
-
// node_modules/@opentelemetry/api-logs/build/esm/NoopLogger.js
|
|
275188
|
-
class NoopLogger2 {
|
|
275189
|
-
emit(_logRecord) {}
|
|
275190
|
-
}
|
|
275191
|
-
var NOOP_LOGGER2 = new NoopLogger2;
|
|
275192
|
-
// node_modules/@opentelemetry/api-logs/build/esm/internal/global-utils.js
|
|
275193
|
-
var GLOBAL_LOGS_API_KEY2 = Symbol.for("io.opentelemetry.js.api.logs");
|
|
275194
|
-
var _global3 = globalThis;
|
|
275195
|
-
function makeGetter2(requiredVersion, instance, fallback) {
|
|
275196
|
-
return (version3) => version3 === requiredVersion ? instance : fallback;
|
|
275197
|
-
}
|
|
275198
|
-
var API_BACKWARDS_COMPATIBILITY_VERSION2 = 1;
|
|
275199
|
-
|
|
275200
|
-
// node_modules/@opentelemetry/api-logs/build/esm/NoopLoggerProvider.js
|
|
275201
|
-
class NoopLoggerProvider2 {
|
|
275202
|
-
getLogger(_name, _version, _options) {
|
|
275203
|
-
return new NoopLogger2;
|
|
275204
|
-
}
|
|
275205
|
-
}
|
|
275206
|
-
var NOOP_LOGGER_PROVIDER2 = new NoopLoggerProvider2;
|
|
275207
|
-
|
|
275208
|
-
// node_modules/@opentelemetry/api-logs/build/esm/ProxyLogger.js
|
|
275209
|
-
class ProxyLogger2 {
|
|
275210
|
-
constructor(provider, name, version3, options) {
|
|
275211
|
-
this._provider = provider;
|
|
275212
|
-
this.name = name;
|
|
275213
|
-
this.version = version3;
|
|
275214
|
-
this.options = options;
|
|
275215
|
-
}
|
|
275216
|
-
emit(logRecord) {
|
|
275217
|
-
this._getLogger().emit(logRecord);
|
|
275218
|
-
}
|
|
275219
|
-
_getLogger() {
|
|
275220
|
-
if (this._delegate) {
|
|
275221
|
-
return this._delegate;
|
|
275222
|
-
}
|
|
275223
|
-
const logger = this._provider._getDelegateLogger(this.name, this.version, this.options);
|
|
275224
|
-
if (!logger) {
|
|
275225
|
-
return NOOP_LOGGER2;
|
|
275226
|
-
}
|
|
275227
|
-
this._delegate = logger;
|
|
275228
|
-
return this._delegate;
|
|
275229
|
-
}
|
|
275230
|
-
}
|
|
275231
|
-
|
|
275232
|
-
// node_modules/@opentelemetry/api-logs/build/esm/ProxyLoggerProvider.js
|
|
275233
|
-
class ProxyLoggerProvider2 {
|
|
275234
|
-
getLogger(name, version3, options) {
|
|
275235
|
-
var _a3;
|
|
275236
|
-
return (_a3 = this._getDelegateLogger(name, version3, options)) !== null && _a3 !== undefined ? _a3 : new ProxyLogger2(this, name, version3, options);
|
|
275237
|
-
}
|
|
275238
|
-
_getDelegate() {
|
|
275239
|
-
var _a3;
|
|
275240
|
-
return (_a3 = this._delegate) !== null && _a3 !== undefined ? _a3 : NOOP_LOGGER_PROVIDER2;
|
|
275241
|
-
}
|
|
275242
|
-
_setDelegate(delegate) {
|
|
275243
|
-
this._delegate = delegate;
|
|
275244
|
-
}
|
|
275245
|
-
_getDelegateLogger(name, version3, options) {
|
|
275246
|
-
var _a3;
|
|
275247
|
-
return (_a3 = this._delegate) === null || _a3 === undefined ? undefined : _a3.getLogger(name, version3, options);
|
|
275248
|
-
}
|
|
275249
|
-
}
|
|
275250
|
-
|
|
275251
|
-
// node_modules/@opentelemetry/api-logs/build/esm/api/logs.js
|
|
275252
|
-
class LogsAPI2 {
|
|
275253
|
-
constructor() {
|
|
275254
|
-
this._proxyLoggerProvider = new ProxyLoggerProvider2;
|
|
275255
|
-
}
|
|
275256
|
-
static getInstance() {
|
|
275257
|
-
if (!this._instance) {
|
|
275258
|
-
this._instance = new LogsAPI2;
|
|
275259
|
-
}
|
|
275260
|
-
return this._instance;
|
|
275261
|
-
}
|
|
275262
|
-
setGlobalLoggerProvider(provider) {
|
|
275263
|
-
if (_global3[GLOBAL_LOGS_API_KEY2]) {
|
|
275264
|
-
return this.getLoggerProvider();
|
|
275265
|
-
}
|
|
275266
|
-
_global3[GLOBAL_LOGS_API_KEY2] = makeGetter2(API_BACKWARDS_COMPATIBILITY_VERSION2, provider, NOOP_LOGGER_PROVIDER2);
|
|
275267
|
-
this._proxyLoggerProvider._setDelegate(provider);
|
|
275268
|
-
return provider;
|
|
275269
|
-
}
|
|
275270
|
-
getLoggerProvider() {
|
|
275271
|
-
var _a3, _b2;
|
|
275272
|
-
return (_b2 = (_a3 = _global3[GLOBAL_LOGS_API_KEY2]) === null || _a3 === undefined ? undefined : _a3.call(_global3, API_BACKWARDS_COMPATIBILITY_VERSION2)) !== null && _b2 !== undefined ? _b2 : this._proxyLoggerProvider;
|
|
275068
|
+
getLoggerProvider() {
|
|
275069
|
+
var _a3, _b2;
|
|
275070
|
+
return (_b2 = (_a3 = _global3[GLOBAL_LOGS_API_KEY2]) === null || _a3 === undefined ? undefined : _a3.call(_global3, API_BACKWARDS_COMPATIBILITY_VERSION2)) !== null && _b2 !== undefined ? _b2 : this._proxyLoggerProvider;
|
|
275273
275071
|
}
|
|
275274
275072
|
getLogger(name, version3, options) {
|
|
275275
275073
|
return this.getLoggerProvider().getLogger(name, version3, options);
|
|
@@ -275283,18 +275081,6 @@ class LogsAPI2 {
|
|
|
275283
275081
|
// node_modules/@opentelemetry/api-logs/build/esm/index.js
|
|
275284
275082
|
var logs2 = LogsAPI2.getInstance();
|
|
275285
275083
|
|
|
275286
|
-
// src/helpers/market-data-archive/types.ts
|
|
275287
|
-
var MARKET_ARCHIVE_TABLES = new Set([
|
|
275288
|
-
"market_data.orderbook_snapshots",
|
|
275289
|
-
"market_data.candles",
|
|
275290
|
-
"market_data.cex_stream_events",
|
|
275291
|
-
"market_data.cex_ticker_events",
|
|
275292
|
-
"market_data.cex_trades"
|
|
275293
|
-
]);
|
|
275294
|
-
function isMarketArchiveTable(table) {
|
|
275295
|
-
return MARKET_ARCHIVE_TABLES.has(table);
|
|
275296
|
-
}
|
|
275297
|
-
|
|
275298
275084
|
// src/helpers/broker-execution-archive/writer.ts
|
|
275299
275085
|
var BROKER_EXECUTION_ARCHIVE_TABLES = new Set([
|
|
275300
275086
|
"broker_execution.order_events",
|
|
@@ -275305,30 +275091,28 @@ var BROKER_EXECUTION_ARCHIVE_TABLES = new Set([
|
|
|
275305
275091
|
function isBrokerExecutionArchiveTable(table) {
|
|
275306
275092
|
return BROKER_EXECUTION_ARCHIVE_TABLES.has(table);
|
|
275307
275093
|
}
|
|
275094
|
+
|
|
275095
|
+
class BrokerExecutionArchiveDurabilityError extends Error {
|
|
275096
|
+
constructor(message, options) {
|
|
275097
|
+
super(message, options);
|
|
275098
|
+
this.name = "BrokerExecutionArchiveDurabilityError";
|
|
275099
|
+
}
|
|
275100
|
+
}
|
|
275101
|
+
function rethrowArchiveDurabilityError(error) {
|
|
275102
|
+
if (error instanceof BrokerExecutionArchiveDurabilityError) {
|
|
275103
|
+
throw error;
|
|
275104
|
+
}
|
|
275105
|
+
}
|
|
275308
275106
|
var DEFAULT_MAX_QUEUE_SIZE = 1e4;
|
|
275309
275107
|
var DEFAULT_BATCH_SIZE = 10;
|
|
275310
275108
|
var DEFAULT_FLUSH_INTERVAL_MS = 1000;
|
|
275311
275109
|
var DEFAULT_FORWARDER_TIMEOUT_MS = 3000;
|
|
275312
275110
|
var SHED_WARN_INTERVAL_MS = 60000;
|
|
275313
|
-
var DEFAULT_ARCHIVE_FORWARDER_PATH = "/archive";
|
|
275314
|
-
var DEFAULT_ARCHIVE_FORWARDER_PORT = 8090;
|
|
275315
275111
|
function isArchiveOtelLogsEnabled() {
|
|
275316
275112
|
return process.env.CEX_BROKER_ARCHIVE_OTEL_LOGS_ENABLED === "true";
|
|
275317
275113
|
}
|
|
275318
275114
|
function resolveArchiveForwarderUrlFromEnv() {
|
|
275319
|
-
|
|
275320
|
-
if (explicit) {
|
|
275321
|
-
return explicit;
|
|
275322
|
-
}
|
|
275323
|
-
const host = process.env.CEX_BROKER_ARCHIVE_FORWARDER_HOST?.trim() || process.env.CEX_BROKER_CLICKHOUSE_HOST?.trim();
|
|
275324
|
-
if (!host) {
|
|
275325
|
-
return;
|
|
275326
|
-
}
|
|
275327
|
-
const protocol = process.env.CEX_BROKER_CLICKHOUSE_PROTOCOL || "http";
|
|
275328
|
-
const port = parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_FORWARDER_PORT, DEFAULT_ARCHIVE_FORWARDER_PORT);
|
|
275329
|
-
const path = process.env.CEX_BROKER_ARCHIVE_FORWARDER_PATH?.trim() || DEFAULT_ARCHIVE_FORWARDER_PATH;
|
|
275330
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
275331
|
-
return `${protocol}://${host}:${port}${normalizedPath}`;
|
|
275115
|
+
return process.env.CEX_BROKER_ARCHIVE_FORWARDER_URL?.trim() || undefined;
|
|
275332
275116
|
}
|
|
275333
275117
|
|
|
275334
275118
|
class BrokerExecutionArchiver {
|
|
@@ -275336,6 +275120,8 @@ class BrokerExecutionArchiver {
|
|
|
275336
275120
|
otelLogs;
|
|
275337
275121
|
otelMetrics;
|
|
275338
275122
|
forwarderUrl;
|
|
275123
|
+
deadLetterPath;
|
|
275124
|
+
deadLetterFd;
|
|
275339
275125
|
maxQueueSize;
|
|
275340
275126
|
batchSize;
|
|
275341
275127
|
flushIntervalMs;
|
|
@@ -275351,7 +275137,6 @@ class BrokerExecutionArchiver {
|
|
|
275351
275137
|
flushInFlight = null;
|
|
275352
275138
|
lastShedWarnAtMs = 0;
|
|
275353
275139
|
closed = false;
|
|
275354
|
-
loggedMissingMarketForwarder = false;
|
|
275355
275140
|
enabled;
|
|
275356
275141
|
forwarderAuthToken;
|
|
275357
275142
|
constructor(options) {
|
|
@@ -275359,67 +275144,78 @@ class BrokerExecutionArchiver {
|
|
|
275359
275144
|
this.otelLogs = options.otelLogs;
|
|
275360
275145
|
this.otelMetrics = options.otelMetrics;
|
|
275361
275146
|
this.forwarderUrl = options.forwarderUrl?.trim();
|
|
275147
|
+
this.deadLetterPath = options.deadLetterPath?.trim();
|
|
275362
275148
|
this.maxQueueSize = options.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE;
|
|
275363
275149
|
this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
275364
275150
|
this.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
|
|
275365
275151
|
this.forwarderTimeoutMs = options.forwarderTimeoutMs ?? DEFAULT_FORWARDER_TIMEOUT_MS;
|
|
275366
275152
|
this.enabled = options.enabled;
|
|
275367
275153
|
this.forwarderAuthToken = process.env.CEX_BROKER_ARCHIVE_FORWARDER_TOKEN?.trim() || undefined;
|
|
275368
|
-
if (this.enabled) {
|
|
275154
|
+
if (!this.enabled) {
|
|
275155
|
+
log.info("Broker execution archive disabled", { enabled: false });
|
|
275156
|
+
return;
|
|
275157
|
+
}
|
|
275158
|
+
validateForwarderUrl(this.forwarderUrl);
|
|
275159
|
+
if (!this.deadLetterPath) {
|
|
275160
|
+
throw new Error("Broker execution archive is enabled but CEX_BROKER_ARCHIVE_DEAD_LETTER_PATH is missing");
|
|
275161
|
+
}
|
|
275162
|
+
try {
|
|
275163
|
+
this.deadLetterFd = openSync(this.deadLetterPath, "a", 384);
|
|
275164
|
+
} catch {
|
|
275165
|
+
throw new Error("Broker execution archive cannot open CEX_BROKER_ARCHIVE_DEAD_LETTER_PATH for append");
|
|
275166
|
+
}
|
|
275167
|
+
try {
|
|
275369
275168
|
this.flushTimer = setInterval(() => {
|
|
275370
|
-
this.flush()
|
|
275371
|
-
log.warn("Broker execution archive flush failed", { error });
|
|
275372
|
-
});
|
|
275169
|
+
this.flush();
|
|
275373
275170
|
}, this.flushIntervalMs);
|
|
275374
275171
|
this.flushTimer.unref?.();
|
|
275172
|
+
log.info("Broker execution archive enabled", {
|
|
275173
|
+
enabled: true,
|
|
275174
|
+
otel_mirror_enabled: Boolean(this.otelLogs?.isOtelEnabled())
|
|
275175
|
+
});
|
|
275176
|
+
} catch (error) {
|
|
275177
|
+
this.closeLossJournal();
|
|
275178
|
+
throw error;
|
|
275375
275179
|
}
|
|
275376
275180
|
}
|
|
275377
275181
|
static disabled() {
|
|
275378
275182
|
return new BrokerExecutionArchiver({ enabled: false });
|
|
275379
275183
|
}
|
|
275380
275184
|
static create(options) {
|
|
275381
|
-
|
|
275382
|
-
const enabled = process.env.CEX_BROKER_ARCHIVE_ENABLED !== "false" && hasSink;
|
|
275383
|
-
return new BrokerExecutionArchiver({ ...options, enabled });
|
|
275185
|
+
return new BrokerExecutionArchiver({ ...options, enabled: true });
|
|
275384
275186
|
}
|
|
275385
275187
|
getDeploymentId() {
|
|
275386
275188
|
return this.deploymentId;
|
|
275387
275189
|
}
|
|
275388
275190
|
isEnabled() {
|
|
275389
|
-
return this.enabled && !this.closed
|
|
275191
|
+
return this.enabled && !this.closed;
|
|
275390
275192
|
}
|
|
275391
275193
|
canPersistMarketMetadataSnapshot() {
|
|
275392
|
-
return this.isEnabled()
|
|
275194
|
+
return this.isEnabled();
|
|
275393
275195
|
}
|
|
275394
275196
|
canPersistAccountBalanceSnapshots() {
|
|
275395
275197
|
return this.isEnabled() && Boolean(this.forwarderUrl);
|
|
275396
275198
|
}
|
|
275397
275199
|
enqueue(row) {
|
|
275398
|
-
if (!this.enabled || this.closed
|
|
275399
|
-
return;
|
|
275400
|
-
}
|
|
275401
|
-
if (isMarketArchiveTable(row.table) && !this.forwarderUrl) {
|
|
275402
|
-
if (!this.loggedMissingMarketForwarder) {
|
|
275403
|
-
this.loggedMissingMarketForwarder = true;
|
|
275404
|
-
log.warn("Market data archive row dropped: configure CEX_BROKER_ARCHIVE_FORWARDER_URL or CEX_BROKER_CLICKHOUSE_HOST", { table: row.table });
|
|
275405
|
-
}
|
|
275406
|
-
return;
|
|
275407
|
-
}
|
|
275408
|
-
if (row.table === "broker_account.balance_snapshots" && !this.forwarderUrl) {
|
|
275200
|
+
if (!this.enabled || this.closed) {
|
|
275409
275201
|
return;
|
|
275410
275202
|
}
|
|
275411
275203
|
if (this.queue.length >= this.maxQueueSize) {
|
|
275412
|
-
this.queue
|
|
275204
|
+
const shedRow = this.queue[0];
|
|
275205
|
+
if (shedRow) {
|
|
275206
|
+
this.appendLossRecords([shedRow], "queue_shed");
|
|
275207
|
+
this.queue.shift();
|
|
275208
|
+
}
|
|
275413
275209
|
this.stats.shed += 1;
|
|
275414
275210
|
this.recordArchiveMetric("cex_archive_rows_shed_total", {
|
|
275415
|
-
table:
|
|
275211
|
+
table: shedRow?.table ?? "unknown"
|
|
275416
275212
|
});
|
|
275417
275213
|
const now3 = Date.now();
|
|
275418
275214
|
if (now3 - this.lastShedWarnAtMs >= SHED_WARN_INTERVAL_MS) {
|
|
275419
275215
|
log.warn("Archive queue full: shedding oldest rows", {
|
|
275420
275216
|
shed_total: this.stats.shed,
|
|
275421
275217
|
queue_max: this.maxQueueSize,
|
|
275422
|
-
table:
|
|
275218
|
+
table: shedRow?.table ?? "unknown"
|
|
275423
275219
|
});
|
|
275424
275220
|
this.lastShedWarnAtMs = now3;
|
|
275425
275221
|
}
|
|
@@ -275430,9 +275226,7 @@ class BrokerExecutionArchiver {
|
|
|
275430
275226
|
table: row.table
|
|
275431
275227
|
});
|
|
275432
275228
|
if (this.queue.length >= this.batchSize) {
|
|
275433
|
-
this.flush()
|
|
275434
|
-
log.warn("Broker execution archive flush failed", { error });
|
|
275435
|
-
});
|
|
275229
|
+
this.flush();
|
|
275436
275230
|
}
|
|
275437
275231
|
}
|
|
275438
275232
|
enqueueInBackground(row) {
|
|
@@ -275458,21 +275252,36 @@ class BrokerExecutionArchiver {
|
|
|
275458
275252
|
clearInterval(this.flushTimer);
|
|
275459
275253
|
this.flushTimer = null;
|
|
275460
275254
|
}
|
|
275461
|
-
|
|
275462
|
-
|
|
275463
|
-
|
|
275464
|
-
|
|
275465
|
-
|
|
275466
|
-
|
|
275467
|
-
|
|
275468
|
-
|
|
275469
|
-
const
|
|
275470
|
-
this.queue.length
|
|
275471
|
-
|
|
275472
|
-
|
|
275255
|
+
let closeError;
|
|
275256
|
+
try {
|
|
275257
|
+
while (this.queue.length > 0 || this.flushInFlight) {
|
|
275258
|
+
if (this.flushInFlight) {
|
|
275259
|
+
await this.flushInFlight;
|
|
275260
|
+
continue;
|
|
275261
|
+
}
|
|
275262
|
+
const depthBefore = this.queue.length;
|
|
275263
|
+
const flushed = await this.flushBatch();
|
|
275264
|
+
if (!flushed && this.queue.length >= depthBefore && depthBefore > 0) {
|
|
275265
|
+
const undelivered = [...this.queue];
|
|
275266
|
+
this.appendLossRecords(undelivered, "shutdown_forwarder_failure");
|
|
275267
|
+
this.queue.length = 0;
|
|
275268
|
+
break;
|
|
275269
|
+
}
|
|
275473
275270
|
}
|
|
275271
|
+
} catch (error) {
|
|
275272
|
+
closeError = error;
|
|
275474
275273
|
}
|
|
275475
275274
|
this.closed = true;
|
|
275275
|
+
if (this.deadLetterFd !== undefined) {
|
|
275276
|
+
try {
|
|
275277
|
+
this.closeLossJournal();
|
|
275278
|
+
} catch (error) {
|
|
275279
|
+
closeError ??= error;
|
|
275280
|
+
}
|
|
275281
|
+
}
|
|
275282
|
+
if (closeError) {
|
|
275283
|
+
throw closeError;
|
|
275284
|
+
}
|
|
275476
275285
|
}
|
|
275477
275286
|
getStats() {
|
|
275478
275287
|
return { ...this.stats };
|
|
@@ -275480,15 +275289,60 @@ class BrokerExecutionArchiver {
|
|
|
275480
275289
|
getQueueDepth() {
|
|
275481
275290
|
return this.queue.length;
|
|
275482
275291
|
}
|
|
275292
|
+
closeLossJournal() {
|
|
275293
|
+
if (this.deadLetterFd === undefined) {
|
|
275294
|
+
return;
|
|
275295
|
+
}
|
|
275296
|
+
const fd2 = this.deadLetterFd;
|
|
275297
|
+
try {
|
|
275298
|
+
closeSync(fd2);
|
|
275299
|
+
} catch (error) {
|
|
275300
|
+
throw new BrokerExecutionArchiveDurabilityError("Broker execution archive failed to close the configured CEX_BROKER_ARCHIVE_DEAD_LETTER_PATH loss journal", { cause: error });
|
|
275301
|
+
} finally {
|
|
275302
|
+
this.deadLetterFd = undefined;
|
|
275303
|
+
}
|
|
275304
|
+
}
|
|
275483
275305
|
enforceQueueBound() {
|
|
275484
275306
|
while (this.queue.length > this.maxQueueSize) {
|
|
275485
|
-
const dropped = this.queue
|
|
275307
|
+
const dropped = this.queue[0];
|
|
275308
|
+
if (!dropped) {
|
|
275309
|
+
return;
|
|
275310
|
+
}
|
|
275311
|
+
this.appendLossRecords([dropped], "queue_shed");
|
|
275312
|
+
this.queue.shift();
|
|
275486
275313
|
this.stats.shed += 1;
|
|
275487
275314
|
this.recordArchiveMetric("cex_archive_rows_shed_total", {
|
|
275488
275315
|
table: dropped?.table ?? "unknown"
|
|
275489
275316
|
});
|
|
275490
275317
|
}
|
|
275491
275318
|
}
|
|
275319
|
+
appendLossRecords(rows, reason) {
|
|
275320
|
+
if (rows.length === 0) {
|
|
275321
|
+
return;
|
|
275322
|
+
}
|
|
275323
|
+
if (this.deadLetterFd === undefined) {
|
|
275324
|
+
throw new BrokerExecutionArchiveDurabilityError(`Broker execution archive cannot record ${reason}: dead-letter file is not open`);
|
|
275325
|
+
}
|
|
275326
|
+
const timestamp = new Date().toISOString();
|
|
275327
|
+
const records = rows.map((payload) => ({
|
|
275328
|
+
timestamp,
|
|
275329
|
+
deployment_id: this.deploymentId,
|
|
275330
|
+
reason,
|
|
275331
|
+
payload
|
|
275332
|
+
}));
|
|
275333
|
+
try {
|
|
275334
|
+
const bytes2 = Buffer.from(records.map((record) => JSON.stringify(record)).join(`
|
|
275335
|
+
`) + `
|
|
275336
|
+
`);
|
|
275337
|
+
const written = writeSync(this.deadLetterFd, bytes2);
|
|
275338
|
+
if (written !== bytes2.length) {
|
|
275339
|
+
throw new Error(`wrote ${written} of ${bytes2.length} bytes`);
|
|
275340
|
+
}
|
|
275341
|
+
fsyncSync(this.deadLetterFd);
|
|
275342
|
+
} catch (error) {
|
|
275343
|
+
throw new BrokerExecutionArchiveDurabilityError(`Broker execution archive failed to durably record ${reason}; affected row(s) were retained`, { cause: error });
|
|
275344
|
+
}
|
|
275345
|
+
}
|
|
275492
275346
|
async flushBatch() {
|
|
275493
275347
|
const batch = this.queue.splice(0, this.batchSize);
|
|
275494
275348
|
if (batch.length === 0) {
|
|
@@ -275525,111 +275379,407 @@ class BrokerExecutionArchiver {
|
|
|
275525
275379
|
for (const [table, count2] of countByTable) {
|
|
275526
275380
|
this.recordArchiveMetric("cex_archive_rows_flushed_total", { table }, count2);
|
|
275527
275381
|
}
|
|
275528
|
-
this.recordArchiveGauge("cex_archive_last_flush_success", Math.floor(Date.now() / 1000));
|
|
275529
|
-
}
|
|
275530
|
-
async recordArchiveMetric(metricName, labels, value = 1) {
|
|
275531
|
-
try {
|
|
275532
|
-
await this.otelMetrics?.recordCounter(metricName, value, labels);
|
|
275533
|
-
} catch {}
|
|
275382
|
+
this.recordArchiveGauge("cex_archive_last_flush_success", Math.floor(Date.now() / 1000));
|
|
275383
|
+
}
|
|
275384
|
+
async recordArchiveMetric(metricName, labels, value = 1) {
|
|
275385
|
+
try {
|
|
275386
|
+
await this.otelMetrics?.recordCounter(metricName, value, labels);
|
|
275387
|
+
} catch {}
|
|
275388
|
+
}
|
|
275389
|
+
async recordArchiveGauge(metricName, value) {
|
|
275390
|
+
try {
|
|
275391
|
+
await this.otelMetrics?.recordGauge(metricName, value, {});
|
|
275392
|
+
} catch {}
|
|
275393
|
+
}
|
|
275394
|
+
emitOtelLog(entry) {
|
|
275395
|
+
if (!this.otelLogs?.isOtelEnabled()) {
|
|
275396
|
+
return;
|
|
275397
|
+
}
|
|
275398
|
+
try {
|
|
275399
|
+
this.otelLogs.emit({
|
|
275400
|
+
body: entry.table,
|
|
275401
|
+
severityNumber: SeverityNumber2.INFO,
|
|
275402
|
+
severityText: "INFO",
|
|
275403
|
+
attributes: flattenArchiveAttributes(redactArchiveErrorForOtel(entry))
|
|
275404
|
+
});
|
|
275405
|
+
} catch (error) {
|
|
275406
|
+
log.warn("Broker execution archive OTLP emit failed", { error });
|
|
275407
|
+
}
|
|
275408
|
+
}
|
|
275409
|
+
postToForwarder(batch) {
|
|
275410
|
+
if (!this.forwarderUrl || batch.length === 0) {
|
|
275411
|
+
return Promise.resolve();
|
|
275412
|
+
}
|
|
275413
|
+
const body = JSON.stringify({
|
|
275414
|
+
source: "broker_write",
|
|
275415
|
+
deployment_id: this.deploymentId,
|
|
275416
|
+
rows: batch
|
|
275417
|
+
});
|
|
275418
|
+
const url2 = new URL(this.forwarderUrl);
|
|
275419
|
+
const doRequest = url2.protocol === "http:" ? httpRequest2 : httpsRequest2;
|
|
275420
|
+
const headers = {
|
|
275421
|
+
"content-type": "application/json",
|
|
275422
|
+
"content-length": Buffer.byteLength(body)
|
|
275423
|
+
};
|
|
275424
|
+
if (this.forwarderAuthToken) {
|
|
275425
|
+
headers.authorization = `Bearer ${this.forwarderAuthToken}`;
|
|
275426
|
+
}
|
|
275427
|
+
return new Promise((resolve, reject) => {
|
|
275428
|
+
const req = doRequest(url2, {
|
|
275429
|
+
method: "POST",
|
|
275430
|
+
headers,
|
|
275431
|
+
timeout: this.forwarderTimeoutMs
|
|
275432
|
+
}, (res) => {
|
|
275433
|
+
res.on("data", () => {});
|
|
275434
|
+
res.on("end", () => {
|
|
275435
|
+
const status = res.statusCode ?? 0;
|
|
275436
|
+
if (status < 200 || status >= 300) {
|
|
275437
|
+
reject(new Error(`Archive forwarder returned ${status} ${res.statusMessage ?? ""}`));
|
|
275438
|
+
return;
|
|
275439
|
+
}
|
|
275440
|
+
resolve();
|
|
275441
|
+
});
|
|
275442
|
+
});
|
|
275443
|
+
req.on("error", reject);
|
|
275444
|
+
req.on("timeout", () => {
|
|
275445
|
+
req.destroy(new Error("Archive forwarder request timed out"));
|
|
275446
|
+
});
|
|
275447
|
+
req.write(body);
|
|
275448
|
+
req.end();
|
|
275449
|
+
});
|
|
275450
|
+
}
|
|
275451
|
+
}
|
|
275452
|
+
function redactArchiveErrorForOtel(entry) {
|
|
275453
|
+
if (entry.table !== "broker_execution.order_events" || typeof entry.row.error_message !== "string" || entry.row.error_message.length === 0) {
|
|
275454
|
+
return entry;
|
|
275455
|
+
}
|
|
275456
|
+
return {
|
|
275457
|
+
...entry,
|
|
275458
|
+
row: { ...entry.row, error_message: REDACTED_ERROR_MESSAGE }
|
|
275459
|
+
};
|
|
275460
|
+
}
|
|
275461
|
+
function flattenArchiveAttributes(entry) {
|
|
275462
|
+
const attributes = {
|
|
275463
|
+
ch_table: entry.table
|
|
275464
|
+
};
|
|
275465
|
+
for (const [key, value] of Object.entries(entry.row)) {
|
|
275466
|
+
if (value === undefined || value === null) {
|
|
275467
|
+
continue;
|
|
275468
|
+
}
|
|
275469
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
275470
|
+
attributes[key] = value;
|
|
275471
|
+
} else {
|
|
275472
|
+
attributes[key] = JSON.stringify(value);
|
|
275473
|
+
}
|
|
275474
|
+
}
|
|
275475
|
+
return attributes;
|
|
275476
|
+
}
|
|
275477
|
+
function createBrokerExecutionArchiverFromEnv(otelLogs, otelMetrics) {
|
|
275478
|
+
if (process.env.CEX_BROKER_ARCHIVE_ENABLED !== "true") {
|
|
275479
|
+
return BrokerExecutionArchiver.disabled();
|
|
275480
|
+
}
|
|
275481
|
+
const forwarderUrl = resolveArchiveForwarderUrlFromEnv();
|
|
275482
|
+
const archiveOtelLogs = isArchiveOtelLogsEnabled() ? otelLogs : undefined;
|
|
275483
|
+
return BrokerExecutionArchiver.create({
|
|
275484
|
+
otelLogs: archiveOtelLogs,
|
|
275485
|
+
otelMetrics,
|
|
275486
|
+
forwarderUrl: forwarderUrl ?? "",
|
|
275487
|
+
deadLetterPath: process.env.CEX_BROKER_ARCHIVE_DEAD_LETTER_PATH?.trim() ?? "",
|
|
275488
|
+
deploymentId: process.env.CEX_BROKER_DEPLOYMENT_ID,
|
|
275489
|
+
maxQueueSize: parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_QUEUE_MAX, DEFAULT_MAX_QUEUE_SIZE),
|
|
275490
|
+
batchSize: parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_BATCH_SIZE, DEFAULT_BATCH_SIZE),
|
|
275491
|
+
flushIntervalMs: parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_FLUSH_INTERVAL_MS, DEFAULT_FLUSH_INTERVAL_MS)
|
|
275492
|
+
});
|
|
275493
|
+
}
|
|
275494
|
+
function validateForwarderUrl(value) {
|
|
275495
|
+
if (!value) {
|
|
275496
|
+
throw new Error("Broker execution archive is enabled but CEX_BROKER_ARCHIVE_FORWARDER_URL is missing");
|
|
275497
|
+
}
|
|
275498
|
+
let url2;
|
|
275499
|
+
try {
|
|
275500
|
+
url2 = new URL(value);
|
|
275501
|
+
} catch (error) {
|
|
275502
|
+
throw new Error("Broker execution archive requires a valid CEX_BROKER_ARCHIVE_FORWARDER_URL", { cause: error });
|
|
275503
|
+
}
|
|
275504
|
+
if (url2.protocol !== "http:" && url2.protocol !== "https:") {
|
|
275505
|
+
throw new Error("Broker execution archive forwarder URL must use http or https");
|
|
275506
|
+
}
|
|
275507
|
+
return url2;
|
|
275508
|
+
}
|
|
275509
|
+
function parsePositiveInt(value, fallback) {
|
|
275510
|
+
if (!value) {
|
|
275511
|
+
return fallback;
|
|
275512
|
+
}
|
|
275513
|
+
const parsed = Number.parseInt(value, 10);
|
|
275514
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
275515
|
+
}
|
|
275516
|
+
|
|
275517
|
+
// src/helpers/broker-execution-archive/capture.ts
|
|
275518
|
+
function archiveOrderExecutionInBackground(archiver, context2, order, error, options) {
|
|
275519
|
+
if (!archiver?.isEnabled()) {
|
|
275520
|
+
return;
|
|
275521
|
+
}
|
|
275522
|
+
queueMicrotask(() => {
|
|
275523
|
+
try {
|
|
275524
|
+
const telemetry = buildOrderExecutionTelemetry(context2, order, error);
|
|
275525
|
+
const tags = buildCommonArchiveTags({
|
|
275526
|
+
deploymentId: archiver.getDeploymentId(),
|
|
275527
|
+
accountSelector: context2.accountLabel,
|
|
275528
|
+
exchange: context2.cex,
|
|
275529
|
+
symbol: telemetry.symbol,
|
|
275530
|
+
brokerObservedTimestamp: telemetry.brokerObservedTimestamp
|
|
275531
|
+
});
|
|
275532
|
+
archiver.enqueue(buildOrderEventArchiveRow({
|
|
275533
|
+
tags,
|
|
275534
|
+
action: context2.action,
|
|
275535
|
+
telemetry,
|
|
275536
|
+
errorDetail: context2.action === "CreateOrder" && error !== undefined ? sanitizeErrorDetail(error, { includeCode: true }) : undefined,
|
|
275537
|
+
marketMetadataHash: options?.marketMetadataHash
|
|
275538
|
+
}));
|
|
275539
|
+
} catch (archiveError) {
|
|
275540
|
+
rethrowArchiveDurabilityError(archiveError);
|
|
275541
|
+
log.warn("Failed to archive order execution", { error: archiveError });
|
|
275542
|
+
}
|
|
275543
|
+
});
|
|
275544
|
+
}
|
|
275545
|
+
function archiveSubscribeStreamInBackground(archiver, input) {
|
|
275546
|
+
if (!archiver?.isEnabled()) {
|
|
275547
|
+
return;
|
|
275548
|
+
}
|
|
275549
|
+
queueMicrotask(() => {
|
|
275550
|
+
try {
|
|
275551
|
+
const tags = buildCommonArchiveTags({
|
|
275552
|
+
deploymentId: archiver.getDeploymentId(),
|
|
275553
|
+
accountSelector: input.accountSelector,
|
|
275554
|
+
exchange: input.exchange,
|
|
275555
|
+
symbol: input.symbol
|
|
275556
|
+
});
|
|
275557
|
+
archiver.enqueue(buildSubscribeStreamArchiveRow({
|
|
275558
|
+
tags,
|
|
275559
|
+
subscriptionType: input.subscriptionType,
|
|
275560
|
+
streamPayload: input.streamPayload,
|
|
275561
|
+
secretLiterals: input.secretLiterals
|
|
275562
|
+
}));
|
|
275563
|
+
} catch (archiveError) {
|
|
275564
|
+
rethrowArchiveDurabilityError(archiveError);
|
|
275565
|
+
log.warn("Failed to archive subscribe stream event", {
|
|
275566
|
+
error: archiveError
|
|
275567
|
+
});
|
|
275568
|
+
}
|
|
275569
|
+
});
|
|
275570
|
+
}
|
|
275571
|
+
function archiveTransferEventInBackground(archiver, input) {
|
|
275572
|
+
if (!archiver?.isEnabled()) {
|
|
275573
|
+
return;
|
|
275574
|
+
}
|
|
275575
|
+
queueMicrotask(() => {
|
|
275576
|
+
try {
|
|
275577
|
+
const tags = buildCommonArchiveTags({
|
|
275578
|
+
deploymentId: archiver.getDeploymentId(),
|
|
275579
|
+
accountSelector: input.accountSelector,
|
|
275580
|
+
exchange: input.exchange,
|
|
275581
|
+
symbol: input.assetSymbol,
|
|
275582
|
+
brokerObservedTimestamp: input.brokerObservedTimestamp
|
|
275583
|
+
});
|
|
275584
|
+
archiver.enqueue(buildTransferEventArchiveRow({ tags, transfer: input.transfer }));
|
|
275585
|
+
} catch (archiveError) {
|
|
275586
|
+
rethrowArchiveDurabilityError(archiveError);
|
|
275587
|
+
log.warn("Failed to archive transfer event", { error: archiveError });
|
|
275588
|
+
}
|
|
275589
|
+
});
|
|
275590
|
+
}
|
|
275591
|
+
function archiveWithdrawalObservationsInBackground(archiver, tracker, input) {
|
|
275592
|
+
try {
|
|
275593
|
+
if (!archiver?.isEnabled() || !Array.isArray(input.transactions)) {
|
|
275594
|
+
return;
|
|
275595
|
+
}
|
|
275596
|
+
const brokerObservedTimestamp = new Date().toISOString();
|
|
275597
|
+
for (const [resultIndex, transaction] of input.transactions.entries()) {
|
|
275598
|
+
const normalized = normalizeCcxtTransactionForArchive(transaction);
|
|
275599
|
+
const assetSymbol = normalized.assetSymbol;
|
|
275600
|
+
if (!tracker.shouldArchive({
|
|
275601
|
+
exchange: input.exchange,
|
|
275602
|
+
accountSelector: input.accountSelector,
|
|
275603
|
+
assetSymbol,
|
|
275604
|
+
transaction,
|
|
275605
|
+
normalized
|
|
275606
|
+
})) {
|
|
275607
|
+
continue;
|
|
275608
|
+
}
|
|
275609
|
+
archiveTransferEventInBackground(archiver, {
|
|
275610
|
+
exchange: input.exchange,
|
|
275611
|
+
accountSelector: input.accountSelector,
|
|
275612
|
+
assetSymbol,
|
|
275613
|
+
brokerObservedTimestamp,
|
|
275614
|
+
transfer: {
|
|
275615
|
+
eventKind: "withdrawal",
|
|
275616
|
+
lifecycleAction: "observe_withdrawal",
|
|
275617
|
+
status: normalized.status,
|
|
275618
|
+
amount: normalized.amount,
|
|
275619
|
+
address: normalized.address,
|
|
275620
|
+
network: normalized.network,
|
|
275621
|
+
externalId: normalized.externalId,
|
|
275622
|
+
txid: normalized.txid,
|
|
275623
|
+
resultIndex,
|
|
275624
|
+
feeAmount: normalized.feeAmount,
|
|
275625
|
+
feeCurrency: normalized.feeCurrency,
|
|
275626
|
+
exchangeTimestamp: normalized.exchangeTimestamp,
|
|
275627
|
+
payload: transaction
|
|
275628
|
+
}
|
|
275629
|
+
});
|
|
275630
|
+
}
|
|
275631
|
+
} catch (archiveError) {
|
|
275632
|
+
rethrowArchiveDurabilityError(archiveError);
|
|
275633
|
+
log.warn("Failed to archive withdrawal observations", {
|
|
275634
|
+
error: archiveError
|
|
275635
|
+
});
|
|
275534
275636
|
}
|
|
275535
|
-
|
|
275536
|
-
|
|
275537
|
-
|
|
275538
|
-
|
|
275637
|
+
}
|
|
275638
|
+
async function captureMarketMetadataSnapshot(archiver, broker, input) {
|
|
275639
|
+
if (!archiver?.canPersistMarketMetadataSnapshot()) {
|
|
275640
|
+
return;
|
|
275539
275641
|
}
|
|
275540
|
-
|
|
275541
|
-
|
|
275642
|
+
try {
|
|
275643
|
+
const fetchOrderBook = broker.fetchOrderBook;
|
|
275644
|
+
if (typeof fetchOrderBook !== "function") {
|
|
275542
275645
|
return;
|
|
275543
275646
|
}
|
|
275544
|
-
|
|
275545
|
-
|
|
275546
|
-
|
|
275547
|
-
|
|
275548
|
-
|
|
275549
|
-
|
|
275550
|
-
|
|
275551
|
-
|
|
275552
|
-
|
|
275553
|
-
|
|
275554
|
-
}
|
|
275555
|
-
postToForwarder(batch) {
|
|
275556
|
-
if (!this.forwarderUrl || batch.length === 0) {
|
|
275557
|
-
return Promise.resolve();
|
|
275558
|
-
}
|
|
275559
|
-
const body = JSON.stringify({
|
|
275560
|
-
source: "broker_write",
|
|
275561
|
-
deployment_id: this.deploymentId,
|
|
275562
|
-
rows: batch
|
|
275563
|
-
});
|
|
275564
|
-
const url2 = new URL(this.forwarderUrl);
|
|
275565
|
-
const doRequest = url2.protocol === "http:" ? httpRequest2 : httpsRequest2;
|
|
275566
|
-
const headers = {
|
|
275567
|
-
"content-type": "application/json",
|
|
275568
|
-
"content-length": Buffer.byteLength(body)
|
|
275647
|
+
const orderBook = await fetchOrderBook.call(broker, input.symbol, 5);
|
|
275648
|
+
const record = asRecord(orderBook);
|
|
275649
|
+
const snapshot = {
|
|
275650
|
+
action: input.action,
|
|
275651
|
+
symbol: input.symbol,
|
|
275652
|
+
bids: record?.bids,
|
|
275653
|
+
asks: record?.asks,
|
|
275654
|
+
timestamp: record?.timestamp ?? Date.now(),
|
|
275655
|
+
datetime: record?.datetime,
|
|
275656
|
+
nonce: record?.nonce
|
|
275569
275657
|
};
|
|
275570
|
-
|
|
275571
|
-
|
|
275572
|
-
|
|
275573
|
-
|
|
275574
|
-
|
|
275575
|
-
|
|
275576
|
-
|
|
275577
|
-
|
|
275578
|
-
|
|
275579
|
-
|
|
275580
|
-
|
|
275581
|
-
|
|
275582
|
-
|
|
275583
|
-
|
|
275584
|
-
|
|
275585
|
-
|
|
275586
|
-
|
|
275587
|
-
|
|
275588
|
-
|
|
275589
|
-
|
|
275590
|
-
|
|
275591
|
-
|
|
275592
|
-
});
|
|
275593
|
-
req.write(body);
|
|
275594
|
-
req.end();
|
|
275658
|
+
const tags = buildCommonArchiveTags({
|
|
275659
|
+
deploymentId: archiver.getDeploymentId(),
|
|
275660
|
+
accountSelector: input.accountSelector,
|
|
275661
|
+
exchange: input.exchange,
|
|
275662
|
+
symbol: input.symbol,
|
|
275663
|
+
brokerObservedTimestamp: input.brokerObservedTimestamp
|
|
275664
|
+
});
|
|
275665
|
+
const row = buildMarketMetadataSnapshotRow({
|
|
275666
|
+
tags,
|
|
275667
|
+
clientOrderId: input.clientOrderId,
|
|
275668
|
+
orderId: input.orderId,
|
|
275669
|
+
makerActionId: input.makerActionId,
|
|
275670
|
+
idempotencyId: input.idempotencyId,
|
|
275671
|
+
marketSnapshot: snapshot
|
|
275672
|
+
});
|
|
275673
|
+
archiver.enqueue(row);
|
|
275674
|
+
const hash4 = row.row.market_metadata_hash;
|
|
275675
|
+
return typeof hash4 === "string" ? hash4 : undefined;
|
|
275676
|
+
} catch (archiveError) {
|
|
275677
|
+
rethrowArchiveDurabilityError(archiveError);
|
|
275678
|
+
log.warn("Failed to capture market metadata snapshot", {
|
|
275679
|
+
error: archiveError
|
|
275595
275680
|
});
|
|
275681
|
+
return;
|
|
275596
275682
|
}
|
|
275597
275683
|
}
|
|
275598
|
-
|
|
275599
|
-
|
|
275600
|
-
|
|
275601
|
-
|
|
275602
|
-
|
|
275603
|
-
|
|
275604
|
-
|
|
275605
|
-
|
|
275606
|
-
|
|
275607
|
-
|
|
275608
|
-
|
|
275609
|
-
|
|
275684
|
+
// src/helpers/broker-execution-archive/withdrawal-observation-tracker.ts
|
|
275685
|
+
var DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES = 1e4;
|
|
275686
|
+
var VENUE_LIFECYCLE_EVIDENCE_FIELDS = [
|
|
275687
|
+
"updated",
|
|
275688
|
+
"updatedAt",
|
|
275689
|
+
"updated_at",
|
|
275690
|
+
"updateTime",
|
|
275691
|
+
"update_time",
|
|
275692
|
+
"updateTimestamp",
|
|
275693
|
+
"lastUpdateTimestamp",
|
|
275694
|
+
"completed",
|
|
275695
|
+
"completedAt",
|
|
275696
|
+
"completed_at",
|
|
275697
|
+
"completeTime",
|
|
275698
|
+
"complete_time",
|
|
275699
|
+
"completionTime",
|
|
275700
|
+
"successTime"
|
|
275701
|
+
];
|
|
275702
|
+
function fingerprintEvidenceValue(value) {
|
|
275703
|
+
if (value === undefined || value === null) {
|
|
275704
|
+
return value;
|
|
275705
|
+
}
|
|
275706
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
275707
|
+
return value;
|
|
275708
|
+
}
|
|
275709
|
+
if (typeof value === "bigint") {
|
|
275710
|
+
return value.toString();
|
|
275711
|
+
}
|
|
275712
|
+
try {
|
|
275713
|
+
return JSON.stringify(value);
|
|
275714
|
+
} catch {
|
|
275715
|
+
return String(value);
|
|
275716
|
+
}
|
|
275717
|
+
}
|
|
275718
|
+
function venueLifecycleEvidence(transaction) {
|
|
275719
|
+
const record = asRecord(transaction);
|
|
275720
|
+
const info = asRecord(record?.info);
|
|
275721
|
+
const evidence = [];
|
|
275722
|
+
for (const [scope, source] of [
|
|
275723
|
+
["transaction", record],
|
|
275724
|
+
["info", info]
|
|
275725
|
+
]) {
|
|
275726
|
+
for (const field of VENUE_LIFECYCLE_EVIDENCE_FIELDS) {
|
|
275727
|
+
const value = source?.[field];
|
|
275728
|
+
if (value !== undefined && value !== null) {
|
|
275729
|
+
evidence.push([scope, field, fingerprintEvidenceValue(value)]);
|
|
275730
|
+
}
|
|
275610
275731
|
}
|
|
275611
275732
|
}
|
|
275612
|
-
return
|
|
275733
|
+
return evidence;
|
|
275613
275734
|
}
|
|
275614
|
-
function
|
|
275615
|
-
const
|
|
275616
|
-
|
|
275617
|
-
|
|
275618
|
-
|
|
275619
|
-
|
|
275620
|
-
|
|
275621
|
-
|
|
275622
|
-
|
|
275623
|
-
|
|
275624
|
-
|
|
275735
|
+
function observationFingerprint(observation) {
|
|
275736
|
+
const { normalized, transaction } = observation;
|
|
275737
|
+
return JSON.stringify({
|
|
275738
|
+
status: normalized.status ?? null,
|
|
275739
|
+
txid: normalized.txid ?? null,
|
|
275740
|
+
amount: normalized.amount ?? null,
|
|
275741
|
+
feeAmount: normalized.feeAmount ?? null,
|
|
275742
|
+
feeCurrency: normalized.feeCurrency ?? null,
|
|
275743
|
+
address: normalized.address ?? null,
|
|
275744
|
+
network: normalized.network ?? null,
|
|
275745
|
+
exchangeTimestamp: normalized.exchangeTimestamp ?? null,
|
|
275746
|
+
venueLifecycleEvidence: venueLifecycleEvidence(transaction)
|
|
275625
275747
|
});
|
|
275626
275748
|
}
|
|
275627
|
-
|
|
275628
|
-
|
|
275629
|
-
|
|
275749
|
+
|
|
275750
|
+
class WithdrawalObservationTracker {
|
|
275751
|
+
#fingerprints = new Map;
|
|
275752
|
+
#maxEntries;
|
|
275753
|
+
#missingIdSequence = 0n;
|
|
275754
|
+
constructor(options) {
|
|
275755
|
+
const maxEntries = options?.maxEntries ?? DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
|
|
275756
|
+
this.#maxEntries = Number.isFinite(maxEntries) ? Math.max(1, Math.floor(maxEntries)) : DEFAULT_WITHDRAWAL_OBSERVATION_TRACKER_MAX_ENTRIES;
|
|
275757
|
+
}
|
|
275758
|
+
shouldArchive(observation) {
|
|
275759
|
+
const externalId = observation.normalized.externalId?.trim();
|
|
275760
|
+
const identity = JSON.stringify([
|
|
275761
|
+
observation.exchange.trim().toLowerCase() || "unknown",
|
|
275762
|
+
observation.accountSelector?.trim() || "unknown",
|
|
275763
|
+
observation.assetSymbol?.trim().toUpperCase() || "unknown",
|
|
275764
|
+
externalId || `missing:${++this.#missingIdSequence}`
|
|
275765
|
+
]);
|
|
275766
|
+
const fingerprint = observationFingerprint(observation);
|
|
275767
|
+
if (this.#fingerprints.get(identity) === fingerprint) {
|
|
275768
|
+
return false;
|
|
275769
|
+
}
|
|
275770
|
+
this.#fingerprints.delete(identity);
|
|
275771
|
+
this.#fingerprints.set(identity, fingerprint);
|
|
275772
|
+
while (this.#fingerprints.size > this.#maxEntries) {
|
|
275773
|
+
const oldest = this.#fingerprints.keys().next().value;
|
|
275774
|
+
if (oldest === undefined)
|
|
275775
|
+
break;
|
|
275776
|
+
this.#fingerprints.delete(oldest);
|
|
275777
|
+
}
|
|
275778
|
+
return true;
|
|
275779
|
+
}
|
|
275780
|
+
getSize() {
|
|
275781
|
+
return this.#fingerprints.size;
|
|
275630
275782
|
}
|
|
275631
|
-
const parsed = Number.parseInt(value, 10);
|
|
275632
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
275633
275783
|
}
|
|
275634
275784
|
// src/helpers/account-balance-archive-poller.ts
|
|
275635
275785
|
var DEFAULT_CONFIG = {
|
|
@@ -275723,6 +275873,7 @@ class AccountBalanceArchivePoller {
|
|
|
275723
275873
|
this.params.metrics?.recordGauge("cex_account_balance_poll_last_success_timestamp_seconds", Math.floor(successMs / 1000), labels);
|
|
275724
275874
|
this.#recordFreshness(labels, successMs, successMs);
|
|
275725
275875
|
} catch (error) {
|
|
275876
|
+
rethrowArchiveDurabilityError(error);
|
|
275726
275877
|
this.params.metrics?.recordCounter("cex_account_balance_poll_failures_total", 1, labels);
|
|
275727
275878
|
const now3 = Date.now();
|
|
275728
275879
|
const lastSuccess = this.#lastSuccessMs.get(this.#targetKey(target));
|
|
@@ -275752,6 +275903,7 @@ class AccountBalanceArchivePoller {
|
|
|
275752
275903
|
try {
|
|
275753
275904
|
await this.pollAllOnce();
|
|
275754
275905
|
} catch (error) {
|
|
275906
|
+
rethrowArchiveDurabilityError(error);
|
|
275755
275907
|
log.error("Account balance archive poller tick failed", error);
|
|
275756
275908
|
} finally {
|
|
275757
275909
|
if (!this.#stopped) {
|
|
@@ -275815,6 +275967,7 @@ class FillArchivePoller {
|
|
|
275815
275967
|
try {
|
|
275816
275968
|
await this.pollTrackedOnce();
|
|
275817
275969
|
} catch (error) {
|
|
275970
|
+
rethrowArchiveDurabilityError(error);
|
|
275818
275971
|
log.error("Fill archive poller tick failed", error);
|
|
275819
275972
|
} finally {
|
|
275820
275973
|
this.#running = false;
|
|
@@ -279806,32 +279959,6 @@ import * as grpc14 from "@grpc/grpc-js";
|
|
|
279806
279959
|
// src/handlers/execute-action/deposit.ts
|
|
279807
279960
|
import * as grpc3 from "@grpc/grpc-js";
|
|
279808
279961
|
|
|
279809
|
-
// src/helpers/shared/errors.ts
|
|
279810
|
-
var MAX_ERROR_DETAIL_LENGTH = 512;
|
|
279811
|
-
function getErrorMessage(error) {
|
|
279812
|
-
return error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
|
|
279813
|
-
}
|
|
279814
|
-
function errorClassName(error) {
|
|
279815
|
-
if (!(error instanceof Error)) {
|
|
279816
|
-
return;
|
|
279817
|
-
}
|
|
279818
|
-
const name = error.constructor?.name || error.name;
|
|
279819
|
-
return name && name !== "Error" ? name : undefined;
|
|
279820
|
-
}
|
|
279821
|
-
function sanitizeErrorDetail(error) {
|
|
279822
|
-
const className = errorClassName(error);
|
|
279823
|
-
const message = getErrorMessage(error);
|
|
279824
|
-
const detail = className ? `${className}: ${message}` : message;
|
|
279825
|
-
return detail.replace(/\s+/g, " ").trim().slice(0, MAX_ERROR_DETAIL_LENGTH);
|
|
279826
|
-
}
|
|
279827
|
-
function safeLogError(context3, error) {
|
|
279828
|
-
try {
|
|
279829
|
-
log.error(context3, { error });
|
|
279830
|
-
} catch {
|
|
279831
|
-
console.error(context3, error);
|
|
279832
|
-
}
|
|
279833
|
-
}
|
|
279834
|
-
|
|
279835
279962
|
// src/helpers/treasury-discovery.ts
|
|
279836
279963
|
function callArgs(args, params) {
|
|
279837
279964
|
const argsArray = Array.isArray(args) ? [...args] : [];
|
|
@@ -294358,7 +294485,8 @@ async function handleCreateOrder(ctx) {
|
|
|
294358
294485
|
archiveOrderExecutionInBackground(brokerArchiver, createOrderContext, order, undefined, { marketMetadataHash });
|
|
294359
294486
|
ctx.wrappedCallback(null, { result: JSON.stringify({ ...order }) });
|
|
294360
294487
|
} catch (error48) {
|
|
294361
|
-
|
|
294488
|
+
rethrowArchiveDurabilityError(error48);
|
|
294489
|
+
safeLogRedactedError("Order Creation failed", error48);
|
|
294362
294490
|
const failedCreateContext = {
|
|
294363
294491
|
action: "CreateOrder",
|
|
294364
294492
|
cex: cex3,
|
|
@@ -295633,8 +295761,8 @@ class BinanceSpotUserDataStream {
|
|
|
295633
295761
|
}
|
|
295634
295762
|
if ("status" in message && typeof message.status === "number" && message.status !== 200) {
|
|
295635
295763
|
const errorMessage = message.error?.msg ?? message.error?.message ?? `Binance user-data request failed with status ${message.status}`;
|
|
295636
|
-
const
|
|
295637
|
-
this.fail(new Error(typeof
|
|
295764
|
+
const errorCode2 = message.error?.code;
|
|
295765
|
+
this.fail(new Error(typeof errorCode2 === "number" ? `${errorMessage} (code ${errorCode2})` : errorMessage));
|
|
295638
295766
|
return;
|
|
295639
295767
|
}
|
|
295640
295768
|
if (!("event" in message) || !message.event) {
|
|
@@ -296232,6 +296360,7 @@ function archiveOrderbookInBackground(archiver, otelMetrics, input, options) {
|
|
|
296232
296360
|
recordWatchMetric(otelMetrics, "cex_watch_frames_archived_total", labels);
|
|
296233
296361
|
}
|
|
296234
296362
|
} catch (error48) {
|
|
296363
|
+
rethrowArchiveDurabilityError(error48);
|
|
296235
296364
|
log.warn("Failed to archive orderbook snapshot", { error: error48 });
|
|
296236
296365
|
}
|
|
296237
296366
|
});
|
|
@@ -296259,6 +296388,7 @@ function archiveOhlcvInBackground(archiver, otelMetrics, tracker, input) {
|
|
|
296259
296388
|
recordWatchMetric(otelMetrics, "cex_watch_frames_archived_total", labels);
|
|
296260
296389
|
}
|
|
296261
296390
|
} catch (error48) {
|
|
296391
|
+
rethrowArchiveDurabilityError(error48);
|
|
296262
296392
|
log.warn("Failed to archive OHLCV candle", { error: error48 });
|
|
296263
296393
|
}
|
|
296264
296394
|
});
|
|
@@ -296285,6 +296415,7 @@ function archiveMarketRowsInBackground(archiver, otelMetrics, stream4, input, en
|
|
|
296285
296415
|
recordWatchMetric(otelMetrics, "cex_watch_frames_archived_total", labels);
|
|
296286
296416
|
}
|
|
296287
296417
|
} catch (error48) {
|
|
296418
|
+
rethrowArchiveDurabilityError(error48);
|
|
296288
296419
|
log.warn(`Failed to archive ${stream4} market data`, { error: error48 });
|
|
296289
296420
|
}
|
|
296290
296421
|
});
|
|
@@ -296320,7 +296451,6 @@ function resolveOhlcvBootstrapLimit(optionValue) {
|
|
|
296320
296451
|
}
|
|
296321
296452
|
return Math.min(parsed, MAX_OHLCV_BOOTSTRAP_LIMIT);
|
|
296322
296453
|
}
|
|
296323
|
-
|
|
296324
296454
|
// src/helpers/market-data-archive/ohlcv-history.ts
|
|
296325
296455
|
function supportsFetchOhlcv(broker) {
|
|
296326
296456
|
const fetchOHLCV = broker.fetchOHLCV;
|
|
@@ -297430,4 +297560,4 @@ export {
|
|
|
297430
297560
|
CEXBroker as default
|
|
297431
297561
|
};
|
|
297432
297562
|
|
|
297433
|
-
//# debugId=
|
|
297563
|
+
//# debugId=9DC3823C033EB0C864756E2164756E21
|