@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/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;
|
|
@@ -275058,11 +275192,27 @@ class BrokerExecutionArchiver {
|
|
|
275058
275192
|
}
|
|
275059
275193
|
}
|
|
275060
275194
|
this.stats.flushed += batch.length;
|
|
275195
|
+
this.recordFlushHealth(batch);
|
|
275061
275196
|
return true;
|
|
275062
275197
|
}
|
|
275063
|
-
|
|
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) {
|
|
275064
275209
|
try {
|
|
275065
|
-
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, {});
|
|
275066
275216
|
} catch {}
|
|
275067
275217
|
}
|
|
275068
275218
|
emitOtelLog(entry) {
|
|
@@ -275159,6 +275309,159 @@ function parsePositiveInt(value, fallback) {
|
|
|
275159
275309
|
const parsed = Number.parseInt(value, 10);
|
|
275160
275310
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
275161
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
|
+
|
|
275162
275465
|
// src/helpers/otel.ts
|
|
275163
275466
|
var import_api14 = __toESM(require_src2(), 1);
|
|
275164
275467
|
|
|
@@ -279052,9 +279355,23 @@ import * as grpc14 from "@grpc/grpc-js";
|
|
|
279052
279355
|
import * as grpc3 from "@grpc/grpc-js";
|
|
279053
279356
|
|
|
279054
279357
|
// src/helpers/shared/errors.ts
|
|
279358
|
+
var MAX_ERROR_DETAIL_LENGTH = 512;
|
|
279055
279359
|
function getErrorMessage(error) {
|
|
279056
279360
|
return error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
|
|
279057
279361
|
}
|
|
279362
|
+
function errorClassName(error) {
|
|
279363
|
+
if (!(error instanceof Error)) {
|
|
279364
|
+
return;
|
|
279365
|
+
}
|
|
279366
|
+
const name = error.constructor?.name || error.name;
|
|
279367
|
+
return name && name !== "Error" ? name : undefined;
|
|
279368
|
+
}
|
|
279369
|
+
function sanitizeErrorDetail(error) {
|
|
279370
|
+
const className = errorClassName(error);
|
|
279371
|
+
const message = getErrorMessage(error);
|
|
279372
|
+
const detail = className ? `${className}: ${message}` : message;
|
|
279373
|
+
return detail.replace(/\s+/g, " ").trim().slice(0, MAX_ERROR_DETAIL_LENGTH);
|
|
279374
|
+
}
|
|
279058
279375
|
function safeLogError(context3, error) {
|
|
279059
279376
|
try {
|
|
279060
279377
|
log.error(context3, { error });
|
|
@@ -292902,12 +293219,24 @@ function rejectWithGrpcError(ctx, error48, options) {
|
|
|
292902
293219
|
} else {
|
|
292903
293220
|
finalMessage = message;
|
|
292904
293221
|
}
|
|
293222
|
+
if (options?.appendClassName) {
|
|
293223
|
+
const className = errorClassName(error48);
|
|
293224
|
+
if (className && !finalMessage.includes(className)) {
|
|
293225
|
+
finalMessage = `${finalMessage} (${className})`;
|
|
293226
|
+
}
|
|
293227
|
+
}
|
|
292905
293228
|
ctx.wrappedCallback({ code, message: finalMessage }, null);
|
|
292906
293229
|
}
|
|
292907
293230
|
|
|
292908
293231
|
// src/handlers/execute-action/deposit.ts
|
|
292909
293232
|
async function handleDeposit(ctx) {
|
|
292910
|
-
const {
|
|
293233
|
+
const {
|
|
293234
|
+
normalizedCex,
|
|
293235
|
+
symbol: symbol2,
|
|
293236
|
+
selectedBrokerAccount,
|
|
293237
|
+
broker,
|
|
293238
|
+
brokerArchiver
|
|
293239
|
+
} = ctx;
|
|
292911
293240
|
if (!symbol2) {
|
|
292912
293241
|
return ctx.wrappedCallback({
|
|
292913
293242
|
code: grpc3.status.INVALID_ARGUMENT,
|
|
@@ -292970,6 +293299,32 @@ async function handleDeposit(ctx) {
|
|
|
292970
293299
|
}, null);
|
|
292971
293300
|
}
|
|
292972
293301
|
const status4 = normalizeDepositStatus(depositField(deposit, ["status", "state"]));
|
|
293302
|
+
const depositTxid = String(depositField(deposit, ["txid", "txId", "tx_hash", "txHash"]) ?? value.transactionHash);
|
|
293303
|
+
const creditedAt = depositField(deposit, [
|
|
293304
|
+
"creditedAt",
|
|
293305
|
+
"credited_at",
|
|
293306
|
+
"updated",
|
|
293307
|
+
"updatedAt",
|
|
293308
|
+
"timestamp",
|
|
293309
|
+
"datetime"
|
|
293310
|
+
]);
|
|
293311
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
293312
|
+
exchange: normalizedCex,
|
|
293313
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
293314
|
+
assetSymbol: symbol2,
|
|
293315
|
+
transfer: {
|
|
293316
|
+
eventKind: "deposit",
|
|
293317
|
+
lifecycleAction: "observe_deposit",
|
|
293318
|
+
status: status4,
|
|
293319
|
+
amount: observedAmount !== undefined ? String(observedAmount) : undefined,
|
|
293320
|
+
address: String(observedAddress ?? value.recipientAddress),
|
|
293321
|
+
network: depositNetwork?.exchangeNetworkId,
|
|
293322
|
+
externalId: depositTxid,
|
|
293323
|
+
txid: depositTxid,
|
|
293324
|
+
exchangeTimestamp: typeof creditedAt === "string" ? creditedAt : undefined,
|
|
293325
|
+
payload: deposit
|
|
293326
|
+
}
|
|
293327
|
+
});
|
|
292973
293328
|
log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
|
|
292974
293329
|
return ctx.wrappedCallback(null, {
|
|
292975
293330
|
proof: ctx.verity.proof,
|
|
@@ -293042,7 +293397,8 @@ async function handleDeposit(ctx) {
|
|
|
293042
293397
|
}
|
|
293043
293398
|
rejectWithGrpcError(ctx, error48, {
|
|
293044
293399
|
message,
|
|
293045
|
-
prefix: "deposit_observation_unavailable: "
|
|
293400
|
+
prefix: "deposit_observation_unavailable: ",
|
|
293401
|
+
appendClassName: true
|
|
293046
293402
|
});
|
|
293047
293403
|
}
|
|
293048
293404
|
}
|
|
@@ -293361,10 +293717,9 @@ async function handleOrderBookCall(ctx) {
|
|
|
293361
293717
|
});
|
|
293362
293718
|
} catch (error48) {
|
|
293363
293719
|
safeLogError("Order-book Call failed", error48);
|
|
293364
|
-
const message = getErrorMessage(error48);
|
|
293365
293720
|
ctx.wrappedCallback({
|
|
293366
293721
|
code: mapCcxtErrorToGrpcStatus(error48) ?? grpc4.status.INTERNAL,
|
|
293367
|
-
message: `Order-book Call failed: ${
|
|
293722
|
+
message: `Order-book Call failed: ${sanitizeErrorDetail(error48)}`
|
|
293368
293723
|
}, null);
|
|
293369
293724
|
}
|
|
293370
293725
|
return true;
|
|
@@ -293383,7 +293738,8 @@ async function handleInternalTransfer(ctx) {
|
|
|
293383
293738
|
symbol: symbol2,
|
|
293384
293739
|
verity,
|
|
293385
293740
|
useVerity,
|
|
293386
|
-
verityProverUrl
|
|
293741
|
+
verityProverUrl,
|
|
293742
|
+
brokerArchiver
|
|
293387
293743
|
} = ctx;
|
|
293388
293744
|
if (!symbol2) {
|
|
293389
293745
|
return ctx.wrappedCallback({
|
|
@@ -293431,6 +293787,21 @@ async function handleInternalTransfer(ctx) {
|
|
|
293431
293787
|
}), verityHttpClientOverridePredicate);
|
|
293432
293788
|
}
|
|
293433
293789
|
const result = await transferBinanceInternal(sourceAccount, destAccount, symbol2, transferPayload.amount);
|
|
293790
|
+
const normalized = normalizeCcxtTransactionForArchive(result);
|
|
293791
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
293792
|
+
exchange: normalizedCex,
|
|
293793
|
+
accountSelector: fromSelector,
|
|
293794
|
+
assetSymbol: symbol2,
|
|
293795
|
+
transfer: {
|
|
293796
|
+
eventKind: "internal_transfer",
|
|
293797
|
+
lifecycleAction: "submit_internal_transfer",
|
|
293798
|
+
status: normalized.status ?? "ok",
|
|
293799
|
+
amount: normalized.amount ?? String(transferPayload.amount),
|
|
293800
|
+
network: "internal",
|
|
293801
|
+
externalId: normalized.externalId,
|
|
293802
|
+
payload: { from: fromSelector, to: toSelector, result }
|
|
293803
|
+
}
|
|
293804
|
+
});
|
|
293434
293805
|
ctx.wrappedCallback(null, {
|
|
293435
293806
|
proof: verity.proof,
|
|
293436
293807
|
result: JSON.stringify(result)
|
|
@@ -293454,7 +293825,7 @@ async function handleInternalTransfer(ctx) {
|
|
|
293454
293825
|
}
|
|
293455
293826
|
ctx.wrappedCallback({
|
|
293456
293827
|
code,
|
|
293457
|
-
message: `InternalTransfer failed: ${
|
|
293828
|
+
message: `InternalTransfer failed: ${sanitizeErrorDetail(error48)}`
|
|
293458
293829
|
}, null);
|
|
293459
293830
|
}
|
|
293460
293831
|
}
|
|
@@ -293478,7 +293849,8 @@ async function handleCreateOrder(ctx) {
|
|
|
293478
293849
|
useVerity,
|
|
293479
293850
|
verityProverUrl,
|
|
293480
293851
|
otelMetrics,
|
|
293481
|
-
brokerArchiver
|
|
293852
|
+
brokerArchiver,
|
|
293853
|
+
orderActivityTracker
|
|
293482
293854
|
} = ctx;
|
|
293483
293855
|
const verityProof = verity.proof;
|
|
293484
293856
|
const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
|
|
@@ -293504,6 +293876,9 @@ async function handleCreateOrder(ctx) {
|
|
|
293504
293876
|
side: resolution.side,
|
|
293505
293877
|
requestedQuantity: resolution.amountBase ?? orderValue.amount
|
|
293506
293878
|
};
|
|
293879
|
+
if (selectedBrokerAccount?.label) {
|
|
293880
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
|
|
293881
|
+
}
|
|
293507
293882
|
const telemetryIds = extractOrderTelemetryIds(orderValue.params);
|
|
293508
293883
|
const submissionTimestamp = new Date().toISOString();
|
|
293509
293884
|
const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
|
|
@@ -293547,7 +293922,7 @@ async function handleCreateOrder(ctx) {
|
|
|
293547
293922
|
archiveOrderExecutionInBackground(brokerArchiver, failedCreateContext, undefined, error48);
|
|
293548
293923
|
ctx.wrappedCallback({
|
|
293549
293924
|
code: grpc6.status.INTERNAL,
|
|
293550
|
-
message:
|
|
293925
|
+
message: `Order Creation failed: ${sanitizeErrorDetail(error48)}`
|
|
293551
293926
|
}, null);
|
|
293552
293927
|
}
|
|
293553
293928
|
}
|
|
@@ -293568,7 +293943,8 @@ async function handleGetOrderDetails(ctx) {
|
|
|
293568
293943
|
useVerity,
|
|
293569
293944
|
verityProverUrl,
|
|
293570
293945
|
otelMetrics,
|
|
293571
|
-
brokerArchiver
|
|
293946
|
+
brokerArchiver,
|
|
293947
|
+
orderActivityTracker
|
|
293572
293948
|
} = ctx;
|
|
293573
293949
|
const verityProof = verity.proof;
|
|
293574
293950
|
const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
|
|
@@ -293582,6 +293958,9 @@ async function handleGetOrderDetails(ctx) {
|
|
|
293582
293958
|
}, null);
|
|
293583
293959
|
}
|
|
293584
293960
|
const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
|
|
293961
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
293962
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
293963
|
+
}
|
|
293585
293964
|
const getOrderContext = {
|
|
293586
293965
|
action: "GetOrderDetails",
|
|
293587
293966
|
cex: cex3,
|
|
@@ -293616,7 +293995,7 @@ async function handleGetOrderDetails(ctx) {
|
|
|
293616
293995
|
archiveOrderExecutionInBackground(brokerArchiver, failedGetOrderContext, undefined, error48);
|
|
293617
293996
|
ctx.wrappedCallback({
|
|
293618
293997
|
code: grpc6.status.INTERNAL,
|
|
293619
|
-
message: `Failed to fetch order details from ${cex3}`
|
|
293998
|
+
message: `Failed to fetch order details from ${cex3}: ${sanitizeErrorDetail(error48)}`
|
|
293620
293999
|
}, null);
|
|
293621
294000
|
}
|
|
293622
294001
|
}
|
|
@@ -293637,7 +294016,8 @@ async function handleCancelOrder(ctx) {
|
|
|
293637
294016
|
useVerity,
|
|
293638
294017
|
verityProverUrl,
|
|
293639
294018
|
otelMetrics,
|
|
293640
|
-
brokerArchiver
|
|
294019
|
+
brokerArchiver,
|
|
294020
|
+
orderActivityTracker
|
|
293641
294021
|
} = ctx;
|
|
293642
294022
|
const verityProof = verity.proof;
|
|
293643
294023
|
const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
|
|
@@ -293658,6 +294038,9 @@ async function handleCancelOrder(ctx) {
|
|
|
293658
294038
|
...extractOrderTelemetryIds(cancelOrderValue.params)
|
|
293659
294039
|
};
|
|
293660
294040
|
const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
|
|
294041
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
294042
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
294043
|
+
}
|
|
293661
294044
|
emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
|
|
293662
294045
|
archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
|
|
293663
294046
|
ctx.wrappedCallback(null, {
|
|
@@ -293676,7 +294059,7 @@ async function handleCancelOrder(ctx) {
|
|
|
293676
294059
|
archiveOrderExecutionInBackground(brokerArchiver, failedCancelContext, undefined, error48);
|
|
293677
294060
|
ctx.wrappedCallback({
|
|
293678
294061
|
code: grpc6.status.INTERNAL,
|
|
293679
|
-
message: `Failed to cancel order from ${cex3}`
|
|
294062
|
+
message: `Failed to cancel order from ${cex3}: ${sanitizeErrorDetail(error48)}`
|
|
293680
294063
|
}, null);
|
|
293681
294064
|
}
|
|
293682
294065
|
}
|
|
@@ -294173,7 +294556,7 @@ async function handleGetPerpConfigState(ctx) {
|
|
|
294173
294556
|
safeLogError(`GetPerpConfigState failed for ${cex3}`, error48);
|
|
294174
294557
|
ctx.wrappedCallback({
|
|
294175
294558
|
code: grpc8.status.INTERNAL,
|
|
294176
|
-
message:
|
|
294559
|
+
message: `GetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
|
|
294177
294560
|
}, null);
|
|
294178
294561
|
}
|
|
294179
294562
|
}
|
|
@@ -294214,7 +294597,7 @@ async function handleSetPerpConfigState(ctx) {
|
|
|
294214
294597
|
safeLogError(`SetPerpConfigState failed for ${cex3}`, error48);
|
|
294215
294598
|
ctx.wrappedCallback({
|
|
294216
294599
|
code: grpc8.status.INTERNAL,
|
|
294217
|
-
message:
|
|
294600
|
+
message: `SetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
|
|
294218
294601
|
}, null);
|
|
294219
294602
|
}
|
|
294220
294603
|
}
|
|
@@ -294265,7 +294648,8 @@ async function handleTreasuryCall(ctx) {
|
|
|
294265
294648
|
safeLogError("Call failed", error48);
|
|
294266
294649
|
rejectWithGrpcError(ctx, error48, {
|
|
294267
294650
|
message: getErrorMessage(error48),
|
|
294268
|
-
preferStableMessageOnly: true
|
|
294651
|
+
preferStableMessageOnly: true,
|
|
294652
|
+
appendClassName: true
|
|
294269
294653
|
});
|
|
294270
294654
|
}
|
|
294271
294655
|
}
|
|
@@ -294288,7 +294672,8 @@ async function handleWithdraw(ctx) {
|
|
|
294288
294672
|
applyVerityToBroker,
|
|
294289
294673
|
useVerity,
|
|
294290
294674
|
verityProverUrl,
|
|
294291
|
-
otelMetrics
|
|
294675
|
+
otelMetrics,
|
|
294676
|
+
brokerArchiver
|
|
294292
294677
|
} = ctx;
|
|
294293
294678
|
const verityProof = verity.proof;
|
|
294294
294679
|
if (!symbol2) {
|
|
@@ -294337,6 +294722,26 @@ async function handleWithdraw(ctx) {
|
|
|
294337
294722
|
network: withdrawNetwork.exchangeNetworkId
|
|
294338
294723
|
});
|
|
294339
294724
|
log.info(`Withdraw Result: ${JSON.stringify(transaction)}`);
|
|
294725
|
+
const normalized = normalizeCcxtTransactionForArchive(transaction);
|
|
294726
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
294727
|
+
exchange: cex3,
|
|
294728
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
294729
|
+
assetSymbol: normalized.assetSymbol ?? symbol2,
|
|
294730
|
+
transfer: {
|
|
294731
|
+
eventKind: "withdrawal",
|
|
294732
|
+
lifecycleAction: "submit_withdrawal",
|
|
294733
|
+
status: normalized.status,
|
|
294734
|
+
amount: normalized.amount ?? String(transferValue.amount),
|
|
294735
|
+
address: normalized.address ?? transferValue.recipientAddress,
|
|
294736
|
+
network: normalized.network ?? withdrawNetwork.exchangeNetworkId,
|
|
294737
|
+
externalId: normalized.externalId,
|
|
294738
|
+
txid: normalized.txid,
|
|
294739
|
+
feeAmount: normalized.feeAmount,
|
|
294740
|
+
feeCurrency: normalized.feeCurrency,
|
|
294741
|
+
exchangeTimestamp: normalized.exchangeTimestamp,
|
|
294742
|
+
payload: transaction
|
|
294743
|
+
}
|
|
294744
|
+
});
|
|
294340
294745
|
ctx.wrappedCallback(null, {
|
|
294341
294746
|
proof: ctx.verity.proof,
|
|
294342
294747
|
result: JSON.stringify({
|
|
@@ -294348,10 +294753,25 @@ async function handleWithdraw(ctx) {
|
|
|
294348
294753
|
});
|
|
294349
294754
|
} catch (error48) {
|
|
294350
294755
|
safeLogError("Withdraw failed", error48);
|
|
294756
|
+
archiveTransferEventInBackground(brokerArchiver, {
|
|
294757
|
+
exchange: cex3,
|
|
294758
|
+
accountSelector: selectedBrokerAccount?.label,
|
|
294759
|
+
assetSymbol: symbol2,
|
|
294760
|
+
transfer: {
|
|
294761
|
+
eventKind: "withdrawal",
|
|
294762
|
+
lifecycleAction: "submit_withdrawal",
|
|
294763
|
+
status: "failed",
|
|
294764
|
+
amount: String(transferValue.amount),
|
|
294765
|
+
address: transferValue.recipientAddress,
|
|
294766
|
+
network: withdrawNetwork.exchangeNetworkId,
|
|
294767
|
+
errorSummary: getErrorMessage(error48),
|
|
294768
|
+
payload: { recipientAddress: transferValue.recipientAddress }
|
|
294769
|
+
}
|
|
294770
|
+
});
|
|
294351
294771
|
const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
|
|
294352
294772
|
ctx.wrappedCallback({
|
|
294353
294773
|
code,
|
|
294354
|
-
message: `Withdraw failed: ${
|
|
294774
|
+
message: `Withdraw failed: ${sanitizeErrorDetail(error48)}`
|
|
294355
294775
|
}, null);
|
|
294356
294776
|
}
|
|
294357
294777
|
}
|
|
@@ -294395,7 +294815,8 @@ function createExecuteActionHandler(deps) {
|
|
|
294395
294815
|
useVerity,
|
|
294396
294816
|
verityProverUrl,
|
|
294397
294817
|
otelMetrics,
|
|
294398
|
-
brokerArchiver
|
|
294818
|
+
brokerArchiver,
|
|
294819
|
+
orderActivityTracker
|
|
294399
294820
|
} = deps;
|
|
294400
294821
|
return async (call, callback) => {
|
|
294401
294822
|
const startTime = Date.now();
|
|
@@ -294474,7 +294895,8 @@ function createExecuteActionHandler(deps) {
|
|
|
294474
294895
|
useVerity,
|
|
294475
294896
|
verityProverUrl,
|
|
294476
294897
|
otelMetrics,
|
|
294477
|
-
brokerArchiver
|
|
294898
|
+
brokerArchiver,
|
|
294899
|
+
orderActivityTracker
|
|
294478
294900
|
};
|
|
294479
294901
|
if (action === Action.Call) {
|
|
294480
294902
|
const handled = await handleOrderBookCall(preludeCtx);
|
|
@@ -296064,7 +296486,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
|
|
|
296064
296486
|
// src/server.ts
|
|
296065
296487
|
var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
|
|
296066
296488
|
var cexNode = grpcObj.cex_broker;
|
|
296067
|
-
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
|
|
296489
|
+
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
|
|
296068
296490
|
const server = new grpc14.Server;
|
|
296069
296491
|
server.addService(cexNode.cex_service.service, {
|
|
296070
296492
|
ExecuteAction: createExecuteActionHandler({
|
|
@@ -296074,7 +296496,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
|
|
|
296074
296496
|
useVerity,
|
|
296075
296497
|
verityProverUrl,
|
|
296076
296498
|
otelMetrics,
|
|
296077
|
-
brokerArchiver
|
|
296499
|
+
brokerArchiver,
|
|
296500
|
+
orderActivityTracker
|
|
296078
296501
|
}),
|
|
296079
296502
|
Subscribe: createSubscribeHandler({
|
|
296080
296503
|
brokers,
|
|
@@ -296219,6 +296642,8 @@ class CEXBroker {
|
|
|
296219
296642
|
otelLogs;
|
|
296220
296643
|
brokerArchiver;
|
|
296221
296644
|
depositReconciler;
|
|
296645
|
+
orderActivityTracker = new OrderActivityTracker;
|
|
296646
|
+
fillArchivePoller;
|
|
296222
296647
|
loadEnvConfig() {
|
|
296223
296648
|
log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
|
|
296224
296649
|
const configMap = {};
|
|
@@ -296359,6 +296784,10 @@ class CEXBroker {
|
|
|
296359
296784
|
this.depositReconciler.stop();
|
|
296360
296785
|
this.depositReconciler = undefined;
|
|
296361
296786
|
}
|
|
296787
|
+
if (this.fillArchivePoller) {
|
|
296788
|
+
this.fillArchivePoller.stop();
|
|
296789
|
+
this.fillArchivePoller = undefined;
|
|
296790
|
+
}
|
|
296362
296791
|
if (this.server) {
|
|
296363
296792
|
await this.server.forceShutdown();
|
|
296364
296793
|
}
|
|
@@ -296380,11 +296809,15 @@ class CEXBroker {
|
|
|
296380
296809
|
this.depositReconciler.stop();
|
|
296381
296810
|
this.depositReconciler = undefined;
|
|
296382
296811
|
}
|
|
296812
|
+
if (this.fillArchivePoller) {
|
|
296813
|
+
this.fillArchivePoller.stop();
|
|
296814
|
+
this.fillArchivePoller = undefined;
|
|
296815
|
+
}
|
|
296383
296816
|
log.info(`Running CEXBroker at ${new Date().toISOString()}`);
|
|
296384
296817
|
if (this.otelMetrics?.isOtelEnabled()) {
|
|
296385
296818
|
await this.otelMetrics.initialize();
|
|
296386
296819
|
}
|
|
296387
|
-
this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver);
|
|
296820
|
+
this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker);
|
|
296388
296821
|
this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
|
|
296389
296822
|
if (err2) {
|
|
296390
296823
|
log.error(err2);
|
|
@@ -296399,6 +296832,15 @@ class CEXBroker {
|
|
|
296399
296832
|
metrics: this.otelMetrics
|
|
296400
296833
|
});
|
|
296401
296834
|
this.depositReconciler.start();
|
|
296835
|
+
if (this.brokerArchiver?.isEnabled()) {
|
|
296836
|
+
this.fillArchivePoller = new FillArchivePoller({
|
|
296837
|
+
brokers: this.brokers,
|
|
296838
|
+
archiver: this.brokerArchiver,
|
|
296839
|
+
tracker: this.orderActivityTracker,
|
|
296840
|
+
metrics: this.otelMetrics
|
|
296841
|
+
});
|
|
296842
|
+
this.fillArchivePoller.start();
|
|
296843
|
+
}
|
|
296402
296844
|
return this;
|
|
296403
296845
|
}
|
|
296404
296846
|
}
|
|
@@ -296406,4 +296848,4 @@ export {
|
|
|
296406
296848
|
CEXBroker as default
|
|
296407
296849
|
};
|
|
296408
296850
|
|
|
296409
|
-
//# debugId=
|
|
296851
|
+
//# debugId=6EFE98438C50664564756E2164756E21
|