@usherlabs/cex-broker 0.2.20 → 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 +434 -13
- 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 +435 -14
- 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;
|
|
@@ -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) {
|
|
315829
|
+
try {
|
|
315830
|
+
await this.otelMetrics?.recordCounter(metricName, value, labels);
|
|
315831
|
+
} catch {}
|
|
315832
|
+
}
|
|
315833
|
+
async recordArchiveGauge(metricName, value) {
|
|
315684
315834
|
try {
|
|
315685
|
-
await this.otelMetrics?.
|
|
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);
|
|
@@ -329955,7 +330258,13 @@ function rejectWithGrpcError(ctx, error48, options) {
|
|
|
329955
330258
|
|
|
329956
330259
|
// src/handlers/execute-action/deposit.ts
|
|
329957
330260
|
async function handleDeposit(ctx) {
|
|
329958
|
-
const {
|
|
330261
|
+
const {
|
|
330262
|
+
normalizedCex,
|
|
330263
|
+
symbol: symbol2,
|
|
330264
|
+
selectedBrokerAccount,
|
|
330265
|
+
broker,
|
|
330266
|
+
brokerArchiver
|
|
330267
|
+
} = ctx;
|
|
329959
330268
|
if (!symbol2) {
|
|
329960
330269
|
return ctx.wrappedCallback({
|
|
329961
330270
|
code: grpc3.status.INVALID_ARGUMENT,
|
|
@@ -330018,6 +330327,32 @@ async function handleDeposit(ctx) {
|
|
|
330018
330327
|
}, null);
|
|
330019
330328
|
}
|
|
330020
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
|
+
});
|
|
330021
330356
|
log.info(`Amount ${value.amount} at ${value.transactionHash} . Paid to ${value.recipientAddress}`);
|
|
330022
330357
|
return ctx.wrappedCallback(null, {
|
|
330023
330358
|
proof: ctx.verity.proof,
|
|
@@ -330431,7 +330766,8 @@ async function handleInternalTransfer(ctx) {
|
|
|
330431
330766
|
symbol: symbol2,
|
|
330432
330767
|
verity,
|
|
330433
330768
|
useVerity,
|
|
330434
|
-
verityProverUrl
|
|
330769
|
+
verityProverUrl,
|
|
330770
|
+
brokerArchiver
|
|
330435
330771
|
} = ctx;
|
|
330436
330772
|
if (!symbol2) {
|
|
330437
330773
|
return ctx.wrappedCallback({
|
|
@@ -330479,6 +330815,21 @@ async function handleInternalTransfer(ctx) {
|
|
|
330479
330815
|
}), verityHttpClientOverridePredicate);
|
|
330480
330816
|
}
|
|
330481
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
|
+
});
|
|
330482
330833
|
ctx.wrappedCallback(null, {
|
|
330483
330834
|
proof: verity.proof,
|
|
330484
330835
|
result: JSON.stringify(result)
|
|
@@ -330526,7 +330877,8 @@ async function handleCreateOrder(ctx) {
|
|
|
330526
330877
|
useVerity,
|
|
330527
330878
|
verityProverUrl,
|
|
330528
330879
|
otelMetrics,
|
|
330529
|
-
brokerArchiver
|
|
330880
|
+
brokerArchiver,
|
|
330881
|
+
orderActivityTracker
|
|
330530
330882
|
} = ctx;
|
|
330531
330883
|
const verityProof = verity.proof;
|
|
330532
330884
|
const orderValue = parsePayloadForAction(ctx, CreateOrderPayloadSchema);
|
|
@@ -330552,6 +330904,9 @@ async function handleCreateOrder(ctx) {
|
|
|
330552
330904
|
side: resolution.side,
|
|
330553
330905
|
requestedQuantity: resolution.amountBase ?? orderValue.amount
|
|
330554
330906
|
};
|
|
330907
|
+
if (selectedBrokerAccount?.label) {
|
|
330908
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, resolution.symbol);
|
|
330909
|
+
}
|
|
330555
330910
|
const telemetryIds = extractOrderTelemetryIds(orderValue.params);
|
|
330556
330911
|
const submissionTimestamp = new Date().toISOString();
|
|
330557
330912
|
const marketMetadataHash = await captureMarketMetadataSnapshot(brokerArchiver, broker, {
|
|
@@ -330616,7 +330971,8 @@ async function handleGetOrderDetails(ctx) {
|
|
|
330616
330971
|
useVerity,
|
|
330617
330972
|
verityProverUrl,
|
|
330618
330973
|
otelMetrics,
|
|
330619
|
-
brokerArchiver
|
|
330974
|
+
brokerArchiver,
|
|
330975
|
+
orderActivityTracker
|
|
330620
330976
|
} = ctx;
|
|
330621
330977
|
const verityProof = verity.proof;
|
|
330622
330978
|
const getOrderValue = parsePayloadForAction(ctx, GetOrderDetailsPayloadSchema);
|
|
@@ -330630,6 +330986,9 @@ async function handleGetOrderDetails(ctx) {
|
|
|
330630
330986
|
}, null);
|
|
330631
330987
|
}
|
|
330632
330988
|
const orderDetails = await broker.fetchOrder(getOrderValue.orderId, symbol2, { ...getOrderValue.params });
|
|
330989
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
330990
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
330991
|
+
}
|
|
330633
330992
|
const getOrderContext = {
|
|
330634
330993
|
action: "GetOrderDetails",
|
|
330635
330994
|
cex: cex3,
|
|
@@ -330685,7 +331044,8 @@ async function handleCancelOrder(ctx) {
|
|
|
330685
331044
|
useVerity,
|
|
330686
331045
|
verityProverUrl,
|
|
330687
331046
|
otelMetrics,
|
|
330688
|
-
brokerArchiver
|
|
331047
|
+
brokerArchiver,
|
|
331048
|
+
orderActivityTracker
|
|
330689
331049
|
} = ctx;
|
|
330690
331050
|
const verityProof = verity.proof;
|
|
330691
331051
|
const cancelOrderValue = parsePayloadForAction(ctx, CancelOrderPayloadSchema);
|
|
@@ -330706,6 +331066,9 @@ async function handleCancelOrder(ctx) {
|
|
|
330706
331066
|
...extractOrderTelemetryIds(cancelOrderValue.params)
|
|
330707
331067
|
};
|
|
330708
331068
|
const cancelledOrder = await broker.cancelOrder(cancelOrderValue.orderId, symbol2, cancelOrderValue.params ?? {});
|
|
331069
|
+
if (selectedBrokerAccount?.label && symbol2) {
|
|
331070
|
+
orderActivityTracker?.record(cex3, selectedBrokerAccount.label, symbol2);
|
|
331071
|
+
}
|
|
330709
331072
|
emitOrderExecutionTelemetryInBackground(otelMetrics, cancelOrderContext, cancelledOrder);
|
|
330710
331073
|
archiveOrderExecutionInBackground(brokerArchiver, cancelOrderContext, cancelledOrder);
|
|
330711
331074
|
ctx.wrappedCallback(null, {
|
|
@@ -331336,7 +331699,8 @@ async function handleWithdraw(ctx) {
|
|
|
331336
331699
|
applyVerityToBroker,
|
|
331337
331700
|
useVerity,
|
|
331338
331701
|
verityProverUrl,
|
|
331339
|
-
otelMetrics
|
|
331702
|
+
otelMetrics,
|
|
331703
|
+
brokerArchiver
|
|
331340
331704
|
} = ctx;
|
|
331341
331705
|
const verityProof = verity.proof;
|
|
331342
331706
|
if (!symbol2) {
|
|
@@ -331385,6 +331749,26 @@ async function handleWithdraw(ctx) {
|
|
|
331385
331749
|
network: withdrawNetwork.exchangeNetworkId
|
|
331386
331750
|
});
|
|
331387
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
|
+
});
|
|
331388
331772
|
ctx.wrappedCallback(null, {
|
|
331389
331773
|
proof: ctx.verity.proof,
|
|
331390
331774
|
result: JSON.stringify({
|
|
@@ -331396,6 +331780,21 @@ async function handleWithdraw(ctx) {
|
|
|
331396
331780
|
});
|
|
331397
331781
|
} catch (error48) {
|
|
331398
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
|
+
});
|
|
331399
331798
|
const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc10.status.INTERNAL;
|
|
331400
331799
|
ctx.wrappedCallback({
|
|
331401
331800
|
code,
|
|
@@ -331443,7 +331842,8 @@ function createExecuteActionHandler(deps) {
|
|
|
331443
331842
|
useVerity,
|
|
331444
331843
|
verityProverUrl,
|
|
331445
331844
|
otelMetrics,
|
|
331446
|
-
brokerArchiver
|
|
331845
|
+
brokerArchiver,
|
|
331846
|
+
orderActivityTracker
|
|
331447
331847
|
} = deps;
|
|
331448
331848
|
return async (call, callback) => {
|
|
331449
331849
|
const startTime = Date.now();
|
|
@@ -331522,7 +331922,8 @@ function createExecuteActionHandler(deps) {
|
|
|
331522
331922
|
useVerity,
|
|
331523
331923
|
verityProverUrl,
|
|
331524
331924
|
otelMetrics,
|
|
331525
|
-
brokerArchiver
|
|
331925
|
+
brokerArchiver,
|
|
331926
|
+
orderActivityTracker
|
|
331526
331927
|
};
|
|
331527
331928
|
if (action === Action.Call) {
|
|
331528
331929
|
const handled = await handleOrderBookCall(preludeCtx);
|
|
@@ -333111,7 +333512,7 @@ var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default
|
|
|
333111
333512
|
// src/server.ts
|
|
333112
333513
|
var grpcObj = grpc14.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
|
|
333113
333514
|
var cexNode = grpcObj.cex_broker;
|
|
333114
|
-
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver) {
|
|
333515
|
+
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker) {
|
|
333115
333516
|
const server = new grpc14.Server;
|
|
333116
333517
|
server.addService(cexNode.cex_service.service, {
|
|
333117
333518
|
ExecuteAction: createExecuteActionHandler({
|
|
@@ -333121,7 +333522,8 @@ function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, ot
|
|
|
333121
333522
|
useVerity,
|
|
333122
333523
|
verityProverUrl,
|
|
333123
333524
|
otelMetrics,
|
|
333124
|
-
brokerArchiver
|
|
333525
|
+
brokerArchiver,
|
|
333526
|
+
orderActivityTracker
|
|
333125
333527
|
}),
|
|
333126
333528
|
Subscribe: createSubscribeHandler({
|
|
333127
333529
|
brokers,
|
|
@@ -333266,6 +333668,8 @@ class CEXBroker {
|
|
|
333266
333668
|
otelLogs;
|
|
333267
333669
|
brokerArchiver;
|
|
333268
333670
|
depositReconciler;
|
|
333671
|
+
orderActivityTracker = new OrderActivityTracker;
|
|
333672
|
+
fillArchivePoller;
|
|
333269
333673
|
loadEnvConfig() {
|
|
333270
333674
|
log.info("\uD83D\uDD27 Loading CEX_BROKER_ environment variables:");
|
|
333271
333675
|
const configMap = {};
|
|
@@ -333406,6 +333810,10 @@ class CEXBroker {
|
|
|
333406
333810
|
this.depositReconciler.stop();
|
|
333407
333811
|
this.depositReconciler = undefined;
|
|
333408
333812
|
}
|
|
333813
|
+
if (this.fillArchivePoller) {
|
|
333814
|
+
this.fillArchivePoller.stop();
|
|
333815
|
+
this.fillArchivePoller = undefined;
|
|
333816
|
+
}
|
|
333409
333817
|
if (this.server) {
|
|
333410
333818
|
await this.server.forceShutdown();
|
|
333411
333819
|
}
|
|
@@ -333427,11 +333835,15 @@ class CEXBroker {
|
|
|
333427
333835
|
this.depositReconciler.stop();
|
|
333428
333836
|
this.depositReconciler = undefined;
|
|
333429
333837
|
}
|
|
333838
|
+
if (this.fillArchivePoller) {
|
|
333839
|
+
this.fillArchivePoller.stop();
|
|
333840
|
+
this.fillArchivePoller = undefined;
|
|
333841
|
+
}
|
|
333430
333842
|
log.info(`Running CEXBroker at ${new Date().toISOString()}`);
|
|
333431
333843
|
if (this.otelMetrics?.isOtelEnabled()) {
|
|
333432
333844
|
await this.otelMetrics.initialize();
|
|
333433
333845
|
}
|
|
333434
|
-
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);
|
|
333435
333847
|
this.server.bindAsync(`0.0.0.0:${this.port}`, grpc15.ServerCredentials.createInsecure(), (err2, port) => {
|
|
333436
333848
|
if (err2) {
|
|
333437
333849
|
log.error(err2);
|
|
@@ -333446,6 +333858,15 @@ class CEXBroker {
|
|
|
333446
333858
|
metrics: this.otelMetrics
|
|
333447
333859
|
});
|
|
333448
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
|
+
}
|
|
333449
333870
|
return this;
|
|
333450
333871
|
}
|
|
333451
333872
|
}
|
|
@@ -5,6 +5,7 @@ import type { z } from "zod";
|
|
|
5
5
|
import type { BrokerAccount, BrokerPoolEntry } from "../../helpers";
|
|
6
6
|
import type { BrokerExecutionArchiver } from "../../helpers/broker-execution-archive";
|
|
7
7
|
import type { Action as ActionType } from "../../helpers/constants";
|
|
8
|
+
import type { OrderActivityTracker } from "../../helpers/order-activity-tracker";
|
|
8
9
|
import type { OtelMetrics } from "../../helpers/otel";
|
|
9
10
|
import type { PolicyConfig } from "../../types";
|
|
10
11
|
import type { ActionRequest, ActionResponse } from "../types";
|
|
@@ -29,6 +30,7 @@ export type ExecuteActionContext = {
|
|
|
29
30
|
verityProverUrl: string;
|
|
30
31
|
otelMetrics?: OtelMetrics;
|
|
31
32
|
brokerArchiver?: BrokerExecutionArchiver;
|
|
33
|
+
orderActivityTracker?: OrderActivityTracker;
|
|
32
34
|
};
|
|
33
35
|
export declare function requireSymbol(ctx: ExecuteActionContext, message?: string): ctx is ExecuteActionContext & {
|
|
34
36
|
symbol: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as grpc from "@grpc/grpc-js";
|
|
2
2
|
import { type BrokerPoolEntry } from "../../helpers/broker";
|
|
3
3
|
import type { BrokerExecutionArchiver } from "../../helpers/broker-execution-archive";
|
|
4
|
+
import type { OrderActivityTracker } from "../../helpers/order-activity-tracker";
|
|
4
5
|
import type { OtelMetrics } from "../../helpers/otel";
|
|
5
6
|
import type { PolicyConfig } from "../../types";
|
|
6
7
|
import type { ActionRequest, ActionResponse } from "../types";
|
|
@@ -12,5 +13,6 @@ export type ExecuteActionDeps = {
|
|
|
12
13
|
verityProverUrl: string;
|
|
13
14
|
otelMetrics?: OtelMetrics;
|
|
14
15
|
brokerArchiver?: BrokerExecutionArchiver;
|
|
16
|
+
orderActivityTracker?: OrderActivityTracker;
|
|
15
17
|
};
|
|
16
18
|
export declare function createExecuteActionHandler(deps: ExecuteActionDeps): (call: grpc.ServerUnaryCall<ActionRequest, ActionResponse>, callback: grpc.sendUnaryData<ActionResponse>) => Promise<void>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Exchange } from "@usherlabs/ccxt";
|
|
2
2
|
import { type OrderTelemetryAction, type OrderTelemetryContext } from "../order-telemetry";
|
|
3
|
+
import { type TransferArchiveFields } from "./rows";
|
|
3
4
|
import type { SubscribeArchiveType } from "./types";
|
|
4
5
|
import type { BrokerExecutionArchiver } from "./writer";
|
|
5
6
|
export declare function archiveOrderExecutionInBackground(archiver: BrokerExecutionArchiver | undefined, context: OrderTelemetryContext, order: unknown, error?: unknown, options?: {
|
|
@@ -13,6 +14,13 @@ export declare function archiveSubscribeStreamInBackground(archiver: BrokerExecu
|
|
|
13
14
|
streamPayload: unknown;
|
|
14
15
|
secretLiterals?: readonly string[];
|
|
15
16
|
}): void;
|
|
17
|
+
export declare function archiveTransferEventInBackground(archiver: BrokerExecutionArchiver | undefined, input: {
|
|
18
|
+
exchange: string;
|
|
19
|
+
accountSelector?: string;
|
|
20
|
+
assetSymbol?: string;
|
|
21
|
+
brokerObservedTimestamp?: string;
|
|
22
|
+
transfer: TransferArchiveFields;
|
|
23
|
+
}): void;
|
|
16
24
|
export declare function captureMarketMetadataSnapshot(archiver: BrokerExecutionArchiver | undefined, broker: Exchange, input: {
|
|
17
25
|
exchange: string;
|
|
18
26
|
accountSelector?: string;
|