@usherlabs/cex-broker 0.2.26 → 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 +686 -540
- 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 +11 -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 +717 -569
- package/dist/index.js.map +19 -20
- 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,29 +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
|
-
var
|
|
275313
|
-
var DEFAULT_ARCHIVE_FORWARDER_PORT = 8090;
|
|
275110
|
+
var SHED_WARN_INTERVAL_MS = 60000;
|
|
275314
275111
|
function isArchiveOtelLogsEnabled() {
|
|
275315
275112
|
return process.env.CEX_BROKER_ARCHIVE_OTEL_LOGS_ENABLED === "true";
|
|
275316
275113
|
}
|
|
275317
275114
|
function resolveArchiveForwarderUrlFromEnv() {
|
|
275318
|
-
|
|
275319
|
-
if (explicit) {
|
|
275320
|
-
return explicit;
|
|
275321
|
-
}
|
|
275322
|
-
const host = process.env.CEX_BROKER_ARCHIVE_FORWARDER_HOST?.trim() || process.env.CEX_BROKER_CLICKHOUSE_HOST?.trim();
|
|
275323
|
-
if (!host) {
|
|
275324
|
-
return;
|
|
275325
|
-
}
|
|
275326
|
-
const protocol = process.env.CEX_BROKER_CLICKHOUSE_PROTOCOL || "http";
|
|
275327
|
-
const port = parsePositiveInt(process.env.CEX_BROKER_ARCHIVE_FORWARDER_PORT, DEFAULT_ARCHIVE_FORWARDER_PORT);
|
|
275328
|
-
const path = process.env.CEX_BROKER_ARCHIVE_FORWARDER_PATH?.trim() || DEFAULT_ARCHIVE_FORWARDER_PATH;
|
|
275329
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
275330
|
-
return `${protocol}://${host}:${port}${normalizedPath}`;
|
|
275115
|
+
return process.env.CEX_BROKER_ARCHIVE_FORWARDER_URL?.trim() || undefined;
|
|
275331
275116
|
}
|
|
275332
275117
|
|
|
275333
275118
|
class BrokerExecutionArchiver {
|
|
@@ -275335,6 +275120,8 @@ class BrokerExecutionArchiver {
|
|
|
275335
275120
|
otelLogs;
|
|
275336
275121
|
otelMetrics;
|
|
275337
275122
|
forwarderUrl;
|
|
275123
|
+
deadLetterPath;
|
|
275124
|
+
deadLetterFd;
|
|
275338
275125
|
maxQueueSize;
|
|
275339
275126
|
batchSize;
|
|
275340
275127
|
flushIntervalMs;
|
|
@@ -275348,8 +275135,8 @@ class BrokerExecutionArchiver {
|
|
|
275348
275135
|
};
|
|
275349
275136
|
flushTimer = null;
|
|
275350
275137
|
flushInFlight = null;
|
|
275138
|
+
lastShedWarnAtMs = 0;
|
|
275351
275139
|
closed = false;
|
|
275352
|
-
loggedMissingMarketForwarder = false;
|
|
275353
275140
|
enabled;
|
|
275354
275141
|
forwarderAuthToken;
|
|
275355
275142
|
constructor(options) {
|
|
@@ -275357,61 +275144,81 @@ class BrokerExecutionArchiver {
|
|
|
275357
275144
|
this.otelLogs = options.otelLogs;
|
|
275358
275145
|
this.otelMetrics = options.otelMetrics;
|
|
275359
275146
|
this.forwarderUrl = options.forwarderUrl?.trim();
|
|
275147
|
+
this.deadLetterPath = options.deadLetterPath?.trim();
|
|
275360
275148
|
this.maxQueueSize = options.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE;
|
|
275361
275149
|
this.batchSize = options.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
275362
275150
|
this.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
|
|
275363
275151
|
this.forwarderTimeoutMs = options.forwarderTimeoutMs ?? DEFAULT_FORWARDER_TIMEOUT_MS;
|
|
275364
275152
|
this.enabled = options.enabled;
|
|
275365
275153
|
this.forwarderAuthToken = process.env.CEX_BROKER_ARCHIVE_FORWARDER_TOKEN?.trim() || undefined;
|
|
275366
|
-
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 {
|
|
275367
275168
|
this.flushTimer = setInterval(() => {
|
|
275368
|
-
this.flush()
|
|
275369
|
-
log.warn("Broker execution archive flush failed", { error });
|
|
275370
|
-
});
|
|
275169
|
+
this.flush();
|
|
275371
275170
|
}, this.flushIntervalMs);
|
|
275372
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;
|
|
275373
275179
|
}
|
|
275374
275180
|
}
|
|
275375
275181
|
static disabled() {
|
|
275376
275182
|
return new BrokerExecutionArchiver({ enabled: false });
|
|
275377
275183
|
}
|
|
275378
275184
|
static create(options) {
|
|
275379
|
-
|
|
275380
|
-
const enabled = process.env.CEX_BROKER_ARCHIVE_ENABLED !== "false" && hasSink;
|
|
275381
|
-
return new BrokerExecutionArchiver({ ...options, enabled });
|
|
275185
|
+
return new BrokerExecutionArchiver({ ...options, enabled: true });
|
|
275382
275186
|
}
|
|
275383
275187
|
getDeploymentId() {
|
|
275384
275188
|
return this.deploymentId;
|
|
275385
275189
|
}
|
|
275386
275190
|
isEnabled() {
|
|
275387
|
-
return this.enabled && !this.closed
|
|
275191
|
+
return this.enabled && !this.closed;
|
|
275388
275192
|
}
|
|
275389
275193
|
canPersistMarketMetadataSnapshot() {
|
|
275390
|
-
return this.isEnabled()
|
|
275194
|
+
return this.isEnabled();
|
|
275391
275195
|
}
|
|
275392
275196
|
canPersistAccountBalanceSnapshots() {
|
|
275393
275197
|
return this.isEnabled() && Boolean(this.forwarderUrl);
|
|
275394
275198
|
}
|
|
275395
275199
|
enqueue(row) {
|
|
275396
|
-
if (!this.enabled || this.closed
|
|
275397
|
-
return;
|
|
275398
|
-
}
|
|
275399
|
-
if (isMarketArchiveTable(row.table) && !this.forwarderUrl) {
|
|
275400
|
-
if (!this.loggedMissingMarketForwarder) {
|
|
275401
|
-
this.loggedMissingMarketForwarder = true;
|
|
275402
|
-
log.warn("Market data archive row dropped: configure CEX_BROKER_ARCHIVE_FORWARDER_URL or CEX_BROKER_CLICKHOUSE_HOST", { table: row.table });
|
|
275403
|
-
}
|
|
275404
|
-
return;
|
|
275405
|
-
}
|
|
275406
|
-
if (row.table === "broker_account.balance_snapshots" && !this.forwarderUrl) {
|
|
275200
|
+
if (!this.enabled || this.closed) {
|
|
275407
275201
|
return;
|
|
275408
275202
|
}
|
|
275409
275203
|
if (this.queue.length >= this.maxQueueSize) {
|
|
275410
|
-
this.queue
|
|
275204
|
+
const shedRow = this.queue[0];
|
|
275205
|
+
if (shedRow) {
|
|
275206
|
+
this.appendLossRecords([shedRow], "queue_shed");
|
|
275207
|
+
this.queue.shift();
|
|
275208
|
+
}
|
|
275411
275209
|
this.stats.shed += 1;
|
|
275412
275210
|
this.recordArchiveMetric("cex_archive_rows_shed_total", {
|
|
275413
|
-
table:
|
|
275211
|
+
table: shedRow?.table ?? "unknown"
|
|
275414
275212
|
});
|
|
275213
|
+
const now3 = Date.now();
|
|
275214
|
+
if (now3 - this.lastShedWarnAtMs >= SHED_WARN_INTERVAL_MS) {
|
|
275215
|
+
log.warn("Archive queue full: shedding oldest rows", {
|
|
275216
|
+
shed_total: this.stats.shed,
|
|
275217
|
+
queue_max: this.maxQueueSize,
|
|
275218
|
+
table: shedRow?.table ?? "unknown"
|
|
275219
|
+
});
|
|
275220
|
+
this.lastShedWarnAtMs = now3;
|
|
275221
|
+
}
|
|
275415
275222
|
}
|
|
275416
275223
|
this.queue.push(row);
|
|
275417
275224
|
this.stats.enqueued += 1;
|
|
@@ -275419,9 +275226,7 @@ class BrokerExecutionArchiver {
|
|
|
275419
275226
|
table: row.table
|
|
275420
275227
|
});
|
|
275421
275228
|
if (this.queue.length >= this.batchSize) {
|
|
275422
|
-
this.flush()
|
|
275423
|
-
log.warn("Broker execution archive flush failed", { error });
|
|
275424
|
-
});
|
|
275229
|
+
this.flush();
|
|
275425
275230
|
}
|
|
275426
275231
|
}
|
|
275427
275232
|
enqueueInBackground(row) {
|
|
@@ -275446,22 +275251,37 @@ class BrokerExecutionArchiver {
|
|
|
275446
275251
|
if (this.flushTimer) {
|
|
275447
275252
|
clearInterval(this.flushTimer);
|
|
275448
275253
|
this.flushTimer = null;
|
|
275449
|
-
}
|
|
275450
|
-
|
|
275451
|
-
|
|
275452
|
-
|
|
275453
|
-
|
|
275454
|
-
|
|
275455
|
-
|
|
275456
|
-
|
|
275457
|
-
|
|
275458
|
-
const
|
|
275459
|
-
this.queue.length
|
|
275460
|
-
|
|
275461
|
-
|
|
275254
|
+
}
|
|
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
|
+
}
|
|
275462
275270
|
}
|
|
275271
|
+
} catch (error) {
|
|
275272
|
+
closeError = error;
|
|
275463
275273
|
}
|
|
275464
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
|
+
}
|
|
275465
275285
|
}
|
|
275466
275286
|
getStats() {
|
|
275467
275287
|
return { ...this.stats };
|
|
@@ -275469,15 +275289,60 @@ class BrokerExecutionArchiver {
|
|
|
275469
275289
|
getQueueDepth() {
|
|
275470
275290
|
return this.queue.length;
|
|
275471
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
|
+
}
|
|
275472
275305
|
enforceQueueBound() {
|
|
275473
275306
|
while (this.queue.length > this.maxQueueSize) {
|
|
275474
|
-
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();
|
|
275475
275313
|
this.stats.shed += 1;
|
|
275476
275314
|
this.recordArchiveMetric("cex_archive_rows_shed_total", {
|
|
275477
275315
|
table: dropped?.table ?? "unknown"
|
|
275478
275316
|
});
|
|
275479
275317
|
}
|
|
275480
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
|
+
}
|
|
275481
275346
|
async flushBatch() {
|
|
275482
275347
|
const batch = this.queue.splice(0, this.batchSize);
|
|
275483
275348
|
if (batch.length === 0) {
|
|
@@ -275514,111 +275379,407 @@ class BrokerExecutionArchiver {
|
|
|
275514
275379
|
for (const [table, count2] of countByTable) {
|
|
275515
275380
|
this.recordArchiveMetric("cex_archive_rows_flushed_total", { table }, count2);
|
|
275516
275381
|
}
|
|
275517
|
-
this.recordArchiveGauge("cex_archive_last_flush_success", Math.floor(Date.now() / 1000));
|
|
275518
|
-
}
|
|
275519
|
-
async recordArchiveMetric(metricName, labels, value = 1) {
|
|
275520
|
-
try {
|
|
275521
|
-
await this.otelMetrics?.recordCounter(metricName, value, labels);
|
|
275522
|
-
} 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
|
+
});
|
|
275523
275636
|
}
|
|
275524
|
-
|
|
275525
|
-
|
|
275526
|
-
|
|
275527
|
-
|
|
275637
|
+
}
|
|
275638
|
+
async function captureMarketMetadataSnapshot(archiver, broker, input) {
|
|
275639
|
+
if (!archiver?.canPersistMarketMetadataSnapshot()) {
|
|
275640
|
+
return;
|
|
275528
275641
|
}
|
|
275529
|
-
|
|
275530
|
-
|
|
275642
|
+
try {
|
|
275643
|
+
const fetchOrderBook = broker.fetchOrderBook;
|
|
275644
|
+
if (typeof fetchOrderBook !== "function") {
|
|
275531
275645
|
return;
|
|
275532
275646
|
}
|
|
275533
|
-
|
|
275534
|
-
|
|
275535
|
-
|
|
275536
|
-
|
|
275537
|
-
|
|
275538
|
-
|
|
275539
|
-
|
|
275540
|
-
|
|
275541
|
-
|
|
275542
|
-
|
|
275543
|
-
}
|
|
275544
|
-
postToForwarder(batch) {
|
|
275545
|
-
if (!this.forwarderUrl || batch.length === 0) {
|
|
275546
|
-
return Promise.resolve();
|
|
275547
|
-
}
|
|
275548
|
-
const body = JSON.stringify({
|
|
275549
|
-
source: "broker_write",
|
|
275550
|
-
deployment_id: this.deploymentId,
|
|
275551
|
-
rows: batch
|
|
275552
|
-
});
|
|
275553
|
-
const url2 = new URL(this.forwarderUrl);
|
|
275554
|
-
const doRequest = url2.protocol === "http:" ? httpRequest2 : httpsRequest2;
|
|
275555
|
-
const headers = {
|
|
275556
|
-
"content-type": "application/json",
|
|
275557
|
-
"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
|
|
275558
275657
|
};
|
|
275559
|
-
|
|
275560
|
-
|
|
275561
|
-
|
|
275562
|
-
|
|
275563
|
-
|
|
275564
|
-
|
|
275565
|
-
|
|
275566
|
-
|
|
275567
|
-
|
|
275568
|
-
|
|
275569
|
-
|
|
275570
|
-
|
|
275571
|
-
|
|
275572
|
-
|
|
275573
|
-
|
|
275574
|
-
|
|
275575
|
-
|
|
275576
|
-
|
|
275577
|
-
|
|
275578
|
-
|
|
275579
|
-
|
|
275580
|
-
|
|
275581
|
-
});
|
|
275582
|
-
req.write(body);
|
|
275583
|
-
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
|
|
275584
275680
|
});
|
|
275681
|
+
return;
|
|
275585
275682
|
}
|
|
275586
275683
|
}
|
|
275587
|
-
|
|
275588
|
-
|
|
275589
|
-
|
|
275590
|
-
|
|
275591
|
-
|
|
275592
|
-
|
|
275593
|
-
|
|
275594
|
-
|
|
275595
|
-
|
|
275596
|
-
|
|
275597
|
-
|
|
275598
|
-
|
|
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
|
+
}
|
|
275599
275731
|
}
|
|
275600
275732
|
}
|
|
275601
|
-
return
|
|
275733
|
+
return evidence;
|
|
275602
275734
|
}
|
|
275603
|
-
function
|
|
275604
|
-
const
|
|
275605
|
-
|
|
275606
|
-
|
|
275607
|
-
|
|
275608
|
-
|
|
275609
|
-
|
|
275610
|
-
|
|
275611
|
-
|
|
275612
|
-
|
|
275613
|
-
|
|
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)
|
|
275614
275747
|
});
|
|
275615
275748
|
}
|
|
275616
|
-
|
|
275617
|
-
|
|
275618
|
-
|
|
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;
|
|
275619
275782
|
}
|
|
275620
|
-
const parsed = Number.parseInt(value, 10);
|
|
275621
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
275622
275783
|
}
|
|
275623
275784
|
// src/helpers/account-balance-archive-poller.ts
|
|
275624
275785
|
var DEFAULT_CONFIG = {
|
|
@@ -275712,6 +275873,7 @@ class AccountBalanceArchivePoller {
|
|
|
275712
275873
|
this.params.metrics?.recordGauge("cex_account_balance_poll_last_success_timestamp_seconds", Math.floor(successMs / 1000), labels);
|
|
275713
275874
|
this.#recordFreshness(labels, successMs, successMs);
|
|
275714
275875
|
} catch (error) {
|
|
275876
|
+
rethrowArchiveDurabilityError(error);
|
|
275715
275877
|
this.params.metrics?.recordCounter("cex_account_balance_poll_failures_total", 1, labels);
|
|
275716
275878
|
const now3 = Date.now();
|
|
275717
275879
|
const lastSuccess = this.#lastSuccessMs.get(this.#targetKey(target));
|
|
@@ -275741,6 +275903,7 @@ class AccountBalanceArchivePoller {
|
|
|
275741
275903
|
try {
|
|
275742
275904
|
await this.pollAllOnce();
|
|
275743
275905
|
} catch (error) {
|
|
275906
|
+
rethrowArchiveDurabilityError(error);
|
|
275744
275907
|
log.error("Account balance archive poller tick failed", error);
|
|
275745
275908
|
} finally {
|
|
275746
275909
|
if (!this.#stopped) {
|
|
@@ -275804,6 +275967,7 @@ class FillArchivePoller {
|
|
|
275804
275967
|
try {
|
|
275805
275968
|
await this.pollTrackedOnce();
|
|
275806
275969
|
} catch (error) {
|
|
275970
|
+
rethrowArchiveDurabilityError(error);
|
|
275807
275971
|
log.error("Fill archive poller tick failed", error);
|
|
275808
275972
|
} finally {
|
|
275809
275973
|
this.#running = false;
|
|
@@ -279795,32 +279959,6 @@ import * as grpc14 from "@grpc/grpc-js";
|
|
|
279795
279959
|
// src/handlers/execute-action/deposit.ts
|
|
279796
279960
|
import * as grpc3 from "@grpc/grpc-js";
|
|
279797
279961
|
|
|
279798
|
-
// src/helpers/shared/errors.ts
|
|
279799
|
-
var MAX_ERROR_DETAIL_LENGTH = 512;
|
|
279800
|
-
function getErrorMessage(error) {
|
|
279801
|
-
return error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
|
|
279802
|
-
}
|
|
279803
|
-
function errorClassName(error) {
|
|
279804
|
-
if (!(error instanceof Error)) {
|
|
279805
|
-
return;
|
|
279806
|
-
}
|
|
279807
|
-
const name = error.constructor?.name || error.name;
|
|
279808
|
-
return name && name !== "Error" ? name : undefined;
|
|
279809
|
-
}
|
|
279810
|
-
function sanitizeErrorDetail(error) {
|
|
279811
|
-
const className = errorClassName(error);
|
|
279812
|
-
const message = getErrorMessage(error);
|
|
279813
|
-
const detail = className ? `${className}: ${message}` : message;
|
|
279814
|
-
return detail.replace(/\s+/g, " ").trim().slice(0, MAX_ERROR_DETAIL_LENGTH);
|
|
279815
|
-
}
|
|
279816
|
-
function safeLogError(context3, error) {
|
|
279817
|
-
try {
|
|
279818
|
-
log.error(context3, { error });
|
|
279819
|
-
} catch {
|
|
279820
|
-
console.error(context3, error);
|
|
279821
|
-
}
|
|
279822
|
-
}
|
|
279823
|
-
|
|
279824
279962
|
// src/helpers/treasury-discovery.ts
|
|
279825
279963
|
function callArgs(args, params) {
|
|
279826
279964
|
const argsArray = Array.isArray(args) ? [...args] : [];
|
|
@@ -294347,7 +294485,8 @@ async function handleCreateOrder(ctx) {
|
|
|
294347
294485
|
archiveOrderExecutionInBackground(brokerArchiver, createOrderContext, order, undefined, { marketMetadataHash });
|
|
294348
294486
|
ctx.wrappedCallback(null, { result: JSON.stringify({ ...order }) });
|
|
294349
294487
|
} catch (error48) {
|
|
294350
|
-
|
|
294488
|
+
rethrowArchiveDurabilityError(error48);
|
|
294489
|
+
safeLogRedactedError("Order Creation failed", error48);
|
|
294351
294490
|
const failedCreateContext = {
|
|
294352
294491
|
action: "CreateOrder",
|
|
294353
294492
|
cex: cex3,
|
|
@@ -295622,8 +295761,8 @@ class BinanceSpotUserDataStream {
|
|
|
295622
295761
|
}
|
|
295623
295762
|
if ("status" in message && typeof message.status === "number" && message.status !== 200) {
|
|
295624
295763
|
const errorMessage = message.error?.msg ?? message.error?.message ?? `Binance user-data request failed with status ${message.status}`;
|
|
295625
|
-
const
|
|
295626
|
-
this.fail(new Error(typeof
|
|
295764
|
+
const errorCode2 = message.error?.code;
|
|
295765
|
+
this.fail(new Error(typeof errorCode2 === "number" ? `${errorMessage} (code ${errorCode2})` : errorMessage));
|
|
295627
295766
|
return;
|
|
295628
295767
|
}
|
|
295629
295768
|
if (!("event" in message) || !message.event) {
|
|
@@ -295775,19 +295914,26 @@ class OhlcvBarTracker {
|
|
|
295775
295914
|
if (!firstBar || !lastBar) {
|
|
295776
295915
|
return [];
|
|
295777
295916
|
}
|
|
295778
|
-
|
|
295917
|
+
const lastOpenTimeMs = this.lastOpenTimeMs;
|
|
295918
|
+
if (lastOpenTimeMs !== null && lastBar.openTimeMs < lastOpenTimeMs) {
|
|
295919
|
+
return [];
|
|
295920
|
+
}
|
|
295921
|
+
const barsToProcess = lastOpenTimeMs === null ? bars : bars.filter((bar) => bar.openTimeMs >= lastOpenTimeMs);
|
|
295922
|
+
const firstBarToProcess = barsToProcess[0];
|
|
295923
|
+
const lastBarToProcess = barsToProcess[barsToProcess.length - 1];
|
|
295924
|
+
if (!firstBarToProcess || !lastBarToProcess) {
|
|
295779
295925
|
return [];
|
|
295780
295926
|
}
|
|
295781
295927
|
const candidates = [];
|
|
295782
|
-
if (this.lastBar !== null &&
|
|
295928
|
+
if (this.lastBar !== null && lastOpenTimeMs !== null && lastOpenTimeMs < firstBarToProcess.openTimeMs) {
|
|
295783
295929
|
candidates.push({
|
|
295784
295930
|
bar: this.lastBar,
|
|
295785
295931
|
isClosed: true,
|
|
295786
295932
|
brokerVersion
|
|
295787
295933
|
});
|
|
295788
295934
|
}
|
|
295789
|
-
for (let index2 = 0;index2 <
|
|
295790
|
-
const bar =
|
|
295935
|
+
for (let index2 = 0;index2 < barsToProcess.length - 1; index2 += 1) {
|
|
295936
|
+
const bar = barsToProcess[index2];
|
|
295791
295937
|
if (bar) {
|
|
295792
295938
|
candidates.push({
|
|
295793
295939
|
bar,
|
|
@@ -295797,12 +295943,12 @@ class OhlcvBarTracker {
|
|
|
295797
295943
|
}
|
|
295798
295944
|
}
|
|
295799
295945
|
candidates.push({
|
|
295800
|
-
bar:
|
|
295946
|
+
bar: lastBarToProcess,
|
|
295801
295947
|
isClosed: false,
|
|
295802
295948
|
brokerVersion
|
|
295803
295949
|
});
|
|
295804
|
-
this.lastOpenTimeMs =
|
|
295805
|
-
this.lastBar =
|
|
295950
|
+
this.lastOpenTimeMs = lastBarToProcess.openTimeMs;
|
|
295951
|
+
this.lastBar = lastBarToProcess;
|
|
295806
295952
|
return candidates;
|
|
295807
295953
|
}
|
|
295808
295954
|
}
|
|
@@ -296214,6 +296360,7 @@ function archiveOrderbookInBackground(archiver, otelMetrics, input, options) {
|
|
|
296214
296360
|
recordWatchMetric(otelMetrics, "cex_watch_frames_archived_total", labels);
|
|
296215
296361
|
}
|
|
296216
296362
|
} catch (error48) {
|
|
296363
|
+
rethrowArchiveDurabilityError(error48);
|
|
296217
296364
|
log.warn("Failed to archive orderbook snapshot", { error: error48 });
|
|
296218
296365
|
}
|
|
296219
296366
|
});
|
|
@@ -296241,6 +296388,7 @@ function archiveOhlcvInBackground(archiver, otelMetrics, tracker, input) {
|
|
|
296241
296388
|
recordWatchMetric(otelMetrics, "cex_watch_frames_archived_total", labels);
|
|
296242
296389
|
}
|
|
296243
296390
|
} catch (error48) {
|
|
296391
|
+
rethrowArchiveDurabilityError(error48);
|
|
296244
296392
|
log.warn("Failed to archive OHLCV candle", { error: error48 });
|
|
296245
296393
|
}
|
|
296246
296394
|
});
|
|
@@ -296267,6 +296415,7 @@ function archiveMarketRowsInBackground(archiver, otelMetrics, stream4, input, en
|
|
|
296267
296415
|
recordWatchMetric(otelMetrics, "cex_watch_frames_archived_total", labels);
|
|
296268
296416
|
}
|
|
296269
296417
|
} catch (error48) {
|
|
296418
|
+
rethrowArchiveDurabilityError(error48);
|
|
296270
296419
|
log.warn(`Failed to archive ${stream4} market data`, { error: error48 });
|
|
296271
296420
|
}
|
|
296272
296421
|
});
|
|
@@ -296302,7 +296451,6 @@ function resolveOhlcvBootstrapLimit(optionValue) {
|
|
|
296302
296451
|
}
|
|
296303
296452
|
return Math.min(parsed, MAX_OHLCV_BOOTSTRAP_LIMIT);
|
|
296304
296453
|
}
|
|
296305
|
-
|
|
296306
296454
|
// src/helpers/market-data-archive/ohlcv-history.ts
|
|
296307
296455
|
function supportsFetchOhlcv(broker) {
|
|
296308
296456
|
const fetchOHLCV = broker.fetchOHLCV;
|
|
@@ -296787,7 +296935,7 @@ function createSubscribeHandler(deps) {
|
|
|
296787
296935
|
}
|
|
296788
296936
|
}
|
|
296789
296937
|
while (ohlcvStreamActive && !isStreamClosed()) {
|
|
296790
|
-
const data = await broker.
|
|
296938
|
+
const data = await broker.watchOHLCV(resolvedSymbol, timeframe);
|
|
296791
296939
|
const receivedTimestamp = Date.now();
|
|
296792
296940
|
if (!await writeSubscribeFrame(call, isStreamClosed, {
|
|
296793
296941
|
data: JSON.stringify(data),
|
|
@@ -297412,4 +297560,4 @@ export {
|
|
|
297412
297560
|
CEXBroker as default
|
|
297413
297561
|
};
|
|
297414
297562
|
|
|
297415
|
-
//# debugId=
|
|
297563
|
+
//# debugId=9DC3823C033EB0C864756E2164756E21
|