@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/commands/cli.js
CHANGED
|
@@ -315278,6 +315278,8 @@ function hashMarketMetadata(payload) {
|
|
|
315278
315278
|
|
|
315279
315279
|
// src/helpers/broker-execution-archive/types.ts
|
|
315280
315280
|
var BROKER_WRITE_SOURCE = "broker_write";
|
|
315281
|
+
var ARCHIVE_SCHEMA_VERSION = "1";
|
|
315282
|
+
var FILL_EVENT_KIND = "trade_history_fill";
|
|
315281
315283
|
|
|
315282
315284
|
// src/helpers/broker-execution-archive/rows.ts
|
|
315283
315285
|
function firstString2(...values2) {
|
|
@@ -315291,6 +315293,26 @@ function firstString2(...values2) {
|
|
|
315291
315293
|
}
|
|
315292
315294
|
return;
|
|
315293
315295
|
}
|
|
315296
|
+
function firstNumber2(...values2) {
|
|
315297
|
+
for (const value of values2) {
|
|
315298
|
+
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
315299
|
+
if (Number.isFinite(numeric)) {
|
|
315300
|
+
return numeric;
|
|
315301
|
+
}
|
|
315302
|
+
}
|
|
315303
|
+
return;
|
|
315304
|
+
}
|
|
315305
|
+
function normalizeTimestamp2(value) {
|
|
315306
|
+
if (typeof value === "string" && value.trim()) {
|
|
315307
|
+
return value.trim();
|
|
315308
|
+
}
|
|
315309
|
+
const ms = firstNumber2(value);
|
|
315310
|
+
if (ms === undefined) {
|
|
315311
|
+
return;
|
|
315312
|
+
}
|
|
315313
|
+
const date = new Date(ms);
|
|
315314
|
+
return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
|
|
315315
|
+
}
|
|
315294
315316
|
function compactUndefined2(record) {
|
|
315295
315317
|
return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
315296
315318
|
}
|
|
@@ -315355,6 +315377,99 @@ function buildSubscribeStreamArchiveRow(input) {
|
|
|
315355
315377
|
})
|
|
315356
315378
|
};
|
|
315357
315379
|
}
|
|
315380
|
+
function quantityString(...values2) {
|
|
315381
|
+
return firstString2(...values2);
|
|
315382
|
+
}
|
|
315383
|
+
function buildTransferEventArchiveRow(input) {
|
|
315384
|
+
const { tags, transfer } = input;
|
|
315385
|
+
return {
|
|
315386
|
+
table: "broker_execution.transfer_events",
|
|
315387
|
+
row: compactUndefined2({
|
|
315388
|
+
...tags,
|
|
315389
|
+
schema_version: ARCHIVE_SCHEMA_VERSION,
|
|
315390
|
+
event_kind: transfer.eventKind,
|
|
315391
|
+
lifecycle_action: transfer.lifecycleAction,
|
|
315392
|
+
status: transfer.status ?? "",
|
|
315393
|
+
asset_symbol: tags.symbol,
|
|
315394
|
+
amount: transfer.amount,
|
|
315395
|
+
address: transfer.address,
|
|
315396
|
+
network: transfer.network,
|
|
315397
|
+
external_id: transfer.externalId ?? "",
|
|
315398
|
+
txid: transfer.txid,
|
|
315399
|
+
result_index: transfer.resultIndex ?? 0,
|
|
315400
|
+
fee_amount: transfer.feeAmount,
|
|
315401
|
+
fee_currency: transfer.feeCurrency,
|
|
315402
|
+
exchange_timestamp: transfer.exchangeTimestamp,
|
|
315403
|
+
error_summary: transfer.errorSummary,
|
|
315404
|
+
payload_json: JSON.stringify(transfer.payload)
|
|
315405
|
+
})
|
|
315406
|
+
};
|
|
315407
|
+
}
|
|
315408
|
+
function normalizeCcxtTransactionForArchive(transaction) {
|
|
315409
|
+
const record = asRecord(transaction);
|
|
315410
|
+
const info = asRecord(record?.info);
|
|
315411
|
+
const fee = asRecord(record?.fee);
|
|
315412
|
+
return compactUndefined2({
|
|
315413
|
+
externalId: firstString2(record?.id, info?.id, record?.txid, info?.txId),
|
|
315414
|
+
txid: firstString2(record?.txid, info?.txId, info?.txid, info?.tx_hash),
|
|
315415
|
+
address: firstString2(record?.address, record?.addressTo, info?.address),
|
|
315416
|
+
network: firstString2(record?.network, info?.network),
|
|
315417
|
+
amount: quantityString(info?.amount, record?.amount),
|
|
315418
|
+
assetSymbol: firstString2(record?.currency, info?.coin, info?.asset),
|
|
315419
|
+
status: firstString2(record?.status, info?.status)?.toLowerCase(),
|
|
315420
|
+
feeAmount: quantityString(fee?.cost, record?.feeCost),
|
|
315421
|
+
feeCurrency: firstString2(fee?.currency, record?.feeCurrency),
|
|
315422
|
+
exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.applyTime))
|
|
315423
|
+
});
|
|
315424
|
+
}
|
|
315425
|
+
function firstValueForTransfer(...values2) {
|
|
315426
|
+
return values2.find((value) => value !== undefined && value !== null);
|
|
315427
|
+
}
|
|
315428
|
+
function buildFillEventArchiveRow(input) {
|
|
315429
|
+
const { tags, fill } = input;
|
|
315430
|
+
return {
|
|
315431
|
+
table: "broker_execution.fill_events",
|
|
315432
|
+
row: compactUndefined2({
|
|
315433
|
+
...tags,
|
|
315434
|
+
schema_version: ARCHIVE_SCHEMA_VERSION,
|
|
315435
|
+
event_kind: FILL_EVENT_KIND,
|
|
315436
|
+
order_id: fill.orderId ?? "",
|
|
315437
|
+
client_order_id: fill.clientOrderId,
|
|
315438
|
+
fill_id: fill.fillId,
|
|
315439
|
+
fill_index: fill.fillIndex ?? 0,
|
|
315440
|
+
side: fill.side,
|
|
315441
|
+
order_type: fill.orderType,
|
|
315442
|
+
price: fill.price,
|
|
315443
|
+
base_quantity: fill.baseQuantity,
|
|
315444
|
+
quote_quantity: fill.quoteQuantity,
|
|
315445
|
+
fee_amount: fill.feeAmount,
|
|
315446
|
+
fee_currency: fill.feeCurrency,
|
|
315447
|
+
fee_rate: fill.feeRate,
|
|
315448
|
+
exchange_timestamp: fill.exchangeTimestamp,
|
|
315449
|
+
payload_json: JSON.stringify(fill.payload)
|
|
315450
|
+
})
|
|
315451
|
+
};
|
|
315452
|
+
}
|
|
315453
|
+
function normalizeCcxtTradeForArchive(trade) {
|
|
315454
|
+
const record = asRecord(trade);
|
|
315455
|
+
const info = asRecord(record?.info);
|
|
315456
|
+
const fee = asRecord(record?.fee);
|
|
315457
|
+
return {
|
|
315458
|
+
orderId: firstString2(record?.order, info?.orderId, info?.orderID),
|
|
315459
|
+
clientOrderId: firstString2(record?.clientOrderId, info?.clientOrderId, info?.origClientOrderId),
|
|
315460
|
+
fillId: firstString2(record?.id, info?.id, info?.tradeId),
|
|
315461
|
+
side: firstString2(record?.side, info?.side)?.toLowerCase(),
|
|
315462
|
+
orderType: firstString2(record?.type, info?.type)?.toLowerCase(),
|
|
315463
|
+
price: quantityString(info?.price, record?.price),
|
|
315464
|
+
baseQuantity: quantityString(info?.qty, record?.amount),
|
|
315465
|
+
quoteQuantity: quantityString(info?.quoteQty, record?.cost),
|
|
315466
|
+
feeAmount: quantityString(fee?.cost, info?.commission),
|
|
315467
|
+
feeCurrency: firstString2(fee?.currency, info?.commissionAsset),
|
|
315468
|
+
feeRate: quantityString(fee?.rate),
|
|
315469
|
+
exchangeTimestamp: normalizeTimestamp2(firstValueForTransfer(record?.timestamp, record?.datetime, info?.time)),
|
|
315470
|
+
payload: trade
|
|
315471
|
+
};
|
|
315472
|
+
}
|
|
315358
315473
|
function buildMarketMetadataSnapshotRow(input) {
|
|
315359
315474
|
const redactedSnapshot = redactStreamPayload(input.marketSnapshot);
|
|
315360
315475
|
const metadataHash = hashMarketMetadata(redactedSnapshot);
|
|
@@ -315423,6 +315538,25 @@ function archiveSubscribeStreamInBackground(archiver, input) {
|
|
|
315423
315538
|
}
|
|
315424
315539
|
});
|
|
315425
315540
|
}
|
|
315541
|
+
function archiveTransferEventInBackground(archiver, input) {
|
|
315542
|
+
if (!archiver?.isEnabled()) {
|
|
315543
|
+
return;
|
|
315544
|
+
}
|
|
315545
|
+
queueMicrotask(() => {
|
|
315546
|
+
try {
|
|
315547
|
+
const tags = buildCommonArchiveTags({
|
|
315548
|
+
deploymentId: archiver.getDeploymentId(),
|
|
315549
|
+
accountSelector: input.accountSelector,
|
|
315550
|
+
exchange: input.exchange,
|
|
315551
|
+
symbol: input.assetSymbol,
|
|
315552
|
+
brokerObservedTimestamp: input.brokerObservedTimestamp
|
|
315553
|
+
});
|
|
315554
|
+
archiver.enqueue(buildTransferEventArchiveRow({ tags, transfer: input.transfer }));
|
|
315555
|
+
} catch (archiveError) {
|
|
315556
|
+
log.warn("Failed to archive transfer event", { error: archiveError });
|
|
315557
|
+
}
|
|
315558
|
+
});
|
|
315559
|
+
}
|
|
315426
315560
|
async function captureMarketMetadataSnapshot(archiver, broker, input) {
|
|
315427
315561
|
if (!archiver?.canPersistMarketMetadataSnapshot()) {
|
|
315428
315562
|
return;
|
|
@@ -315470,6 +315604,8 @@ async function captureMarketMetadataSnapshot(archiver, broker, input) {
|
|
|
315470
315604
|
}
|
|
315471
315605
|
// src/helpers/broker-execution-archive/writer.ts
|
|
315472
315606
|
var import_api_logs2 = __toESM(require_src7(), 1);
|
|
315607
|
+
import { request as httpRequest2 } from "node:http";
|
|
315608
|
+
import { request as httpsRequest2 } from "node:https";
|
|
315473
315609
|
|
|
315474
315610
|
// src/helpers/market-data-archive/types.ts
|
|
315475
315611
|
var MARKET_ARCHIVE_TABLES = new Set([
|
|
@@ -315676,11 +315812,27 @@ class BrokerExecutionArchiver {
|
|
|
315676
315812
|
}
|
|
315677
315813
|
}
|
|
315678
315814
|
this.stats.flushed += batch.length;
|
|
315815
|
+
this.recordFlushHealth(batch);
|
|
315679
315816
|
return true;
|
|
315680
315817
|
}
|
|
315681
|
-
|
|
315818
|
+
recordFlushHealth(batch) {
|
|
315819
|
+
const countByTable = new Map;
|
|
315820
|
+
for (const entry of batch) {
|
|
315821
|
+
countByTable.set(entry.table, (countByTable.get(entry.table) ?? 0) + 1);
|
|
315822
|
+
}
|
|
315823
|
+
for (const [table, count2] of countByTable) {
|
|
315824
|
+
this.recordArchiveMetric("cex_archive_rows_flushed_total", { table }, count2);
|
|
315825
|
+
}
|
|
315826
|
+
this.recordArchiveGauge("cex_archive_last_flush_success", Math.floor(Date.now() / 1000));
|
|
315827
|
+
}
|
|
315828
|
+
async recordArchiveMetric(metricName, labels, value = 1) {
|
|
315829
|
+
try {
|
|
315830
|
+
await this.otelMetrics?.recordCounter(metricName, value, labels);
|
|
315831
|
+
} catch {}
|
|
315832
|
+
}
|
|
315833
|
+
async recordArchiveGauge(metricName, value) {
|
|
315682
315834
|
try {
|
|
315683
|
-
await this.otelMetrics?.
|
|
315835
|
+
await this.otelMetrics?.recordGauge(metricName, value, {});
|
|
315684
315836
|
} catch {}
|
|
315685
315837
|
}
|
|
315686
315838
|
emitOtelLog(entry) {
|
|
@@ -315698,35 +315850,47 @@ class BrokerExecutionArchiver {
|
|
|
315698
315850
|
log.warn("Broker execution archive OTLP emit failed", { error });
|
|
315699
315851
|
}
|
|
315700
315852
|
}
|
|
315701
|
-
|
|
315853
|
+
postToForwarder(batch) {
|
|
315702
315854
|
if (!this.forwarderUrl || batch.length === 0) {
|
|
315703
|
-
return;
|
|
315855
|
+
return Promise.resolve();
|
|
315704
315856
|
}
|
|
315705
|
-
const
|
|
315706
|
-
|
|
315857
|
+
const body = JSON.stringify({
|
|
315858
|
+
source: "broker_write",
|
|
315859
|
+
deployment_id: this.deploymentId,
|
|
315860
|
+
rows: batch
|
|
315861
|
+
});
|
|
315862
|
+
const url2 = new URL(this.forwarderUrl);
|
|
315863
|
+
const doRequest = url2.protocol === "http:" ? httpRequest2 : httpsRequest2;
|
|
315707
315864
|
const headers = {
|
|
315708
|
-
"content-type": "application/json"
|
|
315865
|
+
"content-type": "application/json",
|
|
315866
|
+
"content-length": Buffer.byteLength(body)
|
|
315709
315867
|
};
|
|
315710
315868
|
if (this.forwarderAuthToken) {
|
|
315711
315869
|
headers.authorization = `Bearer ${this.forwarderAuthToken}`;
|
|
315712
315870
|
}
|
|
315713
|
-
|
|
315714
|
-
const
|
|
315871
|
+
return new Promise((resolve, reject) => {
|
|
315872
|
+
const req = doRequest(url2, {
|
|
315715
315873
|
method: "POST",
|
|
315716
315874
|
headers,
|
|
315717
|
-
|
|
315718
|
-
|
|
315719
|
-
|
|
315720
|
-
|
|
315721
|
-
|
|
315722
|
-
|
|
315875
|
+
timeout: this.forwarderTimeoutMs
|
|
315876
|
+
}, (res) => {
|
|
315877
|
+
res.on("data", () => {});
|
|
315878
|
+
res.on("end", () => {
|
|
315879
|
+
const status = res.statusCode ?? 0;
|
|
315880
|
+
if (status < 200 || status >= 300) {
|
|
315881
|
+
reject(new Error(`Archive forwarder returned ${status} ${res.statusMessage ?? ""}`));
|
|
315882
|
+
return;
|
|
315883
|
+
}
|
|
315884
|
+
resolve();
|
|
315885
|
+
});
|
|
315723
315886
|
});
|
|
315724
|
-
|
|
315725
|
-
|
|
315726
|
-
|
|
315727
|
-
|
|
315728
|
-
|
|
315729
|
-
|
|
315887
|
+
req.on("error", reject);
|
|
315888
|
+
req.on("timeout", () => {
|
|
315889
|
+
req.destroy(new Error("Archive forwarder request timed out"));
|
|
315890
|
+
});
|
|
315891
|
+
req.write(body);
|
|
315892
|
+
req.end();
|
|
315893
|
+
});
|
|
315730
315894
|
}
|
|
315731
315895
|
}
|
|
315732
315896
|
function flattenArchiveAttributes(entry) {
|
|
@@ -315765,6 +315929,159 @@ function parsePositiveInt(value, fallback) {
|
|
|
315765
315929
|
const parsed = Number.parseInt(value, 10);
|
|
315766
315930
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
315767
315931
|
}
|
|
315932
|
+
// src/helpers/fill-archive-poller.ts
|
|
315933
|
+
var DEFAULT_CONFIG = {
|
|
315934
|
+
pollIntervalMs: 60000,
|
|
315935
|
+
lookbackMs: 24 * 60 * 60 * 1000,
|
|
315936
|
+
tradesLimit: 500
|
|
315937
|
+
};
|
|
315938
|
+
function nextFillCursor(trades, currentSince) {
|
|
315939
|
+
let next = currentSince;
|
|
315940
|
+
for (const trade of trades) {
|
|
315941
|
+
const ts = asRecord(trade)?.timestamp;
|
|
315942
|
+
if (typeof ts === "number" && Number.isFinite(ts) && ts + 1 > next) {
|
|
315943
|
+
next = ts + 1;
|
|
315944
|
+
}
|
|
315945
|
+
}
|
|
315946
|
+
return next;
|
|
315947
|
+
}
|
|
315948
|
+
|
|
315949
|
+
class FillArchivePoller {
|
|
315950
|
+
params;
|
|
315951
|
+
#timer = null;
|
|
315952
|
+
#stopped = false;
|
|
315953
|
+
#running = false;
|
|
315954
|
+
#cursors = new Map;
|
|
315955
|
+
#config;
|
|
315956
|
+
constructor(params) {
|
|
315957
|
+
this.params = params;
|
|
315958
|
+
this.#config = { ...DEFAULT_CONFIG, ...params.config };
|
|
315959
|
+
}
|
|
315960
|
+
start() {
|
|
315961
|
+
if (this.#timer || this.#stopped) {
|
|
315962
|
+
return;
|
|
315963
|
+
}
|
|
315964
|
+
if (!this.params.archiver.isEnabled()) {
|
|
315965
|
+
return;
|
|
315966
|
+
}
|
|
315967
|
+
log.info("\uD83E\uDDFE Fill archive poller started");
|
|
315968
|
+
this.#timer = setTimeout(() => void this.#tick(), 0);
|
|
315969
|
+
this.#timer.unref?.();
|
|
315970
|
+
}
|
|
315971
|
+
stop() {
|
|
315972
|
+
this.#stopped = true;
|
|
315973
|
+
if (this.#timer) {
|
|
315974
|
+
clearTimeout(this.#timer);
|
|
315975
|
+
this.#timer = null;
|
|
315976
|
+
}
|
|
315977
|
+
}
|
|
315978
|
+
async#tick() {
|
|
315979
|
+
if (this.#stopped || this.#running) {
|
|
315980
|
+
return;
|
|
315981
|
+
}
|
|
315982
|
+
this.#running = true;
|
|
315983
|
+
try {
|
|
315984
|
+
await this.pollTrackedOnce();
|
|
315985
|
+
} catch (error) {
|
|
315986
|
+
log.error("Fill archive poller tick failed", error);
|
|
315987
|
+
} finally {
|
|
315988
|
+
this.#running = false;
|
|
315989
|
+
if (!this.#stopped) {
|
|
315990
|
+
this.#timer = setTimeout(() => void this.#tick(), this.#config.pollIntervalMs);
|
|
315991
|
+
this.#timer.unref?.();
|
|
315992
|
+
}
|
|
315993
|
+
}
|
|
315994
|
+
}
|
|
315995
|
+
async pollTrackedOnce() {
|
|
315996
|
+
for (const entry of this.params.tracker.list()) {
|
|
315997
|
+
if (this.#stopped) {
|
|
315998
|
+
break;
|
|
315999
|
+
}
|
|
316000
|
+
await this.#pollOne(entry.exchangeId, entry.accountLabel, entry.symbol);
|
|
316001
|
+
}
|
|
316002
|
+
}
|
|
316003
|
+
async#pollOne(exchangeId, accountLabel, symbol) {
|
|
316004
|
+
const account = resolveBrokerAccount(this.params.brokers[exchangeId], accountLabel);
|
|
316005
|
+
if (!account) {
|
|
316006
|
+
return;
|
|
316007
|
+
}
|
|
316008
|
+
const exchange = account.exchange;
|
|
316009
|
+
if (typeof exchange.fetchMyTrades !== "function" || exchange.has?.fetchMyTrades === false) {
|
|
316010
|
+
return;
|
|
316011
|
+
}
|
|
316012
|
+
const key = `${exchangeId}|${accountLabel}|${symbol}`;
|
|
316013
|
+
const since = this.#cursors.get(key) ?? Date.now() - this.#config.lookbackMs;
|
|
316014
|
+
let trades;
|
|
316015
|
+
try {
|
|
316016
|
+
trades = await exchange.fetchMyTrades(symbol, since, this.#config.tradesLimit);
|
|
316017
|
+
} catch (error) {
|
|
316018
|
+
this.params.metrics?.recordCounter("cex_fill_poller_errors_total", 1, {
|
|
316019
|
+
exchange: exchangeId
|
|
316020
|
+
});
|
|
316021
|
+
log.warn("Fill archive poll failed", {
|
|
316022
|
+
exchange: exchangeId,
|
|
316023
|
+
account: accountLabel,
|
|
316024
|
+
symbol,
|
|
316025
|
+
error
|
|
316026
|
+
});
|
|
316027
|
+
return;
|
|
316028
|
+
}
|
|
316029
|
+
if (!Array.isArray(trades) || trades.length === 0) {
|
|
316030
|
+
return;
|
|
316031
|
+
}
|
|
316032
|
+
trades.forEach((trade, fillIndex) => {
|
|
316033
|
+
const tags = buildCommonArchiveTags({
|
|
316034
|
+
deploymentId: this.params.archiver.getDeploymentId(),
|
|
316035
|
+
accountSelector: accountLabel,
|
|
316036
|
+
exchange: exchangeId,
|
|
316037
|
+
symbol
|
|
316038
|
+
});
|
|
316039
|
+
this.params.archiver.enqueue(buildFillEventArchiveRow({
|
|
316040
|
+
tags,
|
|
316041
|
+
fill: { ...normalizeCcxtTradeForArchive(trade), fillIndex }
|
|
316042
|
+
}));
|
|
316043
|
+
});
|
|
316044
|
+
this.params.metrics?.recordCounter("cex_fill_poller_trades_archived_total", trades.length, { exchange: exchangeId });
|
|
316045
|
+
this.#cursors.set(key, nextFillCursor(trades, since));
|
|
316046
|
+
}
|
|
316047
|
+
}
|
|
316048
|
+
|
|
316049
|
+
// src/helpers/order-activity-tracker.ts
|
|
316050
|
+
var DEFAULT_MAX_AGE_MS = 6 * 60 * 60 * 1000;
|
|
316051
|
+
|
|
316052
|
+
class OrderActivityTracker {
|
|
316053
|
+
#entries = new Map;
|
|
316054
|
+
#maxAgeMs;
|
|
316055
|
+
constructor(options) {
|
|
316056
|
+
this.#maxAgeMs = options?.maxAgeMs ?? DEFAULT_MAX_AGE_MS;
|
|
316057
|
+
}
|
|
316058
|
+
record(exchangeId, accountLabel, symbol, now3 = Date.now()) {
|
|
316059
|
+
const exchange = exchangeId.trim().toLowerCase();
|
|
316060
|
+
const trimmedSymbol = symbol.trim();
|
|
316061
|
+
if (!exchange || !accountLabel.trim() || !trimmedSymbol) {
|
|
316062
|
+
return;
|
|
316063
|
+
}
|
|
316064
|
+
const key = `${exchange}|${accountLabel}|${trimmedSymbol}`;
|
|
316065
|
+
this.#entries.set(key, {
|
|
316066
|
+
exchangeId: exchange,
|
|
316067
|
+
accountLabel,
|
|
316068
|
+
symbol: trimmedSymbol,
|
|
316069
|
+
lastActivityAt: now3
|
|
316070
|
+
});
|
|
316071
|
+
}
|
|
316072
|
+
list(now3 = Date.now()) {
|
|
316073
|
+
const active = [];
|
|
316074
|
+
for (const [key, entry] of this.#entries) {
|
|
316075
|
+
if (now3 - entry.lastActivityAt > this.#maxAgeMs) {
|
|
316076
|
+
this.#entries.delete(key);
|
|
316077
|
+
continue;
|
|
316078
|
+
}
|
|
316079
|
+
active.push(entry);
|
|
316080
|
+
}
|
|
316081
|
+
return active;
|
|
316082
|
+
}
|
|
316083
|
+
}
|
|
316084
|
+
|
|
315768
316085
|
// src/helpers/otel.ts
|
|
315769
316086
|
var import_api2 = __toESM(require_src5(), 1);
|
|
315770
316087
|
var import_api_logs3 = __toESM(require_src7(), 1);
|
|
@@ -329941,7 +330258,13 @@ function rejectWithGrpcError(ctx, error48, options) {
|
|
|
329941
330258
|
|
|
329942
330259
|
// src/handlers/execute-action/deposit.ts
|
|
329943
330260
|
async function handleDeposit(ctx) {
|
|
329944
|
-
const {
|
|
330261
|
+
const {
|
|
330262
|
+
normalizedCex,
|
|
330263
|
+
symbol: symbol2,
|
|
330264
|
+
selectedBrokerAccount,
|
|
330265
|
+
broker,
|
|
330266
|
+
brokerArchiver
|
|
330267
|
+
} = ctx;
|
|
329945
330268
|
if (!symbol2) {
|
|
329946
330269
|
return ctx.wrappedCallback({
|
|
329947
330270
|
code: grpc3.status.INVALID_ARGUMENT,
|
|
@@ -330004,6 +330327,32 @@ async function handleDeposit(ctx) {
|
|
|
330004
330327
|
}, null);
|
|
330005
330328
|
}
|
|
330006
330329
|
const status4 = normalizeDepositStatus(depositField(deposit, ["status", "state"]));
|
|
330330
|
+
const depositTxid = String(depositField(deposit, ["txid", "txId", "tx_hash", "txHash"]) ?? value.transactionHash);
|
|
330331
|
+
const creditedAt = depositField(deposit, [
|
|
330332
|
+
"creditedAt",
|
|
330333
|
+
"credited_at",
|
|
330334
|
+
"updated",
|
|
330335
|
+
"updatedAt",
|
|
330336
|
+
"timestamp",
|
|
330337
|
+
"datetime"
|
|
330338
|
+
]);
|
|
330339
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
330340
|
+
exchange: normalizedCex,
|
|
330341
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
330342
|
+
assetSymbol: symbol2,
|
|
330343
|
+
transfer: {
|
|
330344
|
+
eventKind: "deposit",
|
|
330345
|
+
lifecycleAction: "observe_deposit",
|
|
330346
|
+
status: status4,
|
|
330347
|
+
amount: observedAmount !== undefined ? String(observedAmount) : undefined,
|
|
330348
|
+
address: String(observedAddress ?? value.recipientAddress),
|
|
330349
|
+
network: depositNetwork?.exchangeNetworkId,
|
|
330350
|
+
externalId: depositTxid,
|
|
330351
|
+
txid: depositTxid,
|
|
330352
|
+
exchangeTimestamp: typeof creditedAt === "string" ? creditedAt : undefined,
|
|
330353
|
+
payload: deposit
|
|
330354
|
+
}
|
|
330355
|
+
});
|
|
330007
330356
|
log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
|
|
330008
330357
|
return ctx.wrappedCallback(null, {
|
|
330009
330358
|
proof: ctx.verity.proof,
|
|
@@ -330417,7 +330766,8 @@ async function handleInternalTransfer(ctx) {
|
|
|
330417
330766
|
symbol: symbol2,
|
|
330418
330767
|
verity,
|
|
330419
330768
|
useVerity,
|
|
330420
|
-
verityProverUrl
|
|
330769
|
+
verityProverUrl,
|
|
330770
|
+
brokerArchiver
|
|
330421
330771
|
} = ctx;
|
|
330422
330772
|
if (!symbol2) {
|
|
330423
330773
|
return ctx.wrappedCallback({
|
|
@@ -330465,6 +330815,21 @@ async function handleInternalTransfer(ctx) {
|
|
|
330465
330815
|
}), verityHttpClientOverridePredicate);
|
|
330466
330816
|
}
|
|
330467
330817
|
const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
|
|
330818
|
+
const normalized = normalizeCcxtTransactionForArchive(result);
|
|
330819
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
330820
|
+
exchange: normalizedCex,
|
|
330821
|
+
accountSelector: fromSelector,
|
|
330822
|
+
assetSymbol: symbol2,
|
|
330823
|
+
transfer: {
|
|
330824
|
+
eventKind: "internal_transfer",
|
|
330825
|
+
lifecycleAction: "submit_internal_transfer",
|
|
330826
|
+
status: normalized.status ?? "ok",
|
|
330827
|
+
amount: normalized.amount ?? String(transferPayload.amount),
|
|
330828
|
+
network: "internal",
|
|
330829
|
+
externalId: normalized.externalId,
|
|
330830
|
+
payload: { from: fromSelector, to: toSelector, result }
|
|
330831
|
+
}
|
|
330832
|
+
});
|
|
330468
330833
|
ctx.wrappedCallback(null, {
|
|
330469
330834
|
proof: verity.proof,
|
|
330470
330835
|
result: JSON.stringify(result)
|
|
@@ -330512,7 +330877,8 @@ async function handleCreateOrder(ctx) {
|
|
|
330512
330877
|
useVerity,
|
|
330513
330878
|
verityProverUrl,
|
|
330514
330879
|
otelMetrics,
|
|
330515
|
-
brokerArchiver
|
|
330880
|
+
brokerArchiver,
|
|
330881
|
+
orderActivityTracker
|
|
330516
330882
|
} = ctx;
|
|
330517
330883
|
const verityProof = verity.proof;
|
|
330518
330884
|
const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
|
|
@@ -330538,6 +330904,9 @@ async function handleCreateOrder(ctx) {
|
|
|
330538
330904
|
side: resolution.side,
|
|
330539
330905
|
requestedQuantity: resolution.amountBase ?? orderValue.amount
|
|
330540
330906
|
};
|
|
330907
|
+
if (selectedBrokerAccount?.label) {
|
|
330908
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
|
|
330909
|
+
}
|
|
330541
330910
|
const telemetryIds = extractOrderTelemetryIds(orderValue.params);
|
|
330542
330911
|
const submissionTimestamp = new Date().toISOString();
|
|
330543
330912
|
const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
|
|
@@ -330602,7 +330971,8 @@ async function handleGetOrderDetails(ctx) {
|
|
|
330602
330971
|
useVerity,
|
|
330603
330972
|
verityProverUrl,
|
|
330604
330973
|
otelMetrics,
|
|
330605
|
-
brokerArchiver
|
|
330974
|
+
brokerArchiver,
|
|
330975
|
+
orderActivityTracker
|
|
330606
330976
|
} = ctx;
|
|
330607
330977
|
const verityProof = verity.proof;
|
|
330608
330978
|
const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
|
|
@@ -330616,6 +330986,9 @@ async function handleGetOrderDetails(ctx) {
|
|
|
330616
330986
|
}, null);
|
|
330617
330987
|
}
|
|
330618
330988
|
const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
|
|
330989
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
330990
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
330991
|
+
}
|
|
330619
330992
|
const getOrderContext = {
|
|
330620
330993
|
action: "GetOrderDetails",
|
|
330621
330994
|
cex: cex3,
|
|
@@ -330671,7 +331044,8 @@ async function handleCancelOrder(ctx) {
|
|
|
330671
331044
|
useVerity,
|
|
330672
331045
|
verityProverUrl,
|
|
330673
331046
|
otelMetrics,
|
|
330674
|
-
brokerArchiver
|
|
331047
|
+
brokerArchiver,
|
|
331048
|
+
orderActivityTracker
|
|
330675
331049
|
} = ctx;
|
|
330676
331050
|
const verityProof = verity.proof;
|
|
330677
331051
|
const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
|
|
@@ -330692,6 +331066,9 @@ async function handleCancelOrder(ctx) {
|
|
|
330692
331066
|
...extractOrderTelemetryIds(cancelOrderValue.params)
|
|
330693
331067
|
};
|
|
330694
331068
|
const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
|
|
331069
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
331070
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
331071
|
+
}
|
|
330695
331072
|
emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
|
|
330696
331073
|
archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
|
|
330697
331074
|
ctx.wrappedCallback(null, {
|
|
@@ -331322,7 +331699,8 @@ async function handleWithdraw(ctx) {
|
|
|
331322
331699
|
applyVerityToBroker,
|
|
331323
331700
|
useVerity,
|
|
331324
331701
|
verityProverUrl,
|
|
331325
|
-
otelMetrics
|
|
331702
|
+
otelMetrics,
|
|
331703
|
+
brokerArchiver
|
|
331326
331704
|
} = ctx;
|
|
331327
331705
|
const verityProof = verity.proof;
|
|
331328
331706
|
if (!symbol2) {
|
|
@@ -331371,6 +331749,26 @@ async function handleWithdraw(ctx) {
|
|
|
331371
331749
|
network: withdrawNetwork.exchangeNetworkId
|
|
331372
331750
|
});
|
|
331373
331751
|
log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
|
|
331752
|
+
const normalized = normalizeCcxtTransactionForArchive(transaction);
|
|
331753
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
331754
|
+
exchange: cex3,
|
|
331755
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
331756
|
+
assetSymbol: normalized.assetSymbol ?? symbol2,
|
|
331757
|
+
transfer: {
|
|
331758
|
+
eventKind: "withdrawal",
|
|
331759
|
+
lifecycleAction: "submit_withdrawal",
|
|
331760
|
+
status: normalized.status,
|
|
331761
|
+
amount: normalized.amount ?? String(transferValue.amount),
|
|
331762
|
+
address: normalized.address ?? transferValue.recipientAddress,
|
|
331763
|
+
network: normalized.network ?? withdrawNetwork.exchangeNetworkId,
|
|
331764
|
+
externalId: normalized.externalId,
|
|
331765
|
+
txid: normalized.txid,
|
|
331766
|
+
feeAmount: normalized.feeAmount,
|
|
331767
|
+
feeCurrency: normalized.feeCurrency,
|
|
331768
|
+
exchangeTimestamp: normalized.exchangeTimestamp,
|
|
331769
|
+
payload: transaction
|
|
331770
|
+
}
|
|
331771
|
+
});
|
|
331374
331772
|
ctx.wrappedCallback(null, {
|
|
331375
331773
|
proof: ctx.verity.proof,
|
|
331376
331774
|
result: JSON.stringify({
|
|
@@ -331382,6 +331780,21 @@ async function handleWithdraw(ctx) {
|
|
|
331382
331780
|
});
|
|
331383
331781
|
} catch (error48) {
|
|
331384
331782
|
safeLogError("Withdraw failed", error48);
|
|
331783
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
331784
|
+
exchange: cex3,
|
|
331785
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
331786
|
+
assetSymbol: symbol2,
|
|
331787
|
+
transfer: {
|
|
331788
|
+
eventKind: "withdrawal",
|
|
331789
|
+
lifecycleAction: "submit_withdrawal",
|
|
331790
|
+
status: "failed",
|
|
331791
|
+
amount: String(transferValue.amount),
|
|
331792
|
+
address: transferValue.recipientAddress,
|
|
331793
|
+
network: withdrawNetwork.exchangeNetworkId,
|
|
331794
|
+
errorSummary: getErrorMessage(error48),
|
|
331795
|
+
payload: { recipientAddress: transferValue.recipientAddress }
|
|
331796
|
+
}
|
|
331797
|
+
});
|
|
331385
331798
|
const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
|
|
331386
331799
|
ctx.wrappedCallback({
|
|
331387
331800
|
code,
|
|
@@ -331429,7 +331842,8 @@ function createExecuteActionHandler(deps) {
|
|
|
331429
331842
|
useVerity,
|
|
331430
331843
|
verityProverUrl,
|
|
331431
331844
|
otelMetrics,
|
|
331432
|
-
brokerArchiver
|
|
331845
|
+
brokerArchiver,
|
|
331846
|
+
orderActivityTracker
|
|
331433
331847
|
} = deps;
|
|
331434
331848
|
return async (call, callback) => {
|
|
331435
331849
|
const startTime = Date.now();
|
|
@@ -331508,7 +331922,8 @@ function createExecuteActionHandler(deps) {
|
|
|
331508
331922
|
useVerity,
|
|
331509
331923
|
verityProverUrl,
|
|
331510
331924
|
otelMetrics,
|
|
331511
|
-
brokerArchiver
|
|
331925
|
+
brokerArchiver,
|
|
331926
|
+
orderActivityTracker
|
|
331512
331927
|
};
|
|
331513
331928
|
if (action === Action.Call) {
|
|
331514
331929
|
const handled = await handleOrderBookCall(preludeCtx);
|
|
@@ -333097,7 +333512,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
|
|
|
333097
333512
|
// src/server.ts
|
|
333098
333513
|
var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
|
|
333099
333514
|
var cexNode = grpcObj.cex_broker;
|
|
333100
|
-
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
|
|
333515
|
+
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
|
|
333101
333516
|
const server = new grpc14.Server;
|
|
333102
333517
|
server.addService(cexNode.cex_service.service, {
|
|
333103
333518
|
ExecuteAction: createExecuteActionHandler({
|
|
@@ -333107,7 +333522,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
|
|
|
333107
333522
|
useVerity,
|
|
333108
333523
|
verityProverUrl,
|
|
333109
333524
|
otelMetrics,
|
|
333110
|
-
brokerArchiver
|
|
333525
|
+
brokerArchiver,
|
|
333526
|
+
orderActivityTracker
|
|
333111
333527
|
}),
|
|
333112
333528
|
Subscribe: createSubscribeHandler({
|
|
333113
333529
|
brokers,
|
|
@@ -333252,6 +333668,8 @@ class CEXBroker {
|
|
|
333252
333668
|
otelLogs;
|
|
333253
333669
|
brokerArchiver;
|
|
333254
333670
|
depositReconciler;
|
|
333671
|
+
orderActivityTracker = new OrderActivityTracker;
|
|
333672
|
+
fillArchivePoller;
|
|
333255
333673
|
loadEnvConfig() {
|
|
333256
333674
|
log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
|
|
333257
333675
|
const configMap = {};
|
|
@@ -333392,6 +333810,10 @@ class CEXBroker {
|
|
|
333392
333810
|
this.depositReconciler.stop();
|
|
333393
333811
|
this.depositReconciler = undefined;
|
|
333394
333812
|
}
|
|
333813
|
+
if (this.fillArchivePoller) {
|
|
333814
|
+
this.fillArchivePoller.stop();
|
|
333815
|
+
this.fillArchivePoller = undefined;
|
|
333816
|
+
}
|
|
333395
333817
|
if (this.server) {
|
|
333396
333818
|
await this.server.forceShutdown();
|
|
333397
333819
|
}
|
|
@@ -333413,11 +333835,15 @@ class CEXBroker {
|
|
|
333413
333835
|
this.depositReconciler.stop();
|
|
333414
333836
|
this.depositReconciler = undefined;
|
|
333415
333837
|
}
|
|
333838
|
+
if (this.fillArchivePoller) {
|
|
333839
|
+
this.fillArchivePoller.stop();
|
|
333840
|
+
this.fillArchivePoller = undefined;
|
|
333841
|
+
}
|
|
333416
333842
|
log.info(`Running CEXBroker at ${new Date().toISOString()}`);
|
|
333417
333843
|
if (this.otelMetrics?.isOtelEnabled()) {
|
|
333418
333844
|
await this.otelMetrics.initialize();
|
|
333419
333845
|
}
|
|
333420
|
-
this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver);
|
|
333846
|
+
this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
|
|
333421
333847
|
this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
|
|
333422
333848
|
if (err2) {
|
|
333423
333849
|
log.error(err2);
|
|
@@ -333432,6 +333858,15 @@ class CEXBroker {
|
|
|
333432
333858
|
metrics: this.otelMetrics
|
|
333433
333859
|
});
|
|
333434
333860
|
this.depositReconciler.start();
|
|
333861
|
+
if (this.brokerArchiver?.isEnabled()) {
|
|
333862
|
+
this.fillArchivePoller = new FillArchivePoller({
|
|
333863
|
+
brokers: this.brokers,
|
|
333864
|
+
archiver: this.brokerArchiver,
|
|
333865
|
+
tracker: this.orderActivityTracker,
|
|
333866
|
+
metrics: this.otelMetrics
|
|
333867
|
+
});
|
|
333868
|
+
this.fillArchivePoller.start();
|
|
333869
|
+
}
|
|
333435
333870
|
return this;
|
|
333436
333871
|
}
|
|
333437
333872
|
}
|