@usherlabs/cex-broker 0.2.20 → 0.2.22
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 +466 -24
- package/dist/handlers/execute-action/context.d.ts +7 -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/helpers/shared/errors.d.ts +10 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +467 -25
- package/dist/index.js.map +21 -19
- 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;
|
|
@@ -315678,11 +315812,27 @@ class BrokerExecutionArchiver {
|
|
|
315678
315812
|
}
|
|
315679
315813
|
}
|
|
315680
315814
|
this.stats.flushed += batch.length;
|
|
315815
|
+
this.recordFlushHealth(batch);
|
|
315681
315816
|
return true;
|
|
315682
315817
|
}
|
|
315683
|
-
|
|
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) {
|
|
315684
315829
|
try {
|
|
315685
|
-
await this.otelMetrics?.recordCounter(metricName,
|
|
315830
|
+
await this.otelMetrics?.recordCounter(metricName, value, labels);
|
|
315831
|
+
} catch {}
|
|
315832
|
+
}
|
|
315833
|
+
async recordArchiveGauge(metricName, value) {
|
|
315834
|
+
try {
|
|
315835
|
+
await this.otelMetrics?.recordGauge(metricName, value, {});
|
|
315686
315836
|
} catch {}
|
|
315687
315837
|
}
|
|
315688
315838
|
emitOtelLog(entry) {
|
|
@@ -315779,6 +315929,159 @@ function parsePositiveInt(value, fallback) {
|
|
|
315779
315929
|
const parsed = Number.parseInt(value, 10);
|
|
315780
315930
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
315781
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
|
+
|
|
315782
316085
|
// src/helpers/otel.ts
|
|
315783
316086
|
var import_api2 = __toESM(require_src5(), 1);
|
|
315784
316087
|
var import_api_logs3 = __toESM(require_src7(), 1);
|
|
@@ -316100,9 +316403,23 @@ var grpc14 = __toESM(require_src3(), 1);
|
|
|
316100
316403
|
var grpc3 = __toESM(require_src3(), 1);
|
|
316101
316404
|
|
|
316102
316405
|
// src/helpers/shared/errors.ts
|
|
316406
|
+
var MAX_ERROR_DETAIL_LENGTH = 512;
|
|
316103
316407
|
function getErrorMessage(error) {
|
|
316104
316408
|
return error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
|
|
316105
316409
|
}
|
|
316410
|
+
function errorClassName(error) {
|
|
316411
|
+
if (!(error instanceof Error)) {
|
|
316412
|
+
return;
|
|
316413
|
+
}
|
|
316414
|
+
const name = error.constructor?.name || error.name;
|
|
316415
|
+
return name && name !== "Error" ? name : undefined;
|
|
316416
|
+
}
|
|
316417
|
+
function sanitizeErrorDetail(error) {
|
|
316418
|
+
const className = errorClassName(error);
|
|
316419
|
+
const message = getErrorMessage(error);
|
|
316420
|
+
const detail = className ? `${className}: ${message}` : message;
|
|
316421
|
+
return detail.replace(/\s+/g, " ").trim().slice(0, MAX_ERROR_DETAIL_LENGTH);
|
|
316422
|
+
}
|
|
316106
316423
|
function safeLogError(context2, error) {
|
|
316107
316424
|
try {
|
|
316108
316425
|
log.error(context2, { error });
|
|
@@ -329950,12 +330267,24 @@ function rejectWithGrpcError(ctx, error48, options) {
|
|
|
329950
330267
|
} else {
|
|
329951
330268
|
finalMessage = message;
|
|
329952
330269
|
}
|
|
330270
|
+
if (options?.appendClassName) {
|
|
330271
|
+
const className = errorClassName(error48);
|
|
330272
|
+
if (className && !finalMessage.includes(className)) {
|
|
330273
|
+
finalMessage = `${finalMessage} (${className})`;
|
|
330274
|
+
}
|
|
330275
|
+
}
|
|
329953
330276
|
ctx.wrappedCallback({ code, message: finalMessage }, null);
|
|
329954
330277
|
}
|
|
329955
330278
|
|
|
329956
330279
|
// src/handlers/execute-action/deposit.ts
|
|
329957
330280
|
async function handleDeposit(ctx) {
|
|
329958
|
-
const {
|
|
330281
|
+
const {
|
|
330282
|
+
normalizedCex,
|
|
330283
|
+
symbol: symbol2,
|
|
330284
|
+
selectedBrokerAccount,
|
|
330285
|
+
broker,
|
|
330286
|
+
brokerArchiver
|
|
330287
|
+
} = ctx;
|
|
329959
330288
|
if (!symbol2) {
|
|
329960
330289
|
return ctx.wrappedCallback({
|
|
329961
330290
|
code: grpc3.status.INVALID_ARGUMENT,
|
|
@@ -330018,6 +330347,32 @@ async function handleDeposit(ctx) {
|
|
|
330018
330347
|
}, null);
|
|
330019
330348
|
}
|
|
330020
330349
|
const status4 = normalizeDepositStatus(depositField(deposit, ["status", "state"]));
|
|
330350
|
+
const depositTxid = String(depositField(deposit, ["txid", "txId", "tx_hash", "txHash"]) ?? value.transactionHash);
|
|
330351
|
+
const creditedAt = depositField(deposit, [
|
|
330352
|
+
"creditedAt",
|
|
330353
|
+
"credited_at",
|
|
330354
|
+
"updated",
|
|
330355
|
+
"updatedAt",
|
|
330356
|
+
"timestamp",
|
|
330357
|
+
"datetime"
|
|
330358
|
+
]);
|
|
330359
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
330360
|
+
exchange: normalizedCex,
|
|
330361
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
330362
|
+
assetSymbol: symbol2,
|
|
330363
|
+
transfer: {
|
|
330364
|
+
eventKind: "deposit",
|
|
330365
|
+
lifecycleAction: "observe_deposit",
|
|
330366
|
+
status: status4,
|
|
330367
|
+
amount: observedAmount !== undefined ? String(observedAmount) : undefined,
|
|
330368
|
+
address: String(observedAddress ?? value.recipientAddress),
|
|
330369
|
+
network: depositNetwork?.exchangeNetworkId,
|
|
330370
|
+
externalId: depositTxid,
|
|
330371
|
+
txid: depositTxid,
|
|
330372
|
+
exchangeTimestamp: typeof creditedAt === "string" ? creditedAt : undefined,
|
|
330373
|
+
payload: deposit
|
|
330374
|
+
}
|
|
330375
|
+
});
|
|
330021
330376
|
log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
|
|
330022
330377
|
return ctx.wrappedCallback(null, {
|
|
330023
330378
|
proof: ctx.verity.proof,
|
|
@@ -330090,7 +330445,8 @@ async function handleDeposit(ctx) {
|
|
|
330090
330445
|
}
|
|
330091
330446
|
rejectWithGrpcError(ctx, error48, {
|
|
330092
330447
|
message,
|
|
330093
|
-
prefix: "deposit_observation_unavailable: "
|
|
330448
|
+
prefix: "deposit_observation_unavailable: ",
|
|
330449
|
+
appendClassName: true
|
|
330094
330450
|
});
|
|
330095
330451
|
}
|
|
330096
330452
|
}
|
|
@@ -330409,10 +330765,9 @@ async function handleOrderBookCall(ctx) {
|
|
|
330409
330765
|
});
|
|
330410
330766
|
} catch (error48) {
|
|
330411
330767
|
safeLogError("Order-book Call failed", error48);
|
|
330412
|
-
const message = getErrorMessage(error48);
|
|
330413
330768
|
ctx.wrappedCallback({
|
|
330414
330769
|
code: mapCcxtErrorToGrpcStatus(error48) ?? grpc4.status.INTERNAL,
|
|
330415
|
-
message: `Order-book Call failed: ${
|
|
330770
|
+
message: `Order-book Call failed: ${sanitizeErrorDetail(error48)}`
|
|
330416
330771
|
}, null);
|
|
330417
330772
|
}
|
|
330418
330773
|
return true;
|
|
@@ -330431,7 +330786,8 @@ async function handleInternalTransfer(ctx) {
|
|
|
330431
330786
|
symbol: symbol2,
|
|
330432
330787
|
verity,
|
|
330433
330788
|
useVerity,
|
|
330434
|
-
verityProverUrl
|
|
330789
|
+
verityProverUrl,
|
|
330790
|
+
brokerArchiver
|
|
330435
330791
|
} = ctx;
|
|
330436
330792
|
if (!symbol2) {
|
|
330437
330793
|
return ctx.wrappedCallback({
|
|
@@ -330479,6 +330835,21 @@ async function handleInternalTransfer(ctx) {
|
|
|
330479
330835
|
}), verityHttpClientOverridePredicate);
|
|
330480
330836
|
}
|
|
330481
330837
|
const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
|
|
330838
|
+
const normalized = normalizeCcxtTransactionForArchive(result);
|
|
330839
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
330840
|
+
exchange: normalizedCex,
|
|
330841
|
+
accountSelector: fromSelector,
|
|
330842
|
+
assetSymbol: symbol2,
|
|
330843
|
+
transfer: {
|
|
330844
|
+
eventKind: "internal_transfer",
|
|
330845
|
+
lifecycleAction: "submit_internal_transfer",
|
|
330846
|
+
status: normalized.status ?? "ok",
|
|
330847
|
+
amount: normalized.amount ?? String(transferPayload.amount),
|
|
330848
|
+
network: "internal",
|
|
330849
|
+
externalId: normalized.externalId,
|
|
330850
|
+
payload: { from: fromSelector, to: toSelector, result }
|
|
330851
|
+
}
|
|
330852
|
+
});
|
|
330482
330853
|
ctx.wrappedCallback(null, {
|
|
330483
330854
|
proof: verity.proof,
|
|
330484
330855
|
result: JSON.stringify(result)
|
|
@@ -330502,7 +330873,7 @@ async function handleInternalTransfer(ctx) {
|
|
|
330502
330873
|
}
|
|
330503
330874
|
ctx.wrappedCallback({
|
|
330504
330875
|
code,
|
|
330505
|
-
message: `InternalTransfer failed: ${
|
|
330876
|
+
message: `InternalTransfer failed: ${sanitizeErrorDetail(error48)}`
|
|
330506
330877
|
}, null);
|
|
330507
330878
|
}
|
|
330508
330879
|
}
|
|
@@ -330526,7 +330897,8 @@ async function handleCreateOrder(ctx) {
|
|
|
330526
330897
|
useVerity,
|
|
330527
330898
|
verityProverUrl,
|
|
330528
330899
|
otelMetrics,
|
|
330529
|
-
brokerArchiver
|
|
330900
|
+
brokerArchiver,
|
|
330901
|
+
orderActivityTracker
|
|
330530
330902
|
} = ctx;
|
|
330531
330903
|
const verityProof = verity.proof;
|
|
330532
330904
|
const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
|
|
@@ -330552,6 +330924,9 @@ async function handleCreateOrder(ctx) {
|
|
|
330552
330924
|
side: resolution.side,
|
|
330553
330925
|
requestedQuantity: resolution.amountBase ?? orderValue.amount
|
|
330554
330926
|
};
|
|
330927
|
+
if (selectedBrokerAccount?.label) {
|
|
330928
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
|
|
330929
|
+
}
|
|
330555
330930
|
const telemetryIds = extractOrderTelemetryIds(orderValue.params);
|
|
330556
330931
|
const submissionTimestamp = new Date().toISOString();
|
|
330557
330932
|
const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
|
|
@@ -330595,7 +330970,7 @@ async function handleCreateOrder(ctx) {
|
|
|
330595
330970
|
archiveOrderExecutionInBackground(brokerArchiver, failedCreateContext, undefined, error48);
|
|
330596
330971
|
ctx.wrappedCallback({
|
|
330597
330972
|
code: grpc6.status.INTERNAL,
|
|
330598
|
-
message:
|
|
330973
|
+
message: `Order Creation failed: ${sanitizeErrorDetail(error48)}`
|
|
330599
330974
|
}, null);
|
|
330600
330975
|
}
|
|
330601
330976
|
}
|
|
@@ -330616,7 +330991,8 @@ async function handleGetOrderDetails(ctx) {
|
|
|
330616
330991
|
useVerity,
|
|
330617
330992
|
verityProverUrl,
|
|
330618
330993
|
otelMetrics,
|
|
330619
|
-
brokerArchiver
|
|
330994
|
+
brokerArchiver,
|
|
330995
|
+
orderActivityTracker
|
|
330620
330996
|
} = ctx;
|
|
330621
330997
|
const verityProof = verity.proof;
|
|
330622
330998
|
const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
|
|
@@ -330630,6 +331006,9 @@ async function handleGetOrderDetails(ctx) {
|
|
|
330630
331006
|
}, null);
|
|
330631
331007
|
}
|
|
330632
331008
|
const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
|
|
331009
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
331010
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
331011
|
+
}
|
|
330633
331012
|
const getOrderContext = {
|
|
330634
331013
|
action: "GetOrderDetails",
|
|
330635
331014
|
cex: cex3,
|
|
@@ -330664,7 +331043,7 @@ async function handleGetOrderDetails(ctx) {
|
|
|
330664
331043
|
archiveOrderExecutionInBackground(brokerArchiver, failedGetOrderContext, undefined, error48);
|
|
330665
331044
|
ctx.wrappedCallback({
|
|
330666
331045
|
code: grpc6.status.INTERNAL,
|
|
330667
|
-
message: `Failed to fetch order details from ${cex3}`
|
|
331046
|
+
message: `Failed to fetch order details from ${cex3}: ${sanitizeErrorDetail(error48)}`
|
|
330668
331047
|
}, null);
|
|
330669
331048
|
}
|
|
330670
331049
|
}
|
|
@@ -330685,7 +331064,8 @@ async function handleCancelOrder(ctx) {
|
|
|
330685
331064
|
useVerity,
|
|
330686
331065
|
verityProverUrl,
|
|
330687
331066
|
otelMetrics,
|
|
330688
|
-
brokerArchiver
|
|
331067
|
+
brokerArchiver,
|
|
331068
|
+
orderActivityTracker
|
|
330689
331069
|
} = ctx;
|
|
330690
331070
|
const verityProof = verity.proof;
|
|
330691
331071
|
const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
|
|
@@ -330706,6 +331086,9 @@ async function handleCancelOrder(ctx) {
|
|
|
330706
331086
|
...extractOrderTelemetryIds(cancelOrderValue.params)
|
|
330707
331087
|
};
|
|
330708
331088
|
const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
|
|
331089
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
331090
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
331091
|
+
}
|
|
330709
331092
|
emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
|
|
330710
331093
|
archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
|
|
330711
331094
|
ctx.wrappedCallback(null, {
|
|
@@ -330724,7 +331107,7 @@ async function handleCancelOrder(ctx) {
|
|
|
330724
331107
|
archiveOrderExecutionInBackground(brokerArchiver, failedCancelContext, undefined, error48);
|
|
330725
331108
|
ctx.wrappedCallback({
|
|
330726
331109
|
code: grpc6.status.INTERNAL,
|
|
330727
|
-
message: `Failed to cancel order from ${cex3}`
|
|
331110
|
+
message: `Failed to cancel order from ${cex3}: ${sanitizeErrorDetail(error48)}`
|
|
330728
331111
|
}, null);
|
|
330729
331112
|
}
|
|
330730
331113
|
}
|
|
@@ -331221,7 +331604,7 @@ async function handleGetPerpConfigState(ctx) {
|
|
|
331221
331604
|
safeLogError(`GetPerpConfigState failed for ${cex3}`, error48);
|
|
331222
331605
|
ctx.wrappedCallback({
|
|
331223
331606
|
code: grpc8.status.INTERNAL,
|
|
331224
|
-
message:
|
|
331607
|
+
message: `GetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
|
|
331225
331608
|
}, null);
|
|
331226
331609
|
}
|
|
331227
331610
|
}
|
|
@@ -331262,7 +331645,7 @@ async function handleSetPerpConfigState(ctx) {
|
|
|
331262
331645
|
safeLogError(`SetPerpConfigState failed for ${cex3}`, error48);
|
|
331263
331646
|
ctx.wrappedCallback({
|
|
331264
331647
|
code: grpc8.status.INTERNAL,
|
|
331265
|
-
message:
|
|
331648
|
+
message: `SetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
|
|
331266
331649
|
}, null);
|
|
331267
331650
|
}
|
|
331268
331651
|
}
|
|
@@ -331313,7 +331696,8 @@ async function handleTreasuryCall(ctx) {
|
|
|
331313
331696
|
safeLogError("Call failed", error48);
|
|
331314
331697
|
rejectWithGrpcError(ctx, error48, {
|
|
331315
331698
|
message: getErrorMessage(error48),
|
|
331316
|
-
preferStableMessageOnly: true
|
|
331699
|
+
preferStableMessageOnly: true,
|
|
331700
|
+
appendClassName: true
|
|
331317
331701
|
});
|
|
331318
331702
|
}
|
|
331319
331703
|
}
|
|
@@ -331336,7 +331720,8 @@ async function handleWithdraw(ctx) {
|
|
|
331336
331720
|
applyVerityToBroker,
|
|
331337
331721
|
useVerity,
|
|
331338
331722
|
verityProverUrl,
|
|
331339
|
-
otelMetrics
|
|
331723
|
+
otelMetrics,
|
|
331724
|
+
brokerArchiver
|
|
331340
331725
|
} = ctx;
|
|
331341
331726
|
const verityProof = verity.proof;
|
|
331342
331727
|
if (!symbol2) {
|
|
@@ -331385,6 +331770,26 @@ async function handleWithdraw(ctx) {
|
|
|
331385
331770
|
network: withdrawNetwork.exchangeNetworkId
|
|
331386
331771
|
});
|
|
331387
331772
|
log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
|
|
331773
|
+
const normalized = normalizeCcxtTransactionForArchive(transaction);
|
|
331774
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
331775
|
+
exchange: cex3,
|
|
331776
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
331777
|
+
assetSymbol: normalized.assetSymbol ?? symbol2,
|
|
331778
|
+
transfer: {
|
|
331779
|
+
eventKind: "withdrawal",
|
|
331780
|
+
lifecycleAction: "submit_withdrawal",
|
|
331781
|
+
status: normalized.status,
|
|
331782
|
+
amount: normalized.amount ?? String(transferValue.amount),
|
|
331783
|
+
address: normalized.address ?? transferValue.recipientAddress,
|
|
331784
|
+
network: normalized.network ?? withdrawNetwork.exchangeNetworkId,
|
|
331785
|
+
externalId: normalized.externalId,
|
|
331786
|
+
txid: normalized.txid,
|
|
331787
|
+
feeAmount: normalized.feeAmount,
|
|
331788
|
+
feeCurrency: normalized.feeCurrency,
|
|
331789
|
+
exchangeTimestamp: normalized.exchangeTimestamp,
|
|
331790
|
+
payload: transaction
|
|
331791
|
+
}
|
|
331792
|
+
});
|
|
331388
331793
|
ctx.wrappedCallback(null, {
|
|
331389
331794
|
proof: ctx.verity.proof,
|
|
331390
331795
|
result: JSON.stringify({
|
|
@@ -331396,10 +331801,25 @@ async function handleWithdraw(ctx) {
|
|
|
331396
331801
|
});
|
|
331397
331802
|
} catch (error48) {
|
|
331398
331803
|
safeLogError("Withdraw failed", error48);
|
|
331804
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
331805
|
+
exchange: cex3,
|
|
331806
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
331807
|
+
assetSymbol: symbol2,
|
|
331808
|
+
transfer: {
|
|
331809
|
+
eventKind: "withdrawal",
|
|
331810
|
+
lifecycleAction: "submit_withdrawal",
|
|
331811
|
+
status: "failed",
|
|
331812
|
+
amount: String(transferValue.amount),
|
|
331813
|
+
address: transferValue.recipientAddress,
|
|
331814
|
+
network: withdrawNetwork.exchangeNetworkId,
|
|
331815
|
+
errorSummary: getErrorMessage(error48),
|
|
331816
|
+
payload: { recipientAddress: transferValue.recipientAddress }
|
|
331817
|
+
}
|
|
331818
|
+
});
|
|
331399
331819
|
const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
|
|
331400
331820
|
ctx.wrappedCallback({
|
|
331401
331821
|
code,
|
|
331402
|
-
message: `Withdraw failed: ${
|
|
331822
|
+
message: `Withdraw failed: ${sanitizeErrorDetail(error48)}`
|
|
331403
331823
|
}, null);
|
|
331404
331824
|
}
|
|
331405
331825
|
}
|
|
@@ -331443,7 +331863,8 @@ function createExecuteActionHandler(deps) {
|
|
|
331443
331863
|
useVerity,
|
|
331444
331864
|
verityProverUrl,
|
|
331445
331865
|
otelMetrics,
|
|
331446
|
-
brokerArchiver
|
|
331866
|
+
brokerArchiver,
|
|
331867
|
+
orderActivityTracker
|
|
331447
331868
|
} = deps;
|
|
331448
331869
|
return async (call, callback) => {
|
|
331449
331870
|
const startTime = Date.now();
|
|
@@ -331522,7 +331943,8 @@ function createExecuteActionHandler(deps) {
|
|
|
331522
331943
|
useVerity,
|
|
331523
331944
|
verityProverUrl,
|
|
331524
331945
|
otelMetrics,
|
|
331525
|
-
brokerArchiver
|
|
331946
|
+
brokerArchiver,
|
|
331947
|
+
orderActivityTracker
|
|
331526
331948
|
};
|
|
331527
331949
|
if (action === Action.Call) {
|
|
331528
331950
|
const handled = await handleOrderBookCall(preludeCtx);
|
|
@@ -333111,7 +333533,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
|
|
|
333111
333533
|
// src/server.ts
|
|
333112
333534
|
var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
|
|
333113
333535
|
var cexNode = grpcObj.cex_broker;
|
|
333114
|
-
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
|
|
333536
|
+
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
|
|
333115
333537
|
const server = new grpc14.Server;
|
|
333116
333538
|
server.addService(cexNode.cex_service.service, {
|
|
333117
333539
|
ExecuteAction: createExecuteActionHandler({
|
|
@@ -333121,7 +333543,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
|
|
|
333121
333543
|
useVerity,
|
|
333122
333544
|
verityProverUrl,
|
|
333123
333545
|
otelMetrics,
|
|
333124
|
-
brokerArchiver
|
|
333546
|
+
brokerArchiver,
|
|
333547
|
+
orderActivityTracker
|
|
333125
333548
|
}),
|
|
333126
333549
|
Subscribe: createSubscribeHandler({
|
|
333127
333550
|
brokers,
|
|
@@ -333266,6 +333689,8 @@ class CEXBroker {
|
|
|
333266
333689
|
otelLogs;
|
|
333267
333690
|
brokerArchiver;
|
|
333268
333691
|
depositReconciler;
|
|
333692
|
+
orderActivityTracker = new OrderActivityTracker;
|
|
333693
|
+
fillArchivePoller;
|
|
333269
333694
|
loadEnvConfig() {
|
|
333270
333695
|
log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
|
|
333271
333696
|
const configMap = {};
|
|
@@ -333406,6 +333831,10 @@ class CEXBroker {
|
|
|
333406
333831
|
this.depositReconciler.stop();
|
|
333407
333832
|
this.depositReconciler = undefined;
|
|
333408
333833
|
}
|
|
333834
|
+
if (this.fillArchivePoller) {
|
|
333835
|
+
this.fillArchivePoller.stop();
|
|
333836
|
+
this.fillArchivePoller = undefined;
|
|
333837
|
+
}
|
|
333409
333838
|
if (this.server) {
|
|
333410
333839
|
await this.server.forceShutdown();
|
|
333411
333840
|
}
|
|
@@ -333427,11 +333856,15 @@ class CEXBroker {
|
|
|
333427
333856
|
this.depositReconciler.stop();
|
|
333428
333857
|
this.depositReconciler = undefined;
|
|
333429
333858
|
}
|
|
333859
|
+
if (this.fillArchivePoller) {
|
|
333860
|
+
this.fillArchivePoller.stop();
|
|
333861
|
+
this.fillArchivePoller = undefined;
|
|
333862
|
+
}
|
|
333430
333863
|
log.info(`Running CEXBroker at ${new Date().toISOString()}`);
|
|
333431
333864
|
if (this.otelMetrics?.isOtelEnabled()) {
|
|
333432
333865
|
await this.otelMetrics.initialize();
|
|
333433
333866
|
}
|
|
333434
|
-
this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver);
|
|
333867
|
+
this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
|
|
333435
333868
|
this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
|
|
333436
333869
|
if (err2) {
|
|
333437
333870
|
log.error(err2);
|
|
@@ -333446,6 +333879,15 @@ class CEXBroker {
|
|
|
333446
333879
|
metrics: this.otelMetrics
|
|
333447
333880
|
});
|
|
333448
333881
|
this.depositReconciler.start();
|
|
333882
|
+
if (this.brokerArchiver?.isEnabled()) {
|
|
333883
|
+
this.fillArchivePoller = new FillArchivePoller({
|
|
333884
|
+
brokers: this.brokers,
|
|
333885
|
+
archiver: this.brokerArchiver,
|
|
333886
|
+
tracker: this.orderActivityTracker,
|
|
333887
|
+
metrics: this.otelMetrics
|
|
333888
|
+
});
|
|
333889
|
+
this.fillArchivePoller.start();
|
|
333890
|
+
}
|
|
333449
333891
|
return this;
|
|
333450
333892
|
}
|
|
333451
333893
|
}
|