@usherlabs/cex-broker 0.2.19 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/cli.js +467 -32
- package/dist/handlers/execute-action/context.d.ts +2 -0
- package/dist/handlers/execute-action/handler.d.ts +2 -0
- package/dist/helpers/broker-execution-archive/capture.d.ts +8 -0
- package/dist/helpers/broker-execution-archive/index.d.ts +3 -3
- package/dist/helpers/broker-execution-archive/rows.d.ts +55 -1
- package/dist/helpers/broker-execution-archive/types.d.ts +5 -1
- package/dist/helpers/broker-execution-archive/writer.d.ts +2 -0
- package/dist/helpers/fill-archive-poller.d.ts +42 -0
- package/dist/helpers/order-activity-tracker.d.ts +22 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +474 -37
- package/dist/index.js.map +17 -15
- package/dist/server.d.ts +2 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -274531,6 +274531,8 @@ function hashMarketMetadata(payload) {
|
|
|
274531
274531
|
|
|
274532
274532
|
// src/helpers/broker-execution-archive/types.ts
|
|
274533
274533
|
var BROKER_WRITE_SOURCE = "broker_write";
|
|
274534
|
+
var ARCHIVE_SCHEMA_VERSION = "1";
|
|
274535
|
+
var FILL_EVENT_KIND = "trade_history_fill";
|
|
274534
274536
|
|
|
274535
274537
|
// src/helpers/broker-execution-archive/rows.ts
|
|
274536
274538
|
function firstString2(...values2) {
|
|
@@ -274544,6 +274546,26 @@ function firstString2(...values2) {
|
|
|
274544
274546
|
}
|
|
274545
274547
|
return;
|
|
274546
274548
|
}
|
|
274549
|
+
function firstNumber2(...values2) {
|
|
274550
|
+
for (const value of values2) {
|
|
274551
|
+
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
274552
|
+
if (Number.isFinite(numeric)) {
|
|
274553
|
+
return numeric;
|
|
274554
|
+
}
|
|
274555
|
+
}
|
|
274556
|
+
return;
|
|
274557
|
+
}
|
|
274558
|
+
function normalizeTimestamp2(value) {
|
|
274559
|
+
if (typeof value === "string" && value.trim()) {
|
|
274560
|
+
return value.trim();
|
|
274561
|
+
}
|
|
274562
|
+
const ms = firstNumber2(value);
|
|
274563
|
+
if (ms === undefined) {
|
|
274564
|
+
return;
|
|
274565
|
+
}
|
|
274566
|
+
const date = new Date(ms);
|
|
274567
|
+
return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
|
|
274568
|
+
}
|
|
274547
274569
|
function compactUndefined2(record) {
|
|
274548
274570
|
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
274549
274571
|
}
|
|
@@ -274608,6 +274630,99 @@ function buildSubscribeStreamArchiveRow(input) {
|
|
|
274608
274630
|
})
|
|
274609
274631
|
};
|
|
274610
274632
|
}
|
|
274633
|
+
function quantityString(...values2) {
|
|
274634
|
+
return firstString2(...values2);
|
|
274635
|
+
}
|
|
274636
|
+
function buildTransferEventArchiveRow(input) {
|
|
274637
|
+
const { tags, transfer } = input;
|
|
274638
|
+
return {
|
|
274639
|
+
table: "broker_execution.transfer_events",
|
|
274640
|
+
row: compactUndefined2({
|
|
274641
|
+
...tags,
|
|
274642
|
+
schema_version: ARCHIVE_SCHEMA_VERSION,
|
|
274643
|
+
event_kind: transfer.eventKind,
|
|
274644
|
+
lifecycle_action: transfer.lifecycleAction,
|
|
274645
|
+
status: transfer.status ?? "",
|
|
274646
|
+
asset_symbol: tags.symbol,
|
|
274647
|
+
amount: transfer.amount,
|
|
274648
|
+
address: transfer.address,
|
|
274649
|
+
network: transfer.network,
|
|
274650
|
+
external_id: transfer.externalId ?? "",
|
|
274651
|
+
txid: transfer.txid,
|
|
274652
|
+
result_index: transfer.resultIndex ?? 0,
|
|
274653
|
+
fee_amount: transfer.feeAmount,
|
|
274654
|
+
fee_currency: transfer.feeCurrency,
|
|
274655
|
+
exchange_timestamp: transfer.exchangeTimestamp,
|
|
274656
|
+
error_summary: transfer.errorSummary,
|
|
274657
|
+
payload_json: JSON.stringify(transfer.payload)
|
|
274658
|
+
})
|
|
274659
|
+
};
|
|
274660
|
+
}
|
|
274661
|
+
function normalizeCcxtTransactionForArchive(transaction) {
|
|
274662
|
+
const record = asRecord(transaction);
|
|
274663
|
+
const info = asRecord(record?.info);
|
|
274664
|
+
const fee = asRecord(record?.fee);
|
|
274665
|
+
return compactUndefined2({
|
|
274666
|
+
externalId: firstString2(record?.id, info?.id, record?.txid, info?.txId),
|
|
274667
|
+
txid: firstString2(record?.txid, info?.txId, info?.txid, info?.tx_hash),
|
|
274668
|
+
address: firstString2(record?.address, record?.addressTo, info?.address),
|
|
274669
|
+
network: firstString2(record?.network, info?.network),
|
|
274670
|
+
amount: quantityString(info?.amount, record?.amount),
|
|
274671
|
+
assetSymbol: firstString2(record?.currency, info?.coin, info?.asset),
|
|
274672
|
+
status: firstString2(record?.status, info?.status)?.toLowerCase(),
|
|
274673
|
+
feeAmount: quantityString(fee?.cost, record?.feeCost),
|
|
274674
|
+
feeCurrency: firstString2(fee?.currency, record?.feeCurrency),
|
|
274675
|
+
exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.applyTime))
|
|
274676
|
+
});
|
|
274677
|
+
}
|
|
274678
|
+
function firstValueForTransfer(...values2) {
|
|
274679
|
+
return values2.find((value) => value !== undefined && value !== null);
|
|
274680
|
+
}
|
|
274681
|
+
function buildFillEventArchiveRow(input) {
|
|
274682
|
+
const { tags, fill } = input;
|
|
274683
|
+
return {
|
|
274684
|
+
table: "broker_execution.fill_events",
|
|
274685
|
+
row: compactUndefined2({
|
|
274686
|
+
...tags,
|
|
274687
|
+
schema_version: ARCHIVE_SCHEMA_VERSION,
|
|
274688
|
+
event_kind: FILL_EVENT_KIND,
|
|
274689
|
+
order_id: fill.orderId ?? "",
|
|
274690
|
+
client_order_id: fill.clientOrderId,
|
|
274691
|
+
fill_id: fill.fillId,
|
|
274692
|
+
fill_index: fill.fillIndex ?? 0,
|
|
274693
|
+
side: fill.side,
|
|
274694
|
+
order_type: fill.orderType,
|
|
274695
|
+
price: fill.price,
|
|
274696
|
+
base_quantity: fill.baseQuantity,
|
|
274697
|
+
quote_quantity: fill.quoteQuantity,
|
|
274698
|
+
fee_amount: fill.feeAmount,
|
|
274699
|
+
fee_currency: fill.feeCurrency,
|
|
274700
|
+
fee_rate: fill.feeRate,
|
|
274701
|
+
exchange_timestamp: fill.exchangeTimestamp,
|
|
274702
|
+
payload_json: JSON.stringify(fill.payload)
|
|
274703
|
+
})
|
|
274704
|
+
};
|
|
274705
|
+
}
|
|
274706
|
+
function normalizeCcxtTradeForArchive(trade) {
|
|
274707
|
+
const record = asRecord(trade);
|
|
274708
|
+
const info = asRecord(record?.info);
|
|
274709
|
+
const fee = asRecord(record?.fee);
|
|
274710
|
+
return {
|
|
274711
|
+
orderId: firstString2(record?.order, info?.orderId, info?.orderID),
|
|
274712
|
+
clientOrderId: firstString2(record?.clientOrderId, info?.clientOrderId, info?.origClientOrderId),
|
|
274713
|
+
fillId: firstString2(record?.id, info?.id, info?.tradeId),
|
|
274714
|
+
side: firstString2(record?.side, info?.side)?.toLowerCase(),
|
|
274715
|
+
orderType: firstString2(record?.type, info?.type)?.toLowerCase(),
|
|
274716
|
+
price: quantityString(info?.price, record?.price),
|
|
274717
|
+
baseQuantity: quantityString(info?.qty, record?.amount),
|
|
274718
|
+
quoteQuantity: quantityString(info?.quoteQty, record?.cost),
|
|
274719
|
+
feeAmount: quantityString(fee?.cost, info?.commission),
|
|
274720
|
+
feeCurrency: firstString2(fee?.currency, info?.commissionAsset),
|
|
274721
|
+
feeRate: quantityString(fee?.rate),
|
|
274722
|
+
exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.time)),
|
|
274723
|
+
payload: trade
|
|
274724
|
+
};
|
|
274725
|
+
}
|
|
274611
274726
|
function buildMarketMetadataSnapshotRow(input) {
|
|
274612
274727
|
const redactedSnapshot = redactStreamPayload(input.marketSnapshot);
|
|
274613
274728
|
const metadataHash = hashMarketMetadata(redactedSnapshot);
|
|
@@ -274676,6 +274791,25 @@ function archiveSubscribeStreamInBackground(archiver, input) {
|
|
|
274676
274791
|
}
|
|
274677
274792
|
});
|
|
274678
274793
|
}
|
|
274794
|
+
function archiveTransferEventInBackground(archiver, input) {
|
|
274795
|
+
if (!archiver?.isEnabled()) {
|
|
274796
|
+
return;
|
|
274797
|
+
}
|
|
274798
|
+
queueMicrotask(() => {
|
|
274799
|
+
try {
|
|
274800
|
+
const tags = buildCommonArchiveTags({
|
|
274801
|
+
deploymentId: archiver.getDeploymentId(),
|
|
274802
|
+
accountSelector: input.accountSelector,
|
|
274803
|
+
exchange: input.exchange,
|
|
274804
|
+
symbol: input.assetSymbol,
|
|
274805
|
+
brokerObservedTimestamp: input.brokerObservedTimestamp
|
|
274806
|
+
});
|
|
274807
|
+
archiver.enqueue(buildTransferEventArchiveRow({ tags, transfer: input.transfer }));
|
|
274808
|
+
} catch (archiveError) {
|
|
274809
|
+
log.warn("Failed to archive transfer event", { error: archiveError });
|
|
274810
|
+
}
|
|
274811
|
+
});
|
|
274812
|
+
}
|
|
274679
274813
|
async function captureMarketMetadataSnapshot(archiver, broker, input) {
|
|
274680
274814
|
if (!archiver?.canPersistMarketMetadataSnapshot()) {
|
|
274681
274815
|
return;
|
|
@@ -274721,6 +274855,10 @@ async function captureMarketMetadataSnapshot(archiver, broker, input) {
|
|
|
274721
274855
|
return;
|
|
274722
274856
|
}
|
|
274723
274857
|
}
|
|
274858
|
+
// src/helpers/broker-execution-archive/writer.ts
|
|
274859
|
+
import { request as httpRequest2 } from "http";
|
|
274860
|
+
import { request as httpsRequest2 } from "https";
|
|
274861
|
+
|
|
274724
274862
|
// node_modules/@opentelemetry/api-logs/build/esm/types/LogRecord.js
|
|
274725
274863
|
var SeverityNumber2;
|
|
274726
274864
|
(function(SeverityNumber3) {
|
|
@@ -275054,11 +275192,27 @@ class BrokerExecutionArchiver {
|
|
|
275054
275192
|
}
|
|
275055
275193
|
}
|
|
275056
275194
|
this.stats.flushed += batch.length;
|
|
275195
|
+
this.recordFlushHealth(batch);
|
|
275057
275196
|
return true;
|
|
275058
275197
|
}
|
|
275059
|
-
|
|
275198
|
+
recordFlushHealth(batch) {
|
|
275199
|
+
const countByTable = new Map;
|
|
275200
|
+
for (const entry of batch) {
|
|
275201
|
+
countByTable.set(entry.table, (countByTable.get(entry.table) ?? 0) + 1);
|
|
275202
|
+
}
|
|
275203
|
+
for (const [table, count2] of countByTable) {
|
|
275204
|
+
this.recordArchiveMetric("cex_archive_rows_flushed_total", { table }, count2);
|
|
275205
|
+
}
|
|
275206
|
+
this.recordArchiveGauge("cex_archive_last_flush_success", Math.floor(Date.now() / 1000));
|
|
275207
|
+
}
|
|
275208
|
+
async recordArchiveMetric(metricName, labels, value = 1) {
|
|
275060
275209
|
try {
|
|
275061
|
-
await this.otelMetrics?.recordCounter(metricName,
|
|
275210
|
+
await this.otelMetrics?.recordCounter(metricName, value, labels);
|
|
275211
|
+
} catch {}
|
|
275212
|
+
}
|
|
275213
|
+
async recordArchiveGauge(metricName, value) {
|
|
275214
|
+
try {
|
|
275215
|
+
await this.otelMetrics?.recordGauge(metricName, value, {});
|
|
275062
275216
|
} catch {}
|
|
275063
275217
|
}
|
|
275064
275218
|
emitOtelLog(entry) {
|
|
@@ -275076,35 +275230,47 @@ class BrokerExecutionArchiver {
|
|
|
275076
275230
|
log.warn("Broker execution archive OTLP emit failed", { error });
|
|
275077
275231
|
}
|
|
275078
275232
|
}
|
|
275079
|
-
|
|
275233
|
+
postToForwarder(batch) {
|
|
275080
275234
|
if (!this.forwarderUrl || batch.length === 0) {
|
|
275081
|
-
return;
|
|
275235
|
+
return Promise.resolve();
|
|
275082
275236
|
}
|
|
275083
|
-
const
|
|
275084
|
-
|
|
275237
|
+
const body = JSON.stringify({
|
|
275238
|
+
source: "broker_write",
|
|
275239
|
+
deployment_id: this.deploymentId,
|
|
275240
|
+
rows: batch
|
|
275241
|
+
});
|
|
275242
|
+
const url2 = new URL(this.forwarderUrl);
|
|
275243
|
+
const doRequest = url2.protocol === "http:" ? httpRequest2 : httpsRequest2;
|
|
275085
275244
|
const headers = {
|
|
275086
|
-
"content-type": "application/json"
|
|
275245
|
+
"content-type": "application/json",
|
|
275246
|
+
"content-length": Buffer.byteLength(body)
|
|
275087
275247
|
};
|
|
275088
275248
|
if (this.forwarderAuthToken) {
|
|
275089
275249
|
headers.authorization = `Bearer ${this.forwarderAuthToken}`;
|
|
275090
275250
|
}
|
|
275091
|
-
|
|
275092
|
-
const
|
|
275251
|
+
return new Promise((resolve, reject) => {
|
|
275252
|
+
const req = doRequest(url2, {
|
|
275093
275253
|
method: "POST",
|
|
275094
275254
|
headers,
|
|
275095
|
-
|
|
275096
|
-
|
|
275097
|
-
|
|
275098
|
-
|
|
275099
|
-
|
|
275100
|
-
|
|
275255
|
+
timeout: this.forwarderTimeoutMs
|
|
275256
|
+
}, (res) => {
|
|
275257
|
+
res.on("data", () => {});
|
|
275258
|
+
res.on("end", () => {
|
|
275259
|
+
const status = res.statusCode ?? 0;
|
|
275260
|
+
if (status < 200 || status >= 300) {
|
|
275261
|
+
reject(new Error(`Archive forwarder returned ${status} ${res.statusMessage ?? ""}`));
|
|
275262
|
+
return;
|
|
275263
|
+
}
|
|
275264
|
+
resolve();
|
|
275265
|
+
});
|
|
275101
275266
|
});
|
|
275102
|
-
|
|
275103
|
-
|
|
275104
|
-
|
|
275105
|
-
|
|
275106
|
-
|
|
275107
|
-
|
|
275267
|
+
req.on("error", reject);
|
|
275268
|
+
req.on("timeout", () => {
|
|
275269
|
+
req.destroy(new Error("Archive forwarder request timed out"));
|
|
275270
|
+
});
|
|
275271
|
+
req.write(body);
|
|
275272
|
+
req.end();
|
|
275273
|
+
});
|
|
275108
275274
|
}
|
|
275109
275275
|
}
|
|
275110
275276
|
function flattenArchiveAttributes(entry) {
|
|
@@ -275143,6 +275309,159 @@ function parsePositiveInt(value, fallback) {
|
|
|
275143
275309
|
const parsed = Number.parseInt(value, 10);
|
|
275144
275310
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
275145
275311
|
}
|
|
275312
|
+
// src/helpers/fill-archive-poller.ts
|
|
275313
|
+
var DEFAULT_CONFIG = {
|
|
275314
|
+
pollIntervalMs: 60000,
|
|
275315
|
+
lookbackMs: 24 * 60 * 60 * 1000,
|
|
275316
|
+
tradesLimit: 500
|
|
275317
|
+
};
|
|
275318
|
+
function nextFillCursor(trades, currentSince) {
|
|
275319
|
+
let next = currentSince;
|
|
275320
|
+
for (const trade of trades) {
|
|
275321
|
+
const ts = asRecord(trade)?.timestamp;
|
|
275322
|
+
if (typeof ts === "number" && Number.isFinite(ts) && ts + 1 > next) {
|
|
275323
|
+
next = ts + 1;
|
|
275324
|
+
}
|
|
275325
|
+
}
|
|
275326
|
+
return next;
|
|
275327
|
+
}
|
|
275328
|
+
|
|
275329
|
+
class FillArchivePoller {
|
|
275330
|
+
params;
|
|
275331
|
+
#timer = null;
|
|
275332
|
+
#stopped = false;
|
|
275333
|
+
#running = false;
|
|
275334
|
+
#cursors = new Map;
|
|
275335
|
+
#config;
|
|
275336
|
+
constructor(params) {
|
|
275337
|
+
this.params = params;
|
|
275338
|
+
this.#config = { ...DEFAULT_CONFIG, ...params.config };
|
|
275339
|
+
}
|
|
275340
|
+
start() {
|
|
275341
|
+
if (this.#timer || this.#stopped) {
|
|
275342
|
+
return;
|
|
275343
|
+
}
|
|
275344
|
+
if (!this.params.archiver.isEnabled()) {
|
|
275345
|
+
return;
|
|
275346
|
+
}
|
|
275347
|
+
log.info("\uD83E\uDDFE Fill archive poller started");
|
|
275348
|
+
this.#timer = setTimeout(() => void this.#tick(), 0);
|
|
275349
|
+
this.#timer.unref?.();
|
|
275350
|
+
}
|
|
275351
|
+
stop() {
|
|
275352
|
+
this.#stopped = true;
|
|
275353
|
+
if (this.#timer) {
|
|
275354
|
+
clearTimeout(this.#timer);
|
|
275355
|
+
this.#timer = null;
|
|
275356
|
+
}
|
|
275357
|
+
}
|
|
275358
|
+
async#tick() {
|
|
275359
|
+
if (this.#stopped || this.#running) {
|
|
275360
|
+
return;
|
|
275361
|
+
}
|
|
275362
|
+
this.#running = true;
|
|
275363
|
+
try {
|
|
275364
|
+
await this.pollTrackedOnce();
|
|
275365
|
+
} catch (error) {
|
|
275366
|
+
log.error("Fill archive poller tick failed", error);
|
|
275367
|
+
} finally {
|
|
275368
|
+
this.#running = false;
|
|
275369
|
+
if (!this.#stopped) {
|
|
275370
|
+
this.#timer = setTimeout(() => void this.#tick(), this.#config.pollIntervalMs);
|
|
275371
|
+
this.#timer.unref?.();
|
|
275372
|
+
}
|
|
275373
|
+
}
|
|
275374
|
+
}
|
|
275375
|
+
async pollTrackedOnce() {
|
|
275376
|
+
for (const entry of this.params.tracker.list()) {
|
|
275377
|
+
if (this.#stopped) {
|
|
275378
|
+
break;
|
|
275379
|
+
}
|
|
275380
|
+
await this.#pollOne(entry.exchangeId, entry.accountLabel, entry.symbol);
|
|
275381
|
+
}
|
|
275382
|
+
}
|
|
275383
|
+
async#pollOne(exchangeId, accountLabel, symbol) {
|
|
275384
|
+
const account = resolveBrokerAccount(this.params.brokers[exchangeId], accountLabel);
|
|
275385
|
+
if (!account) {
|
|
275386
|
+
return;
|
|
275387
|
+
}
|
|
275388
|
+
const exchange = account.exchange;
|
|
275389
|
+
if (typeof exchange.fetchMyTrades !== "function" || exchange.has?.fetchMyTrades === false) {
|
|
275390
|
+
return;
|
|
275391
|
+
}
|
|
275392
|
+
const key = `${exchangeId}|${accountLabel}|${symbol}`;
|
|
275393
|
+
const since = this.#cursors.get(key) ?? Date.now() - this.#config.lookbackMs;
|
|
275394
|
+
let trades;
|
|
275395
|
+
try {
|
|
275396
|
+
trades = await exchange.fetchMyTrades(symbol, since, this.#config.tradesLimit);
|
|
275397
|
+
} catch (error) {
|
|
275398
|
+
this.params.metrics?.recordCounter("cex_fill_poller_errors_total", 1, {
|
|
275399
|
+
exchange: exchangeId
|
|
275400
|
+
});
|
|
275401
|
+
log.warn("Fill archive poll failed", {
|
|
275402
|
+
exchange: exchangeId,
|
|
275403
|
+
account: accountLabel,
|
|
275404
|
+
symbol,
|
|
275405
|
+
error
|
|
275406
|
+
});
|
|
275407
|
+
return;
|
|
275408
|
+
}
|
|
275409
|
+
if (!Array.isArray(trades) || trades.length === 0) {
|
|
275410
|
+
return;
|
|
275411
|
+
}
|
|
275412
|
+
trades.forEach((trade, fillIndex) => {
|
|
275413
|
+
const tags = buildCommonArchiveTags({
|
|
275414
|
+
deploymentId: this.params.archiver.getDeploymentId(),
|
|
275415
|
+
accountSelector: accountLabel,
|
|
275416
|
+
exchange: exchangeId,
|
|
275417
|
+
symbol
|
|
275418
|
+
});
|
|
275419
|
+
this.params.archiver.enqueue(buildFillEventArchiveRow({
|
|
275420
|
+
tags,
|
|
275421
|
+
fill: { ...normalizeCcxtTradeForArchive(trade), fillIndex }
|
|
275422
|
+
}));
|
|
275423
|
+
});
|
|
275424
|
+
this.params.metrics?.recordCounter("cex_fill_poller_trades_archived_total", trades.length, { exchange: exchangeId });
|
|
275425
|
+
this.#cursors.set(key, nextFillCursor(trades, since));
|
|
275426
|
+
}
|
|
275427
|
+
}
|
|
275428
|
+
|
|
275429
|
+
// src/helpers/order-activity-tracker.ts
|
|
275430
|
+
var DEFAULT_MAX_AGE_MS = 6 * 60 * 60 * 1000;
|
|
275431
|
+
|
|
275432
|
+
class OrderActivityTracker {
|
|
275433
|
+
#entries = new Map;
|
|
275434
|
+
#maxAgeMs;
|
|
275435
|
+
constructor(options) {
|
|
275436
|
+
this.#maxAgeMs = options?.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
275437
|
+
}
|
|
275438
|
+
record(exchangeId, accountLabel, symbol, now3 = Date.now()) {
|
|
275439
|
+
const exchange = exchangeId.trim().toLowerCase();
|
|
275440
|
+
const trimmedSymbol = symbol.trim();
|
|
275441
|
+
if (!exchange || !accountLabel.trim() || !trimmedSymbol) {
|
|
275442
|
+
return;
|
|
275443
|
+
}
|
|
275444
|
+
const key = `${exchange}|${accountLabel}|${trimmedSymbol}`;
|
|
275445
|
+
this.#entries.set(key, {
|
|
275446
|
+
exchangeId: exchange,
|
|
275447
|
+
accountLabel,
|
|
275448
|
+
symbol: trimmedSymbol,
|
|
275449
|
+
lastActivityAt: now3
|
|
275450
|
+
});
|
|
275451
|
+
}
|
|
275452
|
+
list(now3 = Date.now()) {
|
|
275453
|
+
const active = [];
|
|
275454
|
+
for (const [key, entry] of this.#entries) {
|
|
275455
|
+
if (now3 - entry.lastActivityAt > this.#maxAgeMs) {
|
|
275456
|
+
this.#entries.delete(key);
|
|
275457
|
+
continue;
|
|
275458
|
+
}
|
|
275459
|
+
active.push(entry);
|
|
275460
|
+
}
|
|
275461
|
+
return active;
|
|
275462
|
+
}
|
|
275463
|
+
}
|
|
275464
|
+
|
|
275146
275465
|
// src/helpers/otel.ts
|
|
275147
275466
|
var import_api14 = __toESM(require_src2(), 1);
|
|
275148
275467
|
|
|
@@ -278110,11 +278429,11 @@ var JsonMetricsSerializer = {
|
|
|
278110
278429
|
}
|
|
278111
278430
|
};
|
|
278112
278431
|
// node_modules/@opentelemetry/exporter-logs-otlp-http/build/esm/platform/node/OTLPLogExporter.js
|
|
278113
|
-
var
|
|
278432
|
+
var import_node_http3 = __toESM(require_index_node_http(), 1);
|
|
278114
278433
|
|
|
278115
278434
|
class OTLPLogExporter extends import_otlp_exporter_base.OTLPExporterBase {
|
|
278116
278435
|
constructor(config = {}) {
|
|
278117
|
-
super(
|
|
278436
|
+
super(import_node_http3.createOtlpHttpExportDelegate(import_node_http3.convertLegacyHttpOptions(config, "LOGS", "v1/logs", {
|
|
278118
278437
|
"Content-Type": "application/json"
|
|
278119
278438
|
}), JsonLogsSerializer));
|
|
278120
278439
|
}
|
|
@@ -278209,11 +278528,11 @@ class OTLPMetricExporterBase extends import_otlp_exporter_base2.OTLPExporterBase
|
|
|
278209
278528
|
}
|
|
278210
278529
|
|
|
278211
278530
|
// node_modules/@opentelemetry/exporter-metrics-otlp-http/build/esm/platform/node/OTLPMetricExporter.js
|
|
278212
|
-
var
|
|
278531
|
+
var import_node_http4 = __toESM(require_index_node_http(), 1);
|
|
278213
278532
|
|
|
278214
278533
|
class OTLPMetricExporter extends OTLPMetricExporterBase {
|
|
278215
278534
|
constructor(config) {
|
|
278216
|
-
super(
|
|
278535
|
+
super(import_node_http4.createOtlpHttpExportDelegate(import_node_http4.convertLegacyHttpOptions(config ?? {}, "METRICS", "v1/metrics", {
|
|
278217
278536
|
"Content-Type": "application/json"
|
|
278218
278537
|
}), JsonMetricsSerializer), config);
|
|
278219
278538
|
}
|
|
@@ -292891,7 +293210,13 @@ function rejectWithGrpcError(ctx, error48, options) {
|
|
|
292891
293210
|
|
|
292892
293211
|
// src/handlers/execute-action/deposit.ts
|
|
292893
293212
|
async function handleDeposit(ctx) {
|
|
292894
|
-
const {
|
|
293213
|
+
const {
|
|
293214
|
+
normalizedCex,
|
|
293215
|
+
symbol: symbol2,
|
|
293216
|
+
selectedBrokerAccount,
|
|
293217
|
+
broker,
|
|
293218
|
+
brokerArchiver
|
|
293219
|
+
} = ctx;
|
|
292895
293220
|
if (!symbol2) {
|
|
292896
293221
|
return ctx.wrappedCallback({
|
|
292897
293222
|
code: grpc3.status.INVALID_ARGUMENT,
|
|
@@ -292954,6 +293279,32 @@ async function handleDeposit(ctx) {
|
|
|
292954
293279
|
}, null);
|
|
292955
293280
|
}
|
|
292956
293281
|
const status4 = normalizeDepositStatus(depositField(deposit, ["status", "state"]));
|
|
293282
|
+
const depositTxid = String(depositField(deposit, ["txid", "txId", "tx_hash", "txHash"]) ?? value.transactionHash);
|
|
293283
|
+
const creditedAt = depositField(deposit, [
|
|
293284
|
+
"creditedAt",
|
|
293285
|
+
"credited_at",
|
|
293286
|
+
"updated",
|
|
293287
|
+
"updatedAt",
|
|
293288
|
+
"timestamp",
|
|
293289
|
+
"datetime"
|
|
293290
|
+
]);
|
|
293291
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
293292
|
+
exchange: normalizedCex,
|
|
293293
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
293294
|
+
assetSymbol: symbol2,
|
|
293295
|
+
transfer: {
|
|
293296
|
+
eventKind: "deposit",
|
|
293297
|
+
lifecycleAction: "observe_deposit",
|
|
293298
|
+
status: status4,
|
|
293299
|
+
amount: observedAmount !== undefined ? String(observedAmount) : undefined,
|
|
293300
|
+
address: String(observedAddress ?? value.recipientAddress),
|
|
293301
|
+
network: depositNetwork?.exchangeNetworkId,
|
|
293302
|
+
externalId: depositTxid,
|
|
293303
|
+
txid: depositTxid,
|
|
293304
|
+
exchangeTimestamp: typeof creditedAt === "string" ? creditedAt : undefined,
|
|
293305
|
+
payload: deposit
|
|
293306
|
+
}
|
|
293307
|
+
});
|
|
292957
293308
|
log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
|
|
292958
293309
|
return ctx.wrappedCallback(null, {
|
|
292959
293310
|
proof: ctx.verity.proof,
|
|
@@ -293367,7 +293718,8 @@ async function handleInternalTransfer(ctx) {
|
|
|
293367
293718
|
symbol: symbol2,
|
|
293368
293719
|
verity,
|
|
293369
293720
|
useVerity,
|
|
293370
|
-
verityProverUrl
|
|
293721
|
+
verityProverUrl,
|
|
293722
|
+
brokerArchiver
|
|
293371
293723
|
} = ctx;
|
|
293372
293724
|
if (!symbol2) {
|
|
293373
293725
|
return ctx.wrappedCallback({
|
|
@@ -293415,6 +293767,21 @@ async function handleInternalTransfer(ctx) {
|
|
|
293415
293767
|
}), verityHttpClientOverridePredicate);
|
|
293416
293768
|
}
|
|
293417
293769
|
const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
|
|
293770
|
+
const normalized = normalizeCcxtTransactionForArchive(result);
|
|
293771
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
293772
|
+
exchange: normalizedCex,
|
|
293773
|
+
accountSelector: fromSelector,
|
|
293774
|
+
assetSymbol: symbol2,
|
|
293775
|
+
transfer: {
|
|
293776
|
+
eventKind: "internal_transfer",
|
|
293777
|
+
lifecycleAction: "submit_internal_transfer",
|
|
293778
|
+
status: normalized.status ?? "ok",
|
|
293779
|
+
amount: normalized.amount ?? String(transferPayload.amount),
|
|
293780
|
+
network: "internal",
|
|
293781
|
+
externalId: normalized.externalId,
|
|
293782
|
+
payload: { from: fromSelector, to: toSelector, result }
|
|
293783
|
+
}
|
|
293784
|
+
});
|
|
293418
293785
|
ctx.wrappedCallback(null, {
|
|
293419
293786
|
proof: verity.proof,
|
|
293420
293787
|
result: JSON.stringify(result)
|
|
@@ -293462,7 +293829,8 @@ async function handleCreateOrder(ctx) {
|
|
|
293462
293829
|
useVerity,
|
|
293463
293830
|
verityProverUrl,
|
|
293464
293831
|
otelMetrics,
|
|
293465
|
-
brokerArchiver
|
|
293832
|
+
brokerArchiver,
|
|
293833
|
+
orderActivityTracker
|
|
293466
293834
|
} = ctx;
|
|
293467
293835
|
const verityProof = verity.proof;
|
|
293468
293836
|
const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
|
|
@@ -293488,6 +293856,9 @@ async function handleCreateOrder(ctx) {
|
|
|
293488
293856
|
side: resolution.side,
|
|
293489
293857
|
requestedQuantity: resolution.amountBase ?? orderValue.amount
|
|
293490
293858
|
};
|
|
293859
|
+
if (selectedBrokerAccount?.label) {
|
|
293860
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
|
|
293861
|
+
}
|
|
293491
293862
|
const telemetryIds = extractOrderTelemetryIds(orderValue.params);
|
|
293492
293863
|
const submissionTimestamp = new Date().toISOString();
|
|
293493
293864
|
const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
|
|
@@ -293552,7 +293923,8 @@ async function handleGetOrderDetails(ctx) {
|
|
|
293552
293923
|
useVerity,
|
|
293553
293924
|
verityProverUrl,
|
|
293554
293925
|
otelMetrics,
|
|
293555
|
-
brokerArchiver
|
|
293926
|
+
brokerArchiver,
|
|
293927
|
+
orderActivityTracker
|
|
293556
293928
|
} = ctx;
|
|
293557
293929
|
const verityProof = verity.proof;
|
|
293558
293930
|
const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
|
|
@@ -293566,6 +293938,9 @@ async function handleGetOrderDetails(ctx) {
|
|
|
293566
293938
|
}, null);
|
|
293567
293939
|
}
|
|
293568
293940
|
const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
|
|
293941
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
293942
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
293943
|
+
}
|
|
293569
293944
|
const getOrderContext = {
|
|
293570
293945
|
action: "GetOrderDetails",
|
|
293571
293946
|
cex: cex3,
|
|
@@ -293621,7 +293996,8 @@ async function handleCancelOrder(ctx) {
|
|
|
293621
293996
|
useVerity,
|
|
293622
293997
|
verityProverUrl,
|
|
293623
293998
|
otelMetrics,
|
|
293624
|
-
brokerArchiver
|
|
293999
|
+
brokerArchiver,
|
|
294000
|
+
orderActivityTracker
|
|
293625
294001
|
} = ctx;
|
|
293626
294002
|
const verityProof = verity.proof;
|
|
293627
294003
|
const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
|
|
@@ -293642,6 +294018,9 @@ async function handleCancelOrder(ctx) {
|
|
|
293642
294018
|
...extractOrderTelemetryIds(cancelOrderValue.params)
|
|
293643
294019
|
};
|
|
293644
294020
|
const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
|
|
294021
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
294022
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
294023
|
+
}
|
|
293645
294024
|
emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
|
|
293646
294025
|
archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
|
|
293647
294026
|
ctx.wrappedCallback(null, {
|
|
@@ -294272,7 +294651,8 @@ async function handleWithdraw(ctx) {
|
|
|
294272
294651
|
applyVerityToBroker,
|
|
294273
294652
|
useVerity,
|
|
294274
294653
|
verityProverUrl,
|
|
294275
|
-
otelMetrics
|
|
294654
|
+
otelMetrics,
|
|
294655
|
+
brokerArchiver
|
|
294276
294656
|
} = ctx;
|
|
294277
294657
|
const verityProof = verity.proof;
|
|
294278
294658
|
if (!symbol2) {
|
|
@@ -294321,6 +294701,26 @@ async function handleWithdraw(ctx) {
|
|
|
294321
294701
|
network: withdrawNetwork.exchangeNetworkId
|
|
294322
294702
|
});
|
|
294323
294703
|
log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
|
|
294704
|
+
const normalized = normalizeCcxtTransactionForArchive(transaction);
|
|
294705
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
294706
|
+
exchange: cex3,
|
|
294707
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
294708
|
+
assetSymbol: normalized.assetSymbol ?? symbol2,
|
|
294709
|
+
transfer: {
|
|
294710
|
+
eventKind: "withdrawal",
|
|
294711
|
+
lifecycleAction: "submit_withdrawal",
|
|
294712
|
+
status: normalized.status,
|
|
294713
|
+
amount: normalized.amount ?? String(transferValue.amount),
|
|
294714
|
+
address: normalized.address ?? transferValue.recipientAddress,
|
|
294715
|
+
network: normalized.network ?? withdrawNetwork.exchangeNetworkId,
|
|
294716
|
+
externalId: normalized.externalId,
|
|
294717
|
+
txid: normalized.txid,
|
|
294718
|
+
feeAmount: normalized.feeAmount,
|
|
294719
|
+
feeCurrency: normalized.feeCurrency,
|
|
294720
|
+
exchangeTimestamp: normalized.exchangeTimestamp,
|
|
294721
|
+
payload: transaction
|
|
294722
|
+
}
|
|
294723
|
+
});
|
|
294324
294724
|
ctx.wrappedCallback(null, {
|
|
294325
294725
|
proof: ctx.verity.proof,
|
|
294326
294726
|
result: JSON.stringify({
|
|
@@ -294332,6 +294732,21 @@ async function handleWithdraw(ctx) {
|
|
|
294332
294732
|
});
|
|
294333
294733
|
} catch (error48) {
|
|
294334
294734
|
safeLogError("Withdraw failed", error48);
|
|
294735
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
294736
|
+
exchange: cex3,
|
|
294737
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
294738
|
+
assetSymbol: symbol2,
|
|
294739
|
+
transfer: {
|
|
294740
|
+
eventKind: "withdrawal",
|
|
294741
|
+
lifecycleAction: "submit_withdrawal",
|
|
294742
|
+
status: "failed",
|
|
294743
|
+
amount: String(transferValue.amount),
|
|
294744
|
+
address: transferValue.recipientAddress,
|
|
294745
|
+
network: withdrawNetwork.exchangeNetworkId,
|
|
294746
|
+
errorSummary: getErrorMessage(error48),
|
|
294747
|
+
payload: { recipientAddress: transferValue.recipientAddress }
|
|
294748
|
+
}
|
|
294749
|
+
});
|
|
294335
294750
|
const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
|
|
294336
294751
|
ctx.wrappedCallback({
|
|
294337
294752
|
code,
|
|
@@ -294379,7 +294794,8 @@ function createExecuteActionHandler(deps) {
|
|
|
294379
294794
|
useVerity,
|
|
294380
294795
|
verityProverUrl,
|
|
294381
294796
|
otelMetrics,
|
|
294382
|
-
brokerArchiver
|
|
294797
|
+
brokerArchiver,
|
|
294798
|
+
orderActivityTracker
|
|
294383
294799
|
} = deps;
|
|
294384
294800
|
return async (call, callback) => {
|
|
294385
294801
|
const startTime = Date.now();
|
|
@@ -294458,7 +294874,8 @@ function createExecuteActionHandler(deps) {
|
|
|
294458
294874
|
useVerity,
|
|
294459
294875
|
verityProverUrl,
|
|
294460
294876
|
otelMetrics,
|
|
294461
|
-
brokerArchiver
|
|
294877
|
+
brokerArchiver,
|
|
294878
|
+
orderActivityTracker
|
|
294462
294879
|
};
|
|
294463
294880
|
if (action === Action.Call) {
|
|
294464
294881
|
const handled = await handleOrderBookCall(preludeCtx);
|
|
@@ -296048,7 +296465,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
|
|
|
296048
296465
|
// src/server.ts
|
|
296049
296466
|
var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
|
|
296050
296467
|
var cexNode = grpcObj.cex_broker;
|
|
296051
|
-
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
|
|
296468
|
+
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
|
|
296052
296469
|
const server = new grpc14.Server;
|
|
296053
296470
|
server.addService(cexNode.cex_service.service, {
|
|
296054
296471
|
ExecuteAction: createExecuteActionHandler({
|
|
@@ -296058,7 +296475,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
|
|
|
296058
296475
|
useVerity,
|
|
296059
296476
|
verityProverUrl,
|
|
296060
296477
|
otelMetrics,
|
|
296061
|
-
brokerArchiver
|
|
296478
|
+
brokerArchiver,
|
|
296479
|
+
orderActivityTracker
|
|
296062
296480
|
}),
|
|
296063
296481
|
Subscribe: createSubscribeHandler({
|
|
296064
296482
|
brokers,
|
|
@@ -296203,6 +296621,8 @@ class CEXBroker {
|
|
|
296203
296621
|
otelLogs;
|
|
296204
296622
|
brokerArchiver;
|
|
296205
296623
|
depositReconciler;
|
|
296624
|
+
orderActivityTracker = new OrderActivityTracker;
|
|
296625
|
+
fillArchivePoller;
|
|
296206
296626
|
loadEnvConfig() {
|
|
296207
296627
|
log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
|
|
296208
296628
|
const configMap = {};
|
|
@@ -296343,6 +296763,10 @@ class CEXBroker {
|
|
|
296343
296763
|
this.depositReconciler.stop();
|
|
296344
296764
|
this.depositReconciler = undefined;
|
|
296345
296765
|
}
|
|
296766
|
+
if (this.fillArchivePoller) {
|
|
296767
|
+
this.fillArchivePoller.stop();
|
|
296768
|
+
this.fillArchivePoller = undefined;
|
|
296769
|
+
}
|
|
296346
296770
|
if (this.server) {
|
|
296347
296771
|
await this.server.forceShutdown();
|
|
296348
296772
|
}
|
|
@@ -296364,11 +296788,15 @@ class CEXBroker {
|
|
|
296364
296788
|
this.depositReconciler.stop();
|
|
296365
296789
|
this.depositReconciler = undefined;
|
|
296366
296790
|
}
|
|
296791
|
+
if (this.fillArchivePoller) {
|
|
296792
|
+
this.fillArchivePoller.stop();
|
|
296793
|
+
this.fillArchivePoller = undefined;
|
|
296794
|
+
}
|
|
296367
296795
|
log.info(`Running CEXBroker at ${new Date().toISOString()}`);
|
|
296368
296796
|
if (this.otelMetrics?.isOtelEnabled()) {
|
|
296369
296797
|
await this.otelMetrics.initialize();
|
|
296370
296798
|
}
|
|
296371
|
-
this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver);
|
|
296799
|
+
this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
|
|
296372
296800
|
this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
|
|
296373
296801
|
if (err2) {
|
|
296374
296802
|
log.error(err2);
|
|
@@ -296383,6 +296811,15 @@ class CEXBroker {
|
|
|
296383
296811
|
metrics: this.otelMetrics
|
|
296384
296812
|
});
|
|
296385
296813
|
this.depositReconciler.start();
|
|
296814
|
+
if (this.brokerArchiver?.isEnabled()) {
|
|
296815
|
+
this.fillArchivePoller = new FillArchivePoller({
|
|
296816
|
+
brokers: this.brokers,
|
|
296817
|
+
archiver: this.brokerArchiver,
|
|
296818
|
+
tracker: this.orderActivityTracker,
|
|
296819
|
+
metrics: this.otelMetrics
|
|
296820
|
+
});
|
|
296821
|
+
this.fillArchivePoller.start();
|
|
296822
|
+
}
|
|
296386
296823
|
return this;
|
|
296387
296824
|
}
|
|
296388
296825
|
}
|
|
@@ -296390,4 +296827,4 @@ export {
|
|
|
296390
296827
|
CEXBroker as default
|
|
296391
296828
|
};
|
|
296392
296829
|
|
|
296393
|
-
//# debugId=
|
|
296830
|
+
//# debugId=72B06323144A22CC64756E2164756E21
|