@usherlabs/cex-broker 0.3.0 → 0.3.1
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/README.md +116 -8
- package/dist/commands/cli.js +1151 -396
- package/dist/index.js +1152 -397
- package/dist/index.js.map +20 -16
- package/dist/proto/node.descriptor.ts +3 -1
- package/dist/proto/node.proto +2 -0
- package/package.json +1 -1
package/dist/commands/cli.js
CHANGED
|
@@ -74496,7 +74496,7 @@ var {
|
|
|
74496
74496
|
} = import__.default;
|
|
74497
74497
|
|
|
74498
74498
|
// src/index.ts
|
|
74499
|
-
var
|
|
74499
|
+
var grpc17 = __toESM(require_src3(), 1);
|
|
74500
74500
|
|
|
74501
74501
|
// node_modules/@usherlabs/ccxt/js/src/base/functions.js
|
|
74502
74502
|
var exports_functions = {};
|
|
@@ -314580,7 +314580,9 @@ var Action = {
|
|
|
314580
314580
|
FetchFees: 12,
|
|
314581
314581
|
InternalTransfer: 13,
|
|
314582
314582
|
GetPerpConfigState: 14,
|
|
314583
|
-
SetPerpConfigState: 15
|
|
314583
|
+
SetPerpConfigState: 15,
|
|
314584
|
+
FetchMarketRules: 16,
|
|
314585
|
+
Batch: 17
|
|
314584
314586
|
};
|
|
314585
314587
|
var SubscriptionType = {
|
|
314586
314588
|
NO_ACTION: 0,
|
|
@@ -315128,6 +315130,30 @@ function redactUnknownValue(value, secretLiterals) {
|
|
|
315128
315130
|
}
|
|
315129
315131
|
return String(value);
|
|
315130
315132
|
}
|
|
315133
|
+
function removeSecretMaterial(value, secretLiterals = []) {
|
|
315134
|
+
if (value === null || value === undefined) {
|
|
315135
|
+
return value;
|
|
315136
|
+
}
|
|
315137
|
+
if (typeof value === "string") {
|
|
315138
|
+
return redactSecretLiterals(value, secretLiterals);
|
|
315139
|
+
}
|
|
315140
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
315141
|
+
return value;
|
|
315142
|
+
}
|
|
315143
|
+
if (Array.isArray(value)) {
|
|
315144
|
+
return value.map((entry) => removeSecretMaterial(entry, secretLiterals));
|
|
315145
|
+
}
|
|
315146
|
+
if (typeof value === "object") {
|
|
315147
|
+
const clean = {};
|
|
315148
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
315149
|
+
if (!SECRET_KEY_PATTERN.test(key)) {
|
|
315150
|
+
clean[key] = removeSecretMaterial(entry, secretLiterals);
|
|
315151
|
+
}
|
|
315152
|
+
}
|
|
315153
|
+
return clean;
|
|
315154
|
+
}
|
|
315155
|
+
return String(value);
|
|
315156
|
+
}
|
|
315131
315157
|
function redactStreamPayload(payload, secretLiterals = []) {
|
|
315132
315158
|
if (Array.isArray(payload)) {
|
|
315133
315159
|
return {
|
|
@@ -317230,6 +317256,17 @@ function withTimeout(promise, timeoutMs, label) {
|
|
|
317230
317256
|
return Promise.race([promise, expiry]).finally(() => clearTimeout(timer));
|
|
317231
317257
|
}
|
|
317232
317258
|
var ALL_CURRENCIES_CODE = "*";
|
|
317259
|
+
var MAX_FAILURE_REASON_CHARS = 256;
|
|
317260
|
+
var DEFAULT_FAILURE_REASON = "fetchDeposits failed";
|
|
317261
|
+
function decimal(value) {
|
|
317262
|
+
return value === null || value === undefined ? null : String(value);
|
|
317263
|
+
}
|
|
317264
|
+
function failureReason(error, exchange) {
|
|
317265
|
+
const text = error instanceof Error ? error.message : typeof error === "string" ? error : "";
|
|
317266
|
+
const secrets = [exchange.apiKey, exchange.secret].filter((value) => typeof value === "string" && value.length > 0);
|
|
317267
|
+
const trimmed = redactSecretLiterals(text, secrets).replace(/\s+/g, " ").trim().slice(0, MAX_FAILURE_REASON_CHARS);
|
|
317268
|
+
return trimmed.length > 0 ? trimmed : DEFAULT_FAILURE_REASON;
|
|
317269
|
+
}
|
|
317233
317270
|
var BINANCE_UNLOCK_PROGRESS_SOURCE = {
|
|
317234
317271
|
venue: "binance",
|
|
317235
317272
|
endpoint: "GET /sapi/v1/capital/deposit/hisrec",
|
|
@@ -317240,6 +317277,20 @@ var BINANCE_UNLOCK_PROGRESS_SOURCE = {
|
|
|
317240
317277
|
completeTime: "info.completeTime"
|
|
317241
317278
|
}
|
|
317242
317279
|
};
|
|
317280
|
+
function observedProgress(progress) {
|
|
317281
|
+
if (progress === undefined)
|
|
317282
|
+
return null;
|
|
317283
|
+
return {
|
|
317284
|
+
state: progress.state,
|
|
317285
|
+
progress_state: progress.progress_state,
|
|
317286
|
+
reason: progress.reason,
|
|
317287
|
+
native_status: decimal(progress.native_status),
|
|
317288
|
+
current: decimal(progress.current),
|
|
317289
|
+
credit_required: decimal(progress.credit_required),
|
|
317290
|
+
unlock_required: decimal(progress.unlock_required),
|
|
317291
|
+
complete_time: decimal(progress.complete_time)
|
|
317292
|
+
};
|
|
317293
|
+
}
|
|
317243
317294
|
function depositTimestamp(record) {
|
|
317244
317295
|
const observedAt = depositField(record, [
|
|
317245
317296
|
"timestamp",
|
|
@@ -317469,6 +317520,7 @@ class DepositArchivePoller {
|
|
|
317469
317520
|
#cursors = new Map;
|
|
317470
317521
|
#lastArchivedByTarget = new Map;
|
|
317471
317522
|
#unsupportedLogged = new Set;
|
|
317523
|
+
#coverage = new Map;
|
|
317472
317524
|
#config;
|
|
317473
317525
|
constructor(params) {
|
|
317474
317526
|
this.params = params;
|
|
@@ -317479,6 +317531,7 @@ class DepositArchivePoller {
|
|
|
317479
317531
|
return;
|
|
317480
317532
|
}
|
|
317481
317533
|
log.info("\uD83D\uDCE5 Deposit archive poller started");
|
|
317534
|
+
this.params.coveragePublisher?.start();
|
|
317482
317535
|
this.#schedule(0);
|
|
317483
317536
|
}
|
|
317484
317537
|
async stop() {
|
|
@@ -317488,6 +317541,7 @@ class DepositArchivePoller {
|
|
|
317488
317541
|
this.#timer = null;
|
|
317489
317542
|
}
|
|
317490
317543
|
await this.#running;
|
|
317544
|
+
await this.params.coveragePublisher?.close(this.#coverageSnapshots());
|
|
317491
317545
|
}
|
|
317492
317546
|
async pollAllOnce() {
|
|
317493
317547
|
if (this.#stopped || this.#running || !this.params.archiver.isEnabled()) {
|
|
@@ -317523,14 +317577,90 @@ class DepositArchivePoller {
|
|
|
317523
317577
|
return true;
|
|
317524
317578
|
}
|
|
317525
317579
|
async#pollOne(target) {
|
|
317580
|
+
const attemptedAt = new Date().toISOString();
|
|
317526
317581
|
let outcome = "error";
|
|
317582
|
+
let observation;
|
|
317527
317583
|
try {
|
|
317528
|
-
|
|
317584
|
+
const result = await this.#pollTarget(target, attemptedAt);
|
|
317585
|
+
outcome = result.outcome;
|
|
317586
|
+
observation = result.observation;
|
|
317587
|
+
} catch (error) {
|
|
317588
|
+
observation = this.#observation("error", attemptedAt, {
|
|
317589
|
+
errorReason: failureReason(error, target.account.exchange)
|
|
317590
|
+
});
|
|
317591
|
+
throw error;
|
|
317529
317592
|
} finally {
|
|
317530
317593
|
this.params.metrics?.recordCounter("cex_deposit_poller_polls_total", 1, { exchange: target.exchangeId, outcome });
|
|
317594
|
+
this.#recordCoverage(target, outcome, observation ?? this.#observation("error", attemptedAt, {
|
|
317595
|
+
errorReason: "poll aborted before completion"
|
|
317596
|
+
}));
|
|
317531
317597
|
}
|
|
317532
317598
|
}
|
|
317533
|
-
|
|
317599
|
+
#observation(disposition, attemptedAt, detail = {}) {
|
|
317600
|
+
if (disposition === "success" && detail.completedAt !== undefined && detail.completedAt < attemptedAt) {
|
|
317601
|
+
return this.#observation("error", attemptedAt, {
|
|
317602
|
+
requestSinceMs: detail.requestSinceMs,
|
|
317603
|
+
errorReason: `source clock invalid: completed ${detail.completedAt} before attempted ${attemptedAt}`
|
|
317604
|
+
});
|
|
317605
|
+
}
|
|
317606
|
+
return {
|
|
317607
|
+
version: "1",
|
|
317608
|
+
disposition,
|
|
317609
|
+
attempted_at: attemptedAt,
|
|
317610
|
+
completed_at: disposition === "success" ? detail.completedAt ?? null : null,
|
|
317611
|
+
poll_interval_ms: String(this.#config.pollIntervalMs),
|
|
317612
|
+
fetch_timeout_ms: String(this.#config.fetchTimeoutMs),
|
|
317613
|
+
deposits_limit: String(this.#config.depositsLimit),
|
|
317614
|
+
request_since_ms: decimal(detail.requestSinceMs),
|
|
317615
|
+
next_cursor_ms: disposition === "success" ? decimal(detail.nextCursorMs) : null,
|
|
317616
|
+
response_truncated: disposition === "success" ? detail.responseTruncated ?? null : null,
|
|
317617
|
+
malformed_count: String(detail.malformedCount ?? 0),
|
|
317618
|
+
error_reason: disposition === "error" ? detail.errorReason?.trim() || DEFAULT_FAILURE_REASON : "",
|
|
317619
|
+
observed_deposits: disposition === "success" ? detail.observedDeposits ?? [] : []
|
|
317620
|
+
};
|
|
317621
|
+
}
|
|
317622
|
+
#recordCoverage(target, outcome, observation) {
|
|
317623
|
+
const publisher = this.params.coveragePublisher;
|
|
317624
|
+
if (!publisher)
|
|
317625
|
+
return;
|
|
317626
|
+
const key = this.#targetKey(target);
|
|
317627
|
+
const previous = this.#coverage.get(key);
|
|
317628
|
+
const now3 = observation.completed_at ?? new Date().toISOString();
|
|
317629
|
+
const state = outcome === "ok" ? "connected" : "error";
|
|
317630
|
+
const base2 = previous?.snapshot;
|
|
317631
|
+
const stateChangedAt = base2 && base2.state === state ? base2.stateChangedAt : now3;
|
|
317632
|
+
const attempts = BigInt(base2?.connectAttemptCount ?? "0") + 1n;
|
|
317633
|
+
const errors = BigInt(base2?.errorCount ?? "0") + (outcome === "error" ? 1n : 0n);
|
|
317634
|
+
const reconnects = BigInt(base2?.reconnectCount ?? "0") + (outcome === "ok" && previous?.lastOutcome === "error" ? 1n : 0n);
|
|
317635
|
+
const snapshot = {
|
|
317636
|
+
exchange: target.exchangeId,
|
|
317637
|
+
accountSelector: target.account.label,
|
|
317638
|
+
accountRole: target.account.role,
|
|
317639
|
+
streamKind: "deposit_poller",
|
|
317640
|
+
accountScope: "spot",
|
|
317641
|
+
registryStatus: "active",
|
|
317642
|
+
retiredAt: null,
|
|
317643
|
+
state,
|
|
317644
|
+
stateChangedAt,
|
|
317645
|
+
lastConnectedAt: outcome === "ok" ? observation.completed_at : base2?.lastConnectedAt ?? null,
|
|
317646
|
+
lastAuthenticatedAt: null,
|
|
317647
|
+
lastReceivedAt: outcome === "ok" ? observation.completed_at : base2?.lastReceivedAt ?? null,
|
|
317648
|
+
connectAttemptCount: attempts.toString(),
|
|
317649
|
+
reconnectCount: reconnects.toString(),
|
|
317650
|
+
errorCount: errors.toString(),
|
|
317651
|
+
lastFailureKind: outcome === "ok" ? "none" : outcome === "unsupported" ? "unsupported_connector" : "transport_error",
|
|
317652
|
+
lastFailureReason: outcome === "ok" ? "" : outcome === "unsupported" ? "fetchDeposits unsupported" : observation.error_reason,
|
|
317653
|
+
trafficMode: "continuous",
|
|
317654
|
+
sourceWatermark: null,
|
|
317655
|
+
pollObservation: observation
|
|
317656
|
+
};
|
|
317657
|
+
this.#coverage.set(key, { snapshot, lastOutcome: outcome });
|
|
317658
|
+
publisher.publish(this.#coverageSnapshots());
|
|
317659
|
+
}
|
|
317660
|
+
#coverageSnapshots() {
|
|
317661
|
+
return [...this.#coverage.values()].map((entry) => entry.snapshot);
|
|
317662
|
+
}
|
|
317663
|
+
async#pollTarget(target, attemptedAt) {
|
|
317534
317664
|
const exchange = target.account.exchange;
|
|
317535
317665
|
const key = this.#targetKey(target);
|
|
317536
317666
|
if (typeof exchange.fetchDeposits !== "function" || exchange.has?.fetchDeposits === false) {
|
|
@@ -317541,7 +317671,10 @@ class DepositArchivePoller {
|
|
|
317541
317671
|
account: target.account.label
|
|
317542
317672
|
});
|
|
317543
317673
|
}
|
|
317544
|
-
return
|
|
317674
|
+
return {
|
|
317675
|
+
outcome: "unsupported",
|
|
317676
|
+
observation: this.#observation("unsupported", attemptedAt)
|
|
317677
|
+
};
|
|
317545
317678
|
}
|
|
317546
317679
|
const since = this.#cursors.get(key) ?? Date.now() - this.#config.lookbackMs;
|
|
317547
317680
|
let deposits;
|
|
@@ -317554,15 +317687,48 @@ class DepositArchivePoller {
|
|
|
317554
317687
|
account: target.account.label,
|
|
317555
317688
|
error
|
|
317556
317689
|
});
|
|
317557
|
-
return
|
|
317690
|
+
return {
|
|
317691
|
+
outcome: "error",
|
|
317692
|
+
observation: this.#observation("error", attemptedAt, {
|
|
317693
|
+
requestSinceMs: since,
|
|
317694
|
+
errorReason: failureReason(error, exchange)
|
|
317695
|
+
})
|
|
317696
|
+
};
|
|
317558
317697
|
}
|
|
317559
|
-
if (!Array.isArray(deposits)
|
|
317560
|
-
return
|
|
317698
|
+
if (!Array.isArray(deposits)) {
|
|
317699
|
+
return {
|
|
317700
|
+
outcome: "error",
|
|
317701
|
+
observation: this.#observation("error", attemptedAt, {
|
|
317702
|
+
requestSinceMs: since,
|
|
317703
|
+
errorReason: "fetchDeposits returned a non-array response"
|
|
317704
|
+
})
|
|
317705
|
+
};
|
|
317706
|
+
}
|
|
317707
|
+
if (deposits.length > this.#config.depositsLimit) {
|
|
317708
|
+
return {
|
|
317709
|
+
outcome: "error",
|
|
317710
|
+
observation: this.#observation("error", attemptedAt, {
|
|
317711
|
+
requestSinceMs: since,
|
|
317712
|
+
errorReason: `fetchDeposits returned ${deposits.length} rows for a limit of ${this.#config.depositsLimit}`
|
|
317713
|
+
})
|
|
317714
|
+
};
|
|
317715
|
+
}
|
|
317716
|
+
if (deposits.length === 0) {
|
|
317717
|
+
return this.#completed(this.#observation("success", attemptedAt, {
|
|
317718
|
+
completedAt: new Date().toISOString(),
|
|
317719
|
+
requestSinceMs: since,
|
|
317720
|
+
nextCursorMs: this.#cursors.get(key),
|
|
317721
|
+
responseTruncated: false,
|
|
317722
|
+
observedDeposits: []
|
|
317723
|
+
}));
|
|
317561
317724
|
}
|
|
317562
317725
|
let archived = 0;
|
|
317726
|
+
let malformed = 0;
|
|
317727
|
+
const observed = [];
|
|
317563
317728
|
for (const deposit of deposits) {
|
|
317564
317729
|
const record = asRecord(deposit);
|
|
317565
317730
|
if (!record) {
|
|
317731
|
+
malformed += 1;
|
|
317566
317732
|
continue;
|
|
317567
317733
|
}
|
|
317568
317734
|
const info = asRecord(record.info);
|
|
@@ -317587,6 +317753,16 @@ class DepositArchivePoller {
|
|
|
317587
317753
|
});
|
|
317588
317754
|
const lastArchived = identity === undefined ? undefined : this.#lastArchivedByTarget.get(key)?.get(identity);
|
|
317589
317755
|
const classification = classifyDeposit(target.exchangeId, record, lastArchived);
|
|
317756
|
+
const depositTimestampMs = depositTimestamp(record);
|
|
317757
|
+
observed.push({
|
|
317758
|
+
coin: assetSymbol === undefined ? "" : String(assetSymbol),
|
|
317759
|
+
network: network === undefined ? "" : String(network),
|
|
317760
|
+
external_id: depositTxid ?? "",
|
|
317761
|
+
txid: depositTxid ?? "",
|
|
317762
|
+
deposit_timestamp_ms: decimal(depositTimestampMs),
|
|
317763
|
+
status: classification.archiveStatus,
|
|
317764
|
+
progress: observedProgress(classification.unlockProgress)
|
|
317765
|
+
});
|
|
317590
317766
|
if (lastArchived && lastArchived.status === classification.archiveStatus && lastArchived.progressKey === classification.progressKey) {
|
|
317591
317767
|
continue;
|
|
317592
317768
|
}
|
|
@@ -317627,7 +317803,7 @@ class DepositArchivePoller {
|
|
|
317627
317803
|
}
|
|
317628
317804
|
targetDeposits2.set(identity, {
|
|
317629
317805
|
status: classification.archiveStatus,
|
|
317630
|
-
timestamp:
|
|
317806
|
+
timestamp: depositTimestampMs,
|
|
317631
317807
|
progressKey: classification.progressKey,
|
|
317632
317808
|
highWatermark: classification.highWatermark
|
|
317633
317809
|
});
|
|
@@ -317650,7 +317826,20 @@ class DepositArchivePoller {
|
|
|
317650
317826
|
this.#lastArchivedByTarget.delete(key);
|
|
317651
317827
|
}
|
|
317652
317828
|
}
|
|
317653
|
-
return "
|
|
317829
|
+
return this.#completed(this.#observation("success", attemptedAt, {
|
|
317830
|
+
completedAt: new Date().toISOString(),
|
|
317831
|
+
requestSinceMs: since,
|
|
317832
|
+
nextCursorMs: nextCursor,
|
|
317833
|
+
responseTruncated: deposits.length >= this.#config.depositsLimit,
|
|
317834
|
+
malformedCount: malformed,
|
|
317835
|
+
observedDeposits: observed
|
|
317836
|
+
}));
|
|
317837
|
+
}
|
|
317838
|
+
#completed(observation) {
|
|
317839
|
+
return {
|
|
317840
|
+
outcome: observation.disposition === "success" ? "ok" : "error",
|
|
317841
|
+
observation
|
|
317842
|
+
};
|
|
317654
317843
|
}
|
|
317655
317844
|
#targetKey(target) {
|
|
317656
317845
|
return `${target.exchangeId}|${target.account.label}|${target.code}`;
|
|
@@ -320528,7 +320717,8 @@ import { request as httpsRequest3 } from "node:https";
|
|
|
320528
320717
|
import { dirname as dirname2 } from "node:path";
|
|
320529
320718
|
var SOURCE = "broker_write";
|
|
320530
320719
|
var TABLE = "broker_stream_health.snapshots";
|
|
320531
|
-
var
|
|
320720
|
+
var USER_DATA_STREAM_HEALTH_PRODUCER_ID = "cex-broker-user-data";
|
|
320721
|
+
var DEPOSIT_POLLER_STREAM_HEALTH_PRODUCER_ID = "cex-broker-deposit-poller";
|
|
320532
320722
|
var STATE_VERSION = 1;
|
|
320533
320723
|
var HEARTBEAT_MS = 30000;
|
|
320534
320724
|
var FORWARDER_TIMEOUT_MS = 3000;
|
|
@@ -320563,11 +320753,11 @@ function registryRevision(snapshots) {
|
|
|
320563
320753
|
})).sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
320564
320754
|
return createHash5("sha256").update(JSON.stringify(rows)).digest("hex");
|
|
320565
320755
|
}
|
|
320566
|
-
function validState(value) {
|
|
320756
|
+
function validState(value, producerId) {
|
|
320567
320757
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
320568
320758
|
return false;
|
|
320569
320759
|
const state = value;
|
|
320570
|
-
return state.version === STATE_VERSION && state.producerId ===
|
|
320760
|
+
return state.version === STATE_VERSION && state.producerId === producerId && typeof state.producerEpoch === "string" && typeof state.runId === "string" && typeof state.nextBatchSequence === "string" && state.nextStreamSequences !== null && typeof state.nextStreamSequences === "object" && (state.pendingBody === undefined || typeof state.pendingBody === "string");
|
|
320571
320761
|
}
|
|
320572
320762
|
function forwarderPost(url2, body, authToken, timeoutMs) {
|
|
320573
320763
|
const request = url2.protocol === "http:" ? httpRequest3 : httpsRequest3;
|
|
@@ -320597,6 +320787,7 @@ function forwarderPost(url2, body, authToken, timeoutMs) {
|
|
|
320597
320787
|
}
|
|
320598
320788
|
|
|
320599
320789
|
class StreamHealthPublisher {
|
|
320790
|
+
#producerId;
|
|
320600
320791
|
#deploymentId;
|
|
320601
320792
|
#statePath;
|
|
320602
320793
|
#heartbeatMs;
|
|
@@ -320611,6 +320802,7 @@ class StreamHealthPublisher {
|
|
|
320611
320802
|
#retry = null;
|
|
320612
320803
|
#retryAttempt = 0;
|
|
320613
320804
|
constructor(options) {
|
|
320805
|
+
this.#producerId = identifier(options.producerId, "producer_id");
|
|
320614
320806
|
this.#deploymentId = identifier(options.deploymentId, "deployment_id");
|
|
320615
320807
|
this.#statePath = options.statePath.trim();
|
|
320616
320808
|
if (!this.#statePath) {
|
|
@@ -320635,7 +320827,7 @@ class StreamHealthPublisher {
|
|
|
320635
320827
|
const loaded = this.#read();
|
|
320636
320828
|
this.#state = loaded ?? {
|
|
320637
320829
|
version: STATE_VERSION,
|
|
320638
|
-
producerId:
|
|
320830
|
+
producerId: this.#producerId,
|
|
320639
320831
|
producerEpoch: "1",
|
|
320640
320832
|
runId: randomUUID3(),
|
|
320641
320833
|
nextBatchSequence: "1",
|
|
@@ -320748,7 +320940,7 @@ class StreamHealthPublisher {
|
|
|
320748
320940
|
return {
|
|
320749
320941
|
table: TABLE,
|
|
320750
320942
|
row: {
|
|
320751
|
-
producer_id:
|
|
320943
|
+
producer_id: this.#producerId,
|
|
320752
320944
|
producer_epoch: this.#state.producerEpoch,
|
|
320753
320945
|
run_id: this.#state.runId,
|
|
320754
320946
|
batch_sequence: batchSequence,
|
|
@@ -320775,7 +320967,8 @@ class StreamHealthPublisher {
|
|
|
320775
320967
|
last_failure_kind: snapshot.lastFailureKind,
|
|
320776
320968
|
last_failure_reason: snapshot.lastFailureReason,
|
|
320777
320969
|
traffic_mode: snapshot.trafficMode,
|
|
320778
|
-
source_watermark: snapshot.sourceWatermark
|
|
320970
|
+
source_watermark: snapshot.sourceWatermark,
|
|
320971
|
+
...snapshot.streamKind === "deposit_poller" ? { poll_observation: snapshot.pollObservation } : {}
|
|
320779
320972
|
}
|
|
320780
320973
|
};
|
|
320781
320974
|
});
|
|
@@ -320803,7 +320996,7 @@ class StreamHealthPublisher {
|
|
|
320803
320996
|
#read() {
|
|
320804
320997
|
try {
|
|
320805
320998
|
const parsed = JSON.parse(readFileSync2(this.#statePath, "utf8"));
|
|
320806
|
-
if (!validState(parsed))
|
|
320999
|
+
if (!validState(parsed, this.#producerId))
|
|
320807
321000
|
throw new Error("invalid state shape");
|
|
320808
321001
|
return parsed;
|
|
320809
321002
|
} catch (error) {
|
|
@@ -320866,6 +321059,14 @@ function streamHealthPublisherConfigFromEnv(env = process.env) {
|
|
|
320866
321059
|
forwarderAuthToken: env.CEX_BROKER_ARCHIVE_FORWARDER_TOKEN?.trim() || undefined
|
|
320867
321060
|
};
|
|
320868
321061
|
}
|
|
321062
|
+
function depositPollerStreamHealthPublisherConfigFromEnv(env = process.env) {
|
|
321063
|
+
const base2 = streamHealthPublisherConfigFromEnv(env);
|
|
321064
|
+
return {
|
|
321065
|
+
...base2,
|
|
321066
|
+
producerId: DEPOSIT_POLLER_STREAM_HEALTH_PRODUCER_ID,
|
|
321067
|
+
statePath: `${base2.statePath}.deposit-poller`
|
|
321068
|
+
};
|
|
321069
|
+
}
|
|
320869
321070
|
|
|
320870
321071
|
// src/helpers/user-asset-archive-poller.ts
|
|
320871
321072
|
var DEFAULT_CONFIG4 = {
|
|
@@ -321436,7 +321637,7 @@ class AccountWorker {
|
|
|
321436
321637
|
for (const subscriber of [...this.#subscribers])
|
|
321437
321638
|
subscriber.close();
|
|
321438
321639
|
}
|
|
321439
|
-
#transition(state, failureKind,
|
|
321640
|
+
#transition(state, failureKind, failureReason2) {
|
|
321440
321641
|
const timestamp = now3();
|
|
321441
321642
|
if (this.#snapshot.state !== state) {
|
|
321442
321643
|
this.#snapshot.state = state;
|
|
@@ -321444,7 +321645,7 @@ class AccountWorker {
|
|
|
321444
321645
|
}
|
|
321445
321646
|
if (failureKind) {
|
|
321446
321647
|
this.#snapshot.lastFailureKind = failureKind;
|
|
321447
|
-
this.#snapshot.lastFailureReason =
|
|
321648
|
+
this.#snapshot.lastFailureReason = failureReason2 ?? "";
|
|
321448
321649
|
}
|
|
321449
321650
|
this.onChange();
|
|
321450
321651
|
}
|
|
@@ -321568,7 +321769,7 @@ class UserDataStreamSupervisor {
|
|
|
321568
321769
|
}
|
|
321569
321770
|
|
|
321570
321771
|
// src/server.ts
|
|
321571
|
-
var
|
|
321772
|
+
var grpc16 = __toESM(require_src3(), 1);
|
|
321572
321773
|
|
|
321573
321774
|
// src/handlers/execute-action/deposit.ts
|
|
321574
321775
|
var grpc3 = __toESM(require_src3(), 1);
|
|
@@ -335220,7 +335421,10 @@ config(en_default());
|
|
|
335220
335421
|
// src/schemas/action-payloads.ts
|
|
335221
335422
|
var parseJsonString = (value) => {
|
|
335222
335423
|
if (typeof value !== "string") {
|
|
335223
|
-
|
|
335424
|
+
if (value === null || value === undefined || typeof value === "number" || typeof value === "boolean" || typeof value === "object") {
|
|
335425
|
+
return value;
|
|
335426
|
+
}
|
|
335427
|
+
return String(value);
|
|
335224
335428
|
}
|
|
335225
335429
|
try {
|
|
335226
335430
|
return JSON.parse(value);
|
|
@@ -335229,19 +335433,6 @@ var parseJsonString = (value) => {
|
|
|
335229
335433
|
}
|
|
335230
335434
|
};
|
|
335231
335435
|
var stringNumberRecordSchema = exports_external.record(exports_external.string(), exports_external.union([exports_external.string(), exports_external.number()]));
|
|
335232
|
-
var booleanLikeSchema = exports_external.preprocess((value) => {
|
|
335233
|
-
if (typeof value !== "string") {
|
|
335234
|
-
return value;
|
|
335235
|
-
}
|
|
335236
|
-
const normalized = value.trim().toLowerCase();
|
|
335237
|
-
if (["true", "1", "yes"].includes(normalized)) {
|
|
335238
|
-
return true;
|
|
335239
|
-
}
|
|
335240
|
-
if (["false", "0", "no"].includes(normalized)) {
|
|
335241
|
-
return false;
|
|
335242
|
-
}
|
|
335243
|
-
return value;
|
|
335244
|
-
}, exports_external.boolean());
|
|
335245
335436
|
var DepositPayloadSchema = exports_external.object({
|
|
335246
335437
|
recipientAddress: exports_external.string().min(1),
|
|
335247
335438
|
amount: exports_external.coerce.number().positive(),
|
|
@@ -335302,10 +335493,22 @@ var CancelOrderPayloadSchema = exports_external.object({
|
|
|
335302
335493
|
orderId: exports_external.string().min(1),
|
|
335303
335494
|
params: exports_external.preprocess(parseJsonString, stringNumberRecordSchema).default({})
|
|
335304
335495
|
});
|
|
335305
|
-
var
|
|
335306
|
-
|
|
335307
|
-
|
|
335308
|
-
|
|
335496
|
+
var EmptyActionPayloadSchema = exports_external.object({}).strict();
|
|
335497
|
+
var FetchFeesPayloadSchema = EmptyActionPayloadSchema;
|
|
335498
|
+
var FetchCurrencyPayloadSchema = exports_external.object({
|
|
335499
|
+
network: exports_external.string().trim().min(1)
|
|
335500
|
+
}).strict();
|
|
335501
|
+
var MAX_BATCH_CHILDREN = 32;
|
|
335502
|
+
var MAX_BATCH_REQUEST_BYTES = 256 * 1024;
|
|
335503
|
+
var BatchChildRequestSchema = exports_external.object({
|
|
335504
|
+
id: exports_external.string().trim().min(1),
|
|
335505
|
+
action: exports_external.number().int().nonnegative(),
|
|
335506
|
+
symbol: exports_external.string(),
|
|
335507
|
+
payload: exports_external.record(exports_external.string(), exports_external.string())
|
|
335508
|
+
}).strict();
|
|
335509
|
+
var BatchPayloadSchema = exports_external.object({
|
|
335510
|
+
requests: exports_external.preprocess(parseJsonString, exports_external.array(BatchChildRequestSchema).min(1).max(MAX_BATCH_CHILDREN))
|
|
335511
|
+
}).strict();
|
|
335309
335512
|
|
|
335310
335513
|
// src/helpers/grpc/callbacks.ts
|
|
335311
335514
|
var grpc = __toESM(require_src3(), 1);
|
|
@@ -335354,7 +335557,7 @@ function stableGrpcErrorCode(message) {
|
|
|
335354
335557
|
if (message.startsWith("AuthenticationError:")) {
|
|
335355
335558
|
return grpc2.status.UNAUTHENTICATED;
|
|
335356
335559
|
}
|
|
335357
|
-
if (message.startsWith("InsufficientFunds:")) {
|
|
335560
|
+
if (message.startsWith("InsufficientFunds:") || message.startsWith("fee_unavailable:")) {
|
|
335358
335561
|
return grpc2.status.FAILED_PRECONDITION;
|
|
335359
335562
|
}
|
|
335360
335563
|
if (message.startsWith("venue_discovery_unavailable:")) {
|
|
@@ -335414,6 +335617,13 @@ function resolveGrpcError(error48, message) {
|
|
|
335414
335617
|
}
|
|
335415
335618
|
|
|
335416
335619
|
// src/handlers/execute-action/context.ts
|
|
335620
|
+
function requireSymbol(ctx, message = "ValidationError: Symbol required") {
|
|
335621
|
+
if (!ctx.symbol) {
|
|
335622
|
+
ctx.wrappedCallback(invalidArgumentError(message), null);
|
|
335623
|
+
return false;
|
|
335624
|
+
}
|
|
335625
|
+
return true;
|
|
335626
|
+
}
|
|
335417
335627
|
function parsePayloadForAction(ctx, schema) {
|
|
335418
335628
|
return parseActionPayload(schema, ctx.call.request.payload, ctx.wrappedCallback);
|
|
335419
335629
|
}
|
|
@@ -335436,6 +335646,12 @@ function rejectWithGrpcError(ctx, error48, options) {
|
|
|
335436
335646
|
}
|
|
335437
335647
|
ctx.wrappedCallback({ code, message: finalMessage }, null);
|
|
335438
335648
|
}
|
|
335649
|
+
function successWithProof(ctx, result) {
|
|
335650
|
+
ctx.wrappedCallback(null, {
|
|
335651
|
+
proof: ctx.verity.proof,
|
|
335652
|
+
result: JSON.stringify(result)
|
|
335653
|
+
});
|
|
335654
|
+
}
|
|
335439
335655
|
|
|
335440
335656
|
// src/handlers/execute-action/deposit.ts
|
|
335441
335657
|
async function handleDeposit(ctx) {
|
|
@@ -335613,7 +335829,7 @@ async function handleDeposit(ctx) {
|
|
|
335613
335829
|
}
|
|
335614
335830
|
}
|
|
335615
335831
|
// src/handlers/execute-action/handler.ts
|
|
335616
|
-
var
|
|
335832
|
+
var grpc14 = __toESM(require_src3(), 1);
|
|
335617
335833
|
|
|
335618
335834
|
// src/helpers/grpc/broker.ts
|
|
335619
335835
|
function selectBrokerAccountForCex(normalizedCex, brokers, metadata) {
|
|
@@ -335745,10 +335961,458 @@ async function handleOrderBookCall(ctx) {
|
|
|
335745
335961
|
}
|
|
335746
335962
|
|
|
335747
335963
|
// src/handlers/execute-action/registry.ts
|
|
335748
|
-
var
|
|
335964
|
+
var grpc13 = __toESM(require_src3(), 1);
|
|
335749
335965
|
|
|
335750
|
-
// src/handlers/execute-action/
|
|
335966
|
+
// src/handlers/execute-action/batch.ts
|
|
335751
335967
|
var grpc5 = __toESM(require_src3(), 1);
|
|
335968
|
+
|
|
335969
|
+
// src/helpers/venue-evidence.ts
|
|
335970
|
+
var DECIMAL_PATTERN = /^\+?(\d+)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/;
|
|
335971
|
+
function exchangeSecretLiterals(broker) {
|
|
335972
|
+
const record2 = broker;
|
|
335973
|
+
return [
|
|
335974
|
+
record2.apiKey,
|
|
335975
|
+
record2.secret,
|
|
335976
|
+
record2.password,
|
|
335977
|
+
record2.privateKey
|
|
335978
|
+
].filter((value) => typeof value === "string" && value.length > 0);
|
|
335979
|
+
}
|
|
335980
|
+
function sanitizeVenueError(error48, broker) {
|
|
335981
|
+
return redactSecretLiterals(sanitizeErrorDetail(error48), exchangeSecretLiterals(broker));
|
|
335982
|
+
}
|
|
335983
|
+
function resolveEvidenceAccountScope(selectedBrokerAccount, metadata) {
|
|
335984
|
+
return {
|
|
335985
|
+
accountSelector: selectedBrokerAccount?.label ?? getCurrentBrokerSelector(metadata),
|
|
335986
|
+
credentialSource: selectedBrokerAccount ? "configured_pool" : "request_metadata"
|
|
335987
|
+
};
|
|
335988
|
+
}
|
|
335989
|
+
function canonicalNonnegativeDecimal(value, field) {
|
|
335990
|
+
const text = typeof value === "number" ? Number.isFinite(value) ? String(value) : "" : typeof value === "string" ? value.trim() : "";
|
|
335991
|
+
const match = text.match(DECIMAL_PATTERN);
|
|
335992
|
+
if (!match) {
|
|
335993
|
+
throw new Error(`venue_discovery_unavailable: ${field} must be decimal`);
|
|
335994
|
+
}
|
|
335995
|
+
const integer2 = match[1] ?? "0";
|
|
335996
|
+
const fraction = match[2] ?? "";
|
|
335997
|
+
const exponent = Number.parseInt(match[3] ?? "0", 10);
|
|
335998
|
+
if (!Number.isSafeInteger(exponent)) {
|
|
335999
|
+
throw new Error(`venue_discovery_unavailable: ${field} exponent is invalid`);
|
|
336000
|
+
}
|
|
336001
|
+
const digits = `${integer2}${fraction}`;
|
|
336002
|
+
const decimalIndex = integer2.length + exponent;
|
|
336003
|
+
let rendered;
|
|
336004
|
+
if (decimalIndex <= 0) {
|
|
336005
|
+
rendered = `0.${"0".repeat(-decimalIndex)}${digits}`;
|
|
336006
|
+
} else if (decimalIndex >= digits.length) {
|
|
336007
|
+
rendered = `${digits}${"0".repeat(decimalIndex - digits.length)}`;
|
|
336008
|
+
} else {
|
|
336009
|
+
rendered = `${digits.slice(0, decimalIndex)}.${digits.slice(decimalIndex)}`;
|
|
336010
|
+
}
|
|
336011
|
+
const [whole = "0", decimals = ""] = rendered.split(".");
|
|
336012
|
+
const normalizedWhole = whole.replace(/^0+(?=\d)/, "") || "0";
|
|
336013
|
+
const normalizedDecimals = decimals.replace(/0+$/, "");
|
|
336014
|
+
return normalizedDecimals ? `${normalizedWhole}.${normalizedDecimals}` : normalizedWhole;
|
|
336015
|
+
}
|
|
336016
|
+
function decimalFractionToBasisPoints(value) {
|
|
336017
|
+
const canonical = canonicalNonnegativeDecimal(value, "fee rate");
|
|
336018
|
+
return canonicalNonnegativeDecimal(`${canonical}e4`, "fee basis points");
|
|
336019
|
+
}
|
|
336020
|
+
function canonicalOptionalDecimal(value, field) {
|
|
336021
|
+
return value === undefined || value === null ? undefined : canonicalNonnegativeDecimal(value, field);
|
|
336022
|
+
}
|
|
336023
|
+
function precisionIncrement(value, precisionMode, field) {
|
|
336024
|
+
if ((precisionMode === 2 || precisionMode === "DECIMAL_PLACES") && (typeof value === "number" || typeof value === "string")) {
|
|
336025
|
+
const decimalPlaces = Number(value);
|
|
336026
|
+
if (Number.isInteger(decimalPlaces) && decimalPlaces >= 0) {
|
|
336027
|
+
return canonicalNonnegativeDecimal(`1e-${decimalPlaces}`, field);
|
|
336028
|
+
}
|
|
336029
|
+
}
|
|
336030
|
+
return canonicalNonnegativeDecimal(value, field);
|
|
336031
|
+
}
|
|
336032
|
+
function evidenceSourceDigest(input) {
|
|
336033
|
+
const source = removeSecretMaterial(input.source, exchangeSecretLiterals(input.broker));
|
|
336034
|
+
return sha256Canonical({
|
|
336035
|
+
action: input.action,
|
|
336036
|
+
exchange: input.exchange,
|
|
336037
|
+
requestedKey: input.requestedKey,
|
|
336038
|
+
accountSelector: input.accountSelector,
|
|
336039
|
+
sourceMethod: input.sourceMethod,
|
|
336040
|
+
source
|
|
336041
|
+
});
|
|
336042
|
+
}
|
|
336043
|
+
async function resolveSpotMarketIdentity(broker, symbol2) {
|
|
336044
|
+
const requestedSymbol = symbol2.trim().toUpperCase();
|
|
336045
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(requestedSymbol)) {
|
|
336046
|
+
throw new Error("venue_discovery_unavailable: symbol must be a slash-delimited spot pair");
|
|
336047
|
+
}
|
|
336048
|
+
await broker.loadMarkets();
|
|
336049
|
+
const market = broker.market(requestedSymbol);
|
|
336050
|
+
if (!isRecord(market)) {
|
|
336051
|
+
throw new Error(`venue_discovery_unavailable: market not found for ${requestedSymbol}`);
|
|
336052
|
+
}
|
|
336053
|
+
const unifiedSymbol = typeof market.symbol === "string" ? market.symbol.trim().toUpperCase() : requestedSymbol;
|
|
336054
|
+
const baseAsset = typeof market.base === "string" ? market.base.trim().toUpperCase() : "";
|
|
336055
|
+
const quoteAsset = typeof market.quote === "string" ? market.quote.trim().toUpperCase() : "";
|
|
336056
|
+
const sourceSymbol = typeof market.id === "string" ? market.id.trim() : "";
|
|
336057
|
+
const isSpot = market.spot === true || market.type === "spot";
|
|
336058
|
+
if (unifiedSymbol !== requestedSymbol || !baseAsset || !quoteAsset || !sourceSymbol || !isSpot || market.active !== true) {
|
|
336059
|
+
throw new Error(`venue_discovery_unavailable: active spot market identity unavailable for ${requestedSymbol}`);
|
|
336060
|
+
}
|
|
336061
|
+
return {
|
|
336062
|
+
market,
|
|
336063
|
+
canonicalPair: `${baseAsset}-${quoteAsset}`,
|
|
336064
|
+
unifiedSymbol,
|
|
336065
|
+
sourceSymbol,
|
|
336066
|
+
baseAsset,
|
|
336067
|
+
quoteAsset
|
|
336068
|
+
};
|
|
336069
|
+
}
|
|
336070
|
+
function extractTradingFeeRates(response) {
|
|
336071
|
+
if (!isRecord(response)) {
|
|
336072
|
+
throw new Error("fee_unavailable: trading-fee response is not an object");
|
|
336073
|
+
}
|
|
336074
|
+
const info = isRecord(response.info) ? response.info : undefined;
|
|
336075
|
+
const nestedData = isRecord(info?.data) ? info.data : isRecord(response.data) ? response.data : undefined;
|
|
336076
|
+
const maker = [
|
|
336077
|
+
response.maker,
|
|
336078
|
+
response.makerCommission,
|
|
336079
|
+
nestedData?.maker,
|
|
336080
|
+
nestedData?.makerCommission
|
|
336081
|
+
].find((value) => value !== undefined && value !== null);
|
|
336082
|
+
const taker = [
|
|
336083
|
+
response.taker,
|
|
336084
|
+
response.takerCommission,
|
|
336085
|
+
nestedData?.taker,
|
|
336086
|
+
nestedData?.takerCommission
|
|
336087
|
+
].find((value) => value !== undefined && value !== null);
|
|
336088
|
+
try {
|
|
336089
|
+
return {
|
|
336090
|
+
makerRate: canonicalNonnegativeDecimal(maker, "maker commission"),
|
|
336091
|
+
takerRate: canonicalNonnegativeDecimal(taker, "taker commission")
|
|
336092
|
+
};
|
|
336093
|
+
} catch (error48) {
|
|
336094
|
+
throw new Error(`fee_unavailable: ${sanitizeErrorDetail(error48)}`, {
|
|
336095
|
+
cause: error48
|
|
336096
|
+
});
|
|
336097
|
+
}
|
|
336098
|
+
}
|
|
336099
|
+
function evidenceExchange(broker) {
|
|
336100
|
+
return broker;
|
|
336101
|
+
}
|
|
336102
|
+
|
|
336103
|
+
// src/schemas/action-evidence.ts
|
|
336104
|
+
var EVIDENCE_DIGEST_ALGORITHM = "sha256-canonical-json-v1";
|
|
336105
|
+
var CanonicalDecimalStringSchema = exports_external.string().regex(/^(?:0|[1-9]\d*)(?:\.\d*[1-9])?$/);
|
|
336106
|
+
var accountScopeShape = {
|
|
336107
|
+
accountSelector: exports_external.string().min(1),
|
|
336108
|
+
credentialSource: exports_external.enum(["configured_pool", "request_metadata"])
|
|
336109
|
+
};
|
|
336110
|
+
var evidenceSourceShape = {
|
|
336111
|
+
observedAt: exports_external.string().datetime({ offset: true }),
|
|
336112
|
+
digestAlgorithm: exports_external.literal(EVIDENCE_DIGEST_ALGORITHM),
|
|
336113
|
+
sourceDigest: exports_external.string().regex(/^[a-f0-9]{64}$/)
|
|
336114
|
+
};
|
|
336115
|
+
var TradingFeeEvidenceSchema = exports_external.object({
|
|
336116
|
+
schemaVersion: exports_external.literal("cex-trading-fee-evidence/v1"),
|
|
336117
|
+
exchange: exports_external.string().min(1),
|
|
336118
|
+
marketType: exports_external.literal("spot"),
|
|
336119
|
+
canonicalPair: exports_external.string().regex(/^[^-\s]+-[^-\s]+$/),
|
|
336120
|
+
unifiedSymbol: exports_external.string().regex(/^[^/\s]+\/[^/\s]+$/),
|
|
336121
|
+
sourceSymbol: exports_external.string().min(1),
|
|
336122
|
+
...accountScopeShape,
|
|
336123
|
+
...evidenceSourceShape,
|
|
336124
|
+
sourceMethod: exports_external.literal("ccxt.fetchTradingFee"),
|
|
336125
|
+
makerRate: CanonicalDecimalStringSchema,
|
|
336126
|
+
takerRate: CanonicalDecimalStringSchema,
|
|
336127
|
+
rateUnit: exports_external.literal("decimal_fraction"),
|
|
336128
|
+
makerBasisPoints: CanonicalDecimalStringSchema,
|
|
336129
|
+
takerBasisPoints: CanonicalDecimalStringSchema,
|
|
336130
|
+
basisPointsUnit: exports_external.literal("basis_points")
|
|
336131
|
+
}).strict();
|
|
336132
|
+
var MarketRuleEvidenceSchema = exports_external.object({
|
|
336133
|
+
schemaVersion: exports_external.literal("cex-market-rule-evidence/v1"),
|
|
336134
|
+
exchange: exports_external.string().min(1),
|
|
336135
|
+
marketType: exports_external.literal("spot"),
|
|
336136
|
+
canonicalPair: exports_external.string().regex(/^[^-\s]+-[^-\s]+$/),
|
|
336137
|
+
unifiedSymbol: exports_external.string().regex(/^[^/\s]+\/[^/\s]+$/),
|
|
336138
|
+
sourceSymbol: exports_external.string().min(1),
|
|
336139
|
+
baseAsset: exports_external.string().min(1),
|
|
336140
|
+
quoteAsset: exports_external.string().min(1),
|
|
336141
|
+
active: exports_external.literal(true),
|
|
336142
|
+
precisionMode: exports_external.union([exports_external.string().min(1), exports_external.number().int()]),
|
|
336143
|
+
priceIncrement: CanonicalDecimalStringSchema,
|
|
336144
|
+
amountIncrement: CanonicalDecimalStringSchema,
|
|
336145
|
+
minimumAmount: CanonicalDecimalStringSchema,
|
|
336146
|
+
minimumNotional: CanonicalDecimalStringSchema,
|
|
336147
|
+
maximumAmount: CanonicalDecimalStringSchema.optional(),
|
|
336148
|
+
maximumPrice: CanonicalDecimalStringSchema.optional(),
|
|
336149
|
+
maximumNotional: CanonicalDecimalStringSchema.optional(),
|
|
336150
|
+
...accountScopeShape,
|
|
336151
|
+
...evidenceSourceShape,
|
|
336152
|
+
sourceMethod: exports_external.literal("ccxt.loadMarkets")
|
|
336153
|
+
}).strict();
|
|
336154
|
+
var TransferNetworkEvidenceSchema = exports_external.object({
|
|
336155
|
+
schemaVersion: exports_external.literal("cex-transfer-network-evidence/v1"),
|
|
336156
|
+
exchange: exports_external.string().min(1),
|
|
336157
|
+
asset: exports_external.string().min(1),
|
|
336158
|
+
operatorNetworkAlias: exports_external.string().min(1),
|
|
336159
|
+
brokerNetworkId: exports_external.string().min(1),
|
|
336160
|
+
exchangeNetworkId: exports_external.string().min(1),
|
|
336161
|
+
depositAvailable: exports_external.boolean(),
|
|
336162
|
+
withdrawalAvailable: exports_external.boolean(),
|
|
336163
|
+
withdrawalFee: CanonicalDecimalStringSchema.nullable(),
|
|
336164
|
+
withdrawalLimits: exports_external.object({
|
|
336165
|
+
minimum: CanonicalDecimalStringSchema.nullable(),
|
|
336166
|
+
maximum: CanonicalDecimalStringSchema.nullable()
|
|
336167
|
+
}).strict(),
|
|
336168
|
+
...accountScopeShape,
|
|
336169
|
+
...evidenceSourceShape,
|
|
336170
|
+
sourceMethod: exports_external.literal("ccxt.fetchCurrencies")
|
|
336171
|
+
}).strict();
|
|
336172
|
+
var BatchSuccessEntrySchema = exports_external.object({
|
|
336173
|
+
id: exports_external.string().min(1),
|
|
336174
|
+
action: exports_external.number().int().nonnegative(),
|
|
336175
|
+
symbol: exports_external.string(),
|
|
336176
|
+
response: exports_external.object({
|
|
336177
|
+
result: exports_external.string(),
|
|
336178
|
+
proof: exports_external.string()
|
|
336179
|
+
}).strict(),
|
|
336180
|
+
error: exports_external.null()
|
|
336181
|
+
}).strict();
|
|
336182
|
+
var BatchErrorEntrySchema = exports_external.object({
|
|
336183
|
+
id: exports_external.string().min(1),
|
|
336184
|
+
action: exports_external.number().int().nonnegative(),
|
|
336185
|
+
symbol: exports_external.string(),
|
|
336186
|
+
response: exports_external.null(),
|
|
336187
|
+
error: exports_external.object({
|
|
336188
|
+
code: exports_external.string().min(1),
|
|
336189
|
+
grpcStatus: exports_external.number().int().nonnegative(),
|
|
336190
|
+
message: exports_external.string()
|
|
336191
|
+
}).strict()
|
|
336192
|
+
}).strict();
|
|
336193
|
+
var BatchResponseEntrySchema = exports_external.union([
|
|
336194
|
+
BatchSuccessEntrySchema,
|
|
336195
|
+
BatchErrorEntrySchema
|
|
336196
|
+
]);
|
|
336197
|
+
var BatchResponseEnvelopeSchema = exports_external.object({
|
|
336198
|
+
schemaVersion: exports_external.literal("cex-broker-action-batch/v1"),
|
|
336199
|
+
responses: exports_external.array(BatchResponseEntrySchema).max(32)
|
|
336200
|
+
}).strict();
|
|
336201
|
+
|
|
336202
|
+
// src/handlers/execute-action/batch.ts
|
|
336203
|
+
var FORBIDDEN_ROUTING_KEYS = new Set([
|
|
336204
|
+
"account",
|
|
336205
|
+
"accountid",
|
|
336206
|
+
"accountselector",
|
|
336207
|
+
"apikey",
|
|
336208
|
+
"apisecret",
|
|
336209
|
+
"auth",
|
|
336210
|
+
"authorization",
|
|
336211
|
+
"cex",
|
|
336212
|
+
"credential",
|
|
336213
|
+
"credentials",
|
|
336214
|
+
"exchange",
|
|
336215
|
+
"metadata",
|
|
336216
|
+
"password",
|
|
336217
|
+
"secret",
|
|
336218
|
+
"signature",
|
|
336219
|
+
"usesecondarykey"
|
|
336220
|
+
]);
|
|
336221
|
+
function normalizeRoutingKey(key2) {
|
|
336222
|
+
return key2.replace(/[-_]/g, "").toLowerCase();
|
|
336223
|
+
}
|
|
336224
|
+
function hasForbiddenRoutingKey(value) {
|
|
336225
|
+
if (Array.isArray(value)) {
|
|
336226
|
+
return value.some(hasForbiddenRoutingKey);
|
|
336227
|
+
}
|
|
336228
|
+
if (value === null || typeof value !== "object") {
|
|
336229
|
+
return false;
|
|
336230
|
+
}
|
|
336231
|
+
for (const [key2, entry] of Object.entries(value)) {
|
|
336232
|
+
if (FORBIDDEN_ROUTING_KEYS.has(normalizeRoutingKey(key2))) {
|
|
336233
|
+
return true;
|
|
336234
|
+
}
|
|
336235
|
+
if (hasForbiddenRoutingKey(entry)) {
|
|
336236
|
+
return true;
|
|
336237
|
+
}
|
|
336238
|
+
}
|
|
336239
|
+
return false;
|
|
336240
|
+
}
|
|
336241
|
+
function childContainsRoutingOverride(child) {
|
|
336242
|
+
for (const [key2, value] of Object.entries(child.payload)) {
|
|
336243
|
+
if (FORBIDDEN_ROUTING_KEYS.has(normalizeRoutingKey(key2))) {
|
|
336244
|
+
return true;
|
|
336245
|
+
}
|
|
336246
|
+
try {
|
|
336247
|
+
if (hasForbiddenRoutingKey(JSON.parse(value))) {
|
|
336248
|
+
return true;
|
|
336249
|
+
}
|
|
336250
|
+
} catch {}
|
|
336251
|
+
}
|
|
336252
|
+
return false;
|
|
336253
|
+
}
|
|
336254
|
+
function stableBatchErrorCode(message, grpcStatus) {
|
|
336255
|
+
const prefix = message.match(/^([A-Za-z][A-Za-z0-9_]*):/)?.[1];
|
|
336256
|
+
if (prefix) {
|
|
336257
|
+
return prefix;
|
|
336258
|
+
}
|
|
336259
|
+
return grpc5.status[grpcStatus] ?? "UNKNOWN";
|
|
336260
|
+
}
|
|
336261
|
+
function batchErrorEntry(child, error48, broker) {
|
|
336262
|
+
const resolved = resolveGrpcError(error48);
|
|
336263
|
+
const errorRecord = error48 !== null && typeof error48 === "object" ? error48 : undefined;
|
|
336264
|
+
const grpcStatus = typeof errorRecord?.code === "number" ? errorRecord.code : resolved.code;
|
|
336265
|
+
const rawMessage = typeof errorRecord?.message === "string" ? errorRecord.message : resolved.message;
|
|
336266
|
+
const sanitizedMessage = redactSecretLiterals(sanitizeErrorDetail(rawMessage), exchangeSecretLiterals(broker));
|
|
336267
|
+
return {
|
|
336268
|
+
id: child.id,
|
|
336269
|
+
action: child.action,
|
|
336270
|
+
symbol: child.symbol,
|
|
336271
|
+
response: null,
|
|
336272
|
+
error: {
|
|
336273
|
+
code: stableBatchErrorCode(rawMessage, grpcStatus),
|
|
336274
|
+
grpcStatus,
|
|
336275
|
+
message: sanitizedMessage
|
|
336276
|
+
}
|
|
336277
|
+
};
|
|
336278
|
+
}
|
|
336279
|
+
function childCall(ctx, request) {
|
|
336280
|
+
return {
|
|
336281
|
+
...ctx.call,
|
|
336282
|
+
request
|
|
336283
|
+
};
|
|
336284
|
+
}
|
|
336285
|
+
async function executeChild(ctx, child, descriptor) {
|
|
336286
|
+
const action = resolveAction(child.action);
|
|
336287
|
+
if (action === undefined) {
|
|
336288
|
+
return batchErrorEntry(child, new Error(`ValidationError: invalid action ${child.action}`), ctx.broker);
|
|
336289
|
+
}
|
|
336290
|
+
const proofState = { proof: "" };
|
|
336291
|
+
let completion;
|
|
336292
|
+
const localCallback = (error48, response) => {
|
|
336293
|
+
if (completion === undefined) {
|
|
336294
|
+
completion = { error: error48, response: response ?? null };
|
|
336295
|
+
}
|
|
336296
|
+
};
|
|
336297
|
+
const request = {
|
|
336298
|
+
action,
|
|
336299
|
+
cex: ctx.cex,
|
|
336300
|
+
symbol: child.symbol,
|
|
336301
|
+
payload: child.payload
|
|
336302
|
+
};
|
|
336303
|
+
const childContext = {
|
|
336304
|
+
...ctx,
|
|
336305
|
+
call: childCall(ctx, request),
|
|
336306
|
+
wrappedCallback: localCallback,
|
|
336307
|
+
action,
|
|
336308
|
+
symbol: child.symbol,
|
|
336309
|
+
verity: proofState,
|
|
336310
|
+
applyVerityToBroker: (target) => ctx.applyVerityToBroker(target, proofState)
|
|
336311
|
+
};
|
|
336312
|
+
try {
|
|
336313
|
+
childContext.applyVerityToBroker(ctx.broker);
|
|
336314
|
+
await descriptor.handler(childContext);
|
|
336315
|
+
} catch (error48) {
|
|
336316
|
+
completion ??= {
|
|
336317
|
+
error: resolveGrpcError(error48),
|
|
336318
|
+
response: null
|
|
336319
|
+
};
|
|
336320
|
+
}
|
|
336321
|
+
if (completion === undefined) {
|
|
336322
|
+
return batchErrorEntry(child, new Error(`INTERNAL: ${getActionName(action)} completed without a callback`), ctx.broker);
|
|
336323
|
+
}
|
|
336324
|
+
if (completion.error || !completion.response) {
|
|
336325
|
+
return batchErrorEntry(child, completion.error ?? new Error("INTERNAL: child returned no response"), ctx.broker);
|
|
336326
|
+
}
|
|
336327
|
+
return {
|
|
336328
|
+
id: child.id,
|
|
336329
|
+
action: child.action,
|
|
336330
|
+
symbol: child.symbol,
|
|
336331
|
+
response: {
|
|
336332
|
+
result: completion.response.result,
|
|
336333
|
+
proof: completion.response.proof ?? proofState.proof
|
|
336334
|
+
},
|
|
336335
|
+
error: null
|
|
336336
|
+
};
|
|
336337
|
+
}
|
|
336338
|
+
async function handleBatch(ctx, lookupDescriptor) {
|
|
336339
|
+
if (ctx.symbol?.trim()) {
|
|
336340
|
+
return ctx.wrappedCallback({
|
|
336341
|
+
code: grpc5.status.INVALID_ARGUMENT,
|
|
336342
|
+
message: "ValidationError: Batch symbol must be empty"
|
|
336343
|
+
}, null);
|
|
336344
|
+
}
|
|
336345
|
+
const encodedRequests = ctx.call.request.payload?.requests;
|
|
336346
|
+
if (typeof encodedRequests === "string" && Buffer.byteLength(encodedRequests, "utf8") > MAX_BATCH_REQUEST_BYTES) {
|
|
336347
|
+
return ctx.wrappedCallback({
|
|
336348
|
+
code: grpc5.status.INVALID_ARGUMENT,
|
|
336349
|
+
message: `ValidationError: Batch requests exceed ${MAX_BATCH_REQUEST_BYTES} bytes`
|
|
336350
|
+
}, null);
|
|
336351
|
+
}
|
|
336352
|
+
const payload = parsePayloadForAction(ctx, BatchPayloadSchema);
|
|
336353
|
+
if (payload === null) {
|
|
336354
|
+
return;
|
|
336355
|
+
}
|
|
336356
|
+
const seenIds = new Set;
|
|
336357
|
+
const prepared = [];
|
|
336358
|
+
for (const child of payload.requests) {
|
|
336359
|
+
if (seenIds.has(child.id)) {
|
|
336360
|
+
return ctx.wrappedCallback({
|
|
336361
|
+
code: grpc5.status.INVALID_ARGUMENT,
|
|
336362
|
+
message: `ValidationError: duplicate batch child id '${child.id}'`
|
|
336363
|
+
}, null);
|
|
336364
|
+
}
|
|
336365
|
+
seenIds.add(child.id);
|
|
336366
|
+
if (childContainsRoutingOverride(child)) {
|
|
336367
|
+
return ctx.wrappedCallback({
|
|
336368
|
+
code: grpc5.status.INVALID_ARGUMENT,
|
|
336369
|
+
message: `ValidationError: batch child '${child.id}' contains a routing override`
|
|
336370
|
+
}, null);
|
|
336371
|
+
}
|
|
336372
|
+
const action = resolveAction(child.action);
|
|
336373
|
+
const descriptor = action === undefined ? undefined : lookupDescriptor(action);
|
|
336374
|
+
if (!descriptor || descriptor.access !== "read" || !descriptor.batchable) {
|
|
336375
|
+
return ctx.wrappedCallback({
|
|
336376
|
+
code: grpc5.status.INVALID_ARGUMENT,
|
|
336377
|
+
message: `ValidationError: batch child '${child.id}' action ${child.action} is not batchable`
|
|
336378
|
+
}, null);
|
|
336379
|
+
}
|
|
336380
|
+
const validation = descriptor.validateBatchRequest?.({
|
|
336381
|
+
action,
|
|
336382
|
+
cex: ctx.cex,
|
|
336383
|
+
symbol: child.symbol,
|
|
336384
|
+
payload: child.payload
|
|
336385
|
+
});
|
|
336386
|
+
if (validation && !validation.valid) {
|
|
336387
|
+
return ctx.wrappedCallback({
|
|
336388
|
+
code: grpc5.status.INVALID_ARGUMENT,
|
|
336389
|
+
message: `ValidationError: batch child '${child.id}': ${validation.message}`
|
|
336390
|
+
}, null);
|
|
336391
|
+
}
|
|
336392
|
+
prepared.push({ child, descriptor });
|
|
336393
|
+
}
|
|
336394
|
+
const responses = [];
|
|
336395
|
+
for (const { child, descriptor } of prepared) {
|
|
336396
|
+
const response = await executeChild(ctx, child, descriptor);
|
|
336397
|
+
responses.push(response);
|
|
336398
|
+
ctx.otelMetrics?.recordCounter("execute_action_batch_items_total", 1, {
|
|
336399
|
+
action: getActionName(child.action),
|
|
336400
|
+
cex: ctx.normalizedCex,
|
|
336401
|
+
outcome: response.error ? "error" : "success"
|
|
336402
|
+
});
|
|
336403
|
+
}
|
|
336404
|
+
const envelope = BatchResponseEnvelopeSchema.parse({
|
|
336405
|
+
schemaVersion: "cex-broker-action-batch/v1",
|
|
336406
|
+
responses
|
|
336407
|
+
});
|
|
336408
|
+
ctx.wrappedCallback(null, {
|
|
336409
|
+
result: JSON.stringify(envelope),
|
|
336410
|
+
proof: ""
|
|
336411
|
+
});
|
|
336412
|
+
}
|
|
336413
|
+
|
|
336414
|
+
// src/handlers/execute-action/internal-transfer.ts
|
|
336415
|
+
var grpc6 = __toESM(require_src3(), 1);
|
|
335752
336416
|
async function handleInternalTransfer(ctx) {
|
|
335753
336417
|
const {
|
|
335754
336418
|
brokers,
|
|
@@ -335762,7 +336426,7 @@ async function handleInternalTransfer(ctx) {
|
|
|
335762
336426
|
} = ctx;
|
|
335763
336427
|
if (!symbol2) {
|
|
335764
336428
|
return ctx.wrappedCallback({
|
|
335765
|
-
code:
|
|
336429
|
+
code: grpc6.status.INVALID_ARGUMENT,
|
|
335766
336430
|
message: `ValidationError: Symbol required`
|
|
335767
336431
|
}, null);
|
|
335768
336432
|
}
|
|
@@ -335771,14 +336435,14 @@ async function handleInternalTransfer(ctx) {
|
|
|
335771
336435
|
return;
|
|
335772
336436
|
if (normalizedCex !== "binance") {
|
|
335773
336437
|
return ctx.wrappedCallback({
|
|
335774
|
-
code:
|
|
336438
|
+
code: grpc6.status.UNIMPLEMENTED,
|
|
335775
336439
|
message: `InternalTransfer is only supported for Binance`
|
|
335776
336440
|
}, null);
|
|
335777
336441
|
}
|
|
335778
336442
|
const pool = brokers[normalizedCex];
|
|
335779
336443
|
if (!pool) {
|
|
335780
336444
|
return ctx.wrappedCallback({
|
|
335781
|
-
code:
|
|
336445
|
+
code: grpc6.status.FAILED_PRECONDITION,
|
|
335782
336446
|
message: `No broker accounts configured for ${normalizedCex}`
|
|
335783
336447
|
}, null);
|
|
335784
336448
|
}
|
|
@@ -335787,14 +336451,14 @@ async function handleInternalTransfer(ctx) {
|
|
|
335787
336451
|
const sourceAccount = resolveBrokerAccount(pool, fromSelector);
|
|
335788
336452
|
if (!sourceAccount) {
|
|
335789
336453
|
return ctx.wrappedCallback({
|
|
335790
|
-
code:
|
|
336454
|
+
code: grpc6.status.INVALID_ARGUMENT,
|
|
335791
336455
|
message: `Source account "${fromSelector}" is not configured`
|
|
335792
336456
|
}, null);
|
|
335793
336457
|
}
|
|
335794
336458
|
const destAccount = resolveBrokerAccount(pool, toSelector);
|
|
335795
336459
|
if (!destAccount) {
|
|
335796
336460
|
return ctx.wrappedCallback({
|
|
335797
|
-
code:
|
|
336461
|
+
code: grpc6.status.INVALID_ARGUMENT,
|
|
335798
336462
|
message: `Destination account "${toSelector}" is not configured`
|
|
335799
336463
|
}, null);
|
|
335800
336464
|
}
|
|
@@ -335828,18 +336492,18 @@ async function handleInternalTransfer(ctx) {
|
|
|
335828
336492
|
safeLogError("InternalTransfer failed", error48);
|
|
335829
336493
|
if (error48 instanceof BrokerAccountPreconditionError) {
|
|
335830
336494
|
return ctx.wrappedCallback({
|
|
335831
|
-
code:
|
|
336495
|
+
code: grpc6.status.FAILED_PRECONDITION,
|
|
335832
336496
|
message: getErrorMessage(error48)
|
|
335833
336497
|
}, null);
|
|
335834
336498
|
}
|
|
335835
336499
|
const msg = getErrorMessage(error48);
|
|
335836
336500
|
let code;
|
|
335837
336501
|
if (msg.includes("Unsupported transfer direction")) {
|
|
335838
|
-
code =
|
|
336502
|
+
code = grpc6.status.INVALID_ARGUMENT;
|
|
335839
336503
|
} else if (msg.includes("unavailable in this CCXT build")) {
|
|
335840
|
-
code =
|
|
336504
|
+
code = grpc6.status.UNIMPLEMENTED;
|
|
335841
336505
|
} else {
|
|
335842
|
-
code = mapCcxtErrorToGrpcStatus(error48) ??
|
|
336506
|
+
code = mapCcxtErrorToGrpcStatus(error48) ?? grpc6.status.INTERNAL;
|
|
335843
336507
|
}
|
|
335844
336508
|
ctx.wrappedCallback({
|
|
335845
336509
|
code,
|
|
@@ -335849,7 +336513,7 @@ async function handleInternalTransfer(ctx) {
|
|
|
335849
336513
|
}
|
|
335850
336514
|
|
|
335851
336515
|
// src/handlers/execute-action/orders.ts
|
|
335852
|
-
var
|
|
336516
|
+
var grpc7 = __toESM(require_src3(), 1);
|
|
335853
336517
|
|
|
335854
336518
|
// src/helpers/passive-order.ts
|
|
335855
336519
|
var PASSIVE_ORDER_ERROR_CODES = {
|
|
@@ -335910,7 +336574,7 @@ async function handleCreateOrder(ctx) {
|
|
|
335910
336574
|
const isPassiveOrder = orderValue.orderIntent === "passive_only";
|
|
335911
336575
|
if (isPassiveOrder && orderValue.orderType !== "limit") {
|
|
335912
336576
|
return ctx.wrappedCallback({
|
|
335913
|
-
code:
|
|
336577
|
+
code: grpc7.status.INVALID_ARGUMENT,
|
|
335914
336578
|
message: "ValidationError: passive_only order intent requires a limit order"
|
|
335915
336579
|
}, null);
|
|
335916
336580
|
}
|
|
@@ -335927,14 +336591,14 @@ async function handleCreateOrder(ctx) {
|
|
|
335927
336591
|
try {
|
|
335928
336592
|
if (!broker) {
|
|
335929
336593
|
return ctx.wrappedCallback({
|
|
335930
|
-
code:
|
|
336594
|
+
code: grpc7.status.INVALID_ARGUMENT,
|
|
335931
336595
|
message: `Invalid CEX key: ${cex3}. Supported keys: ${Object.keys(brokers).join(", ")}`
|
|
335932
336596
|
}, null);
|
|
335933
336597
|
}
|
|
335934
336598
|
const resolution = await resolveOrderExecution(policy, broker, cex3, orderValue.fromToken, orderValue.toToken, orderValue.amount, orderValue.price, orderValue.marketType);
|
|
335935
336599
|
if (!resolution.valid || !resolution.symbol || !resolution.side) {
|
|
335936
336600
|
return ctx.wrappedCallback({
|
|
335937
|
-
code:
|
|
336601
|
+
code: grpc7.status.INVALID_ARGUMENT,
|
|
335938
336602
|
message: resolution.error ?? "Order rejected by policy: market or limits not satisfied"
|
|
335939
336603
|
}, null);
|
|
335940
336604
|
}
|
|
@@ -336007,7 +336671,7 @@ async function handleCreateOrder(ctx) {
|
|
|
336007
336671
|
});
|
|
336008
336672
|
}
|
|
336009
336673
|
ctx.wrappedCallback({
|
|
336010
|
-
code:
|
|
336674
|
+
code: grpc7.status.INTERNAL,
|
|
336011
336675
|
message: `Order Creation failed: ${sanitizeErrorDetail(error48)}`
|
|
336012
336676
|
}, null);
|
|
336013
336677
|
}
|
|
@@ -336039,7 +336703,7 @@ async function handleGetOrderDetails(ctx) {
|
|
|
336039
336703
|
try {
|
|
336040
336704
|
if (!broker) {
|
|
336041
336705
|
return ctx.wrappedCallback({
|
|
336042
|
-
code:
|
|
336706
|
+
code: grpc7.status.INVALID_ARGUMENT,
|
|
336043
336707
|
message: `Invalid CEX key: ${cex3}. Supported keys: ${Object.keys(brokers).join(", ")}`
|
|
336044
336708
|
}, null);
|
|
336045
336709
|
}
|
|
@@ -336080,7 +336744,7 @@ async function handleGetOrderDetails(ctx) {
|
|
|
336080
336744
|
emitOrderExecutionTelemetryInBackground(otelMetrics, failedGetOrderContext, undefined, error48);
|
|
336081
336745
|
archiveOrderExecutionInBackground(brokerArchiver, failedGetOrderContext, undefined, error48);
|
|
336082
336746
|
ctx.wrappedCallback({
|
|
336083
|
-
code:
|
|
336747
|
+
code: grpc7.status.INTERNAL,
|
|
336084
336748
|
message: `Failed to fetch order details from ${cex3}: ${sanitizeErrorDetail(error48)}`
|
|
336085
336749
|
}, null);
|
|
336086
336750
|
}
|
|
@@ -336112,7 +336776,7 @@ async function handleCancelOrder(ctx) {
|
|
|
336112
336776
|
try {
|
|
336113
336777
|
if (!broker) {
|
|
336114
336778
|
return ctx.wrappedCallback({
|
|
336115
|
-
code:
|
|
336779
|
+
code: grpc7.status.INVALID_ARGUMENT,
|
|
336116
336780
|
message: `Invalid CEX key: ${cex3}. Supported keys: ${Object.keys(brokers).join(", ")}`
|
|
336117
336781
|
}, null);
|
|
336118
336782
|
}
|
|
@@ -336144,7 +336808,7 @@ async function handleCancelOrder(ctx) {
|
|
|
336144
336808
|
emitOrderExecutionTelemetryInBackground(otelMetrics, failedCancelContext, undefined, error48);
|
|
336145
336809
|
archiveOrderExecutionInBackground(brokerArchiver, failedCancelContext, undefined, error48);
|
|
336146
336810
|
ctx.wrappedCallback({
|
|
336147
|
-
code:
|
|
336811
|
+
code: grpc7.status.INTERNAL,
|
|
336148
336812
|
message: `Failed to cancel order from ${cex3}: ${sanitizeErrorDetail(error48)}`
|
|
336149
336813
|
}, null);
|
|
336150
336814
|
}
|
|
@@ -336159,261 +336823,254 @@ async function handleOrders(ctx) {
|
|
|
336159
336823
|
}
|
|
336160
336824
|
|
|
336161
336825
|
// src/handlers/execute-action/pass-through.ts
|
|
336162
|
-
var
|
|
336163
|
-
|
|
336164
|
-
|
|
336165
|
-
|
|
336166
|
-
|
|
336167
|
-
|
|
336168
|
-
|
|
336169
|
-
|
|
336170
|
-
|
|
336171
|
-
|
|
336172
|
-
|
|
336173
|
-
|
|
336174
|
-
|
|
336175
|
-
|
|
336176
|
-
|
|
336177
|
-
|
|
336178
|
-
|
|
336179
|
-
|
|
336180
|
-
|
|
336181
|
-
|
|
336182
|
-
|
|
336826
|
+
var grpc9 = __toESM(require_src3(), 1);
|
|
336827
|
+
|
|
336828
|
+
// src/handlers/execute-action/venue-evidence.ts
|
|
336829
|
+
var grpc8 = __toESM(require_src3(), 1);
|
|
336830
|
+
function failVenueDiscovery(ctx, error48, operation) {
|
|
336831
|
+
safeLogRedactedError(`${operation} failed`, error48);
|
|
336832
|
+
const sanitized = sanitizeVenueError(error48, ctx.broker);
|
|
336833
|
+
const message = sanitized.startsWith("venue_discovery_unavailable:") ? sanitized : `venue_discovery_unavailable: ${sanitized}`;
|
|
336834
|
+
ctx.wrappedCallback({
|
|
336835
|
+
code: stableGrpcErrorCode(message) ?? mapCcxtErrorToGrpcStatus(error48) ?? grpc8.status.UNIMPLEMENTED,
|
|
336836
|
+
message
|
|
336837
|
+
}, null);
|
|
336838
|
+
}
|
|
336839
|
+
function optionalDecimalFields(values2) {
|
|
336840
|
+
const result = {};
|
|
336841
|
+
for (const [key2, value, field] of values2) {
|
|
336842
|
+
const normalized = canonicalOptionalDecimal(value, field);
|
|
336843
|
+
if (normalized !== undefined) {
|
|
336844
|
+
result[key2] = normalized;
|
|
336845
|
+
}
|
|
336846
|
+
}
|
|
336847
|
+
return result;
|
|
336848
|
+
}
|
|
336849
|
+
async function handleFetchFeesEvidence(ctx) {
|
|
336850
|
+
if (!requireSymbol(ctx, "ValidationError: symbol must be a slash-delimited spot pair")) {
|
|
336851
|
+
return;
|
|
336852
|
+
}
|
|
336853
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(ctx.symbol.trim())) {
|
|
336183
336854
|
return ctx.wrappedCallback({
|
|
336184
|
-
code:
|
|
336185
|
-
message:
|
|
336855
|
+
code: grpc8.status.INVALID_ARGUMENT,
|
|
336856
|
+
message: "ValidationError: symbol must be a slash-delimited spot pair"
|
|
336186
336857
|
}, null);
|
|
336187
336858
|
}
|
|
336859
|
+
if (parsePayloadForAction(ctx, FetchFeesPayloadSchema) === null) {
|
|
336860
|
+
return;
|
|
336861
|
+
}
|
|
336188
336862
|
try {
|
|
336189
|
-
const
|
|
336190
|
-
const
|
|
336191
|
-
if (
|
|
336192
|
-
|
|
336193
|
-
|
|
336194
|
-
|
|
336195
|
-
|
|
336196
|
-
|
|
336197
|
-
|
|
336198
|
-
|
|
336199
|
-
|
|
336200
|
-
|
|
336201
|
-
|
|
336202
|
-
|
|
336203
|
-
|
|
336204
|
-
|
|
336205
|
-
|
|
336206
|
-
|
|
336207
|
-
|
|
336208
|
-
|
|
336863
|
+
const identity2 = await resolveSpotMarketIdentity(ctx.broker, ctx.symbol);
|
|
336864
|
+
const exchange = evidenceExchange(ctx.broker);
|
|
336865
|
+
if (exchange.has?.fetchTradingFee === false || typeof exchange.fetchTradingFee !== "function") {
|
|
336866
|
+
throw new Error(`fee_unavailable: ${ctx.normalizedCex} does not support fetchTradingFee`);
|
|
336867
|
+
}
|
|
336868
|
+
const sourceResponse = await exchange.fetchTradingFee(identity2.unifiedSymbol);
|
|
336869
|
+
if (isRecord(sourceResponse) && typeof sourceResponse.symbol === "string" && sourceResponse.symbol.trim().toUpperCase() !== identity2.unifiedSymbol) {
|
|
336870
|
+
throw new Error(`fee_unavailable: trading-fee response symbol does not match ${identity2.unifiedSymbol}`);
|
|
336871
|
+
}
|
|
336872
|
+
const { makerRate, takerRate } = extractTradingFeeRates(sourceResponse);
|
|
336873
|
+
const accountScope = resolveEvidenceAccountScope(ctx.selectedBrokerAccount, ctx.metadata);
|
|
336874
|
+
const evidence = TradingFeeEvidenceSchema.parse({
|
|
336875
|
+
schemaVersion: "cex-trading-fee-evidence/v1",
|
|
336876
|
+
exchange: ctx.normalizedCex,
|
|
336877
|
+
marketType: "spot",
|
|
336878
|
+
canonicalPair: identity2.canonicalPair,
|
|
336879
|
+
unifiedSymbol: identity2.unifiedSymbol,
|
|
336880
|
+
sourceSymbol: identity2.sourceSymbol,
|
|
336881
|
+
...accountScope,
|
|
336882
|
+
observedAt: new Date().toISOString(),
|
|
336883
|
+
sourceMethod: "ccxt.fetchTradingFee",
|
|
336884
|
+
makerRate,
|
|
336885
|
+
takerRate,
|
|
336886
|
+
rateUnit: "decimal_fraction",
|
|
336887
|
+
makerBasisPoints: decimalFractionToBasisPoints(makerRate),
|
|
336888
|
+
takerBasisPoints: decimalFractionToBasisPoints(takerRate),
|
|
336889
|
+
basisPointsUnit: "basis_points",
|
|
336890
|
+
digestAlgorithm: "sha256-canonical-json-v1",
|
|
336891
|
+
sourceDigest: evidenceSourceDigest({
|
|
336892
|
+
action: "FetchFees",
|
|
336893
|
+
exchange: ctx.normalizedCex,
|
|
336894
|
+
requestedKey: identity2.canonicalPair,
|
|
336895
|
+
accountSelector: accountScope.accountSelector,
|
|
336896
|
+
sourceMethod: "ccxt.fetchTradingFee",
|
|
336897
|
+
source: sourceResponse,
|
|
336898
|
+
broker: ctx.broker
|
|
336209
336899
|
})
|
|
336210
336900
|
});
|
|
336901
|
+
successWithProof(ctx, evidence);
|
|
336211
336902
|
} catch (error48) {
|
|
336212
|
-
|
|
336213
|
-
const
|
|
336903
|
+
safeLogRedactedError(`FetchFees failed for ${ctx.normalizedCex}/${ctx.symbol}`, error48);
|
|
336904
|
+
const sanitized = sanitizeVenueError(error48, ctx.broker);
|
|
336905
|
+
const message = sanitized.startsWith("fee_unavailable:") ? sanitized : `fee_unavailable: ${sanitized}`;
|
|
336214
336906
|
ctx.wrappedCallback({
|
|
336215
|
-
code:
|
|
336216
|
-
message
|
|
336907
|
+
code: grpc8.status.FAILED_PRECONDITION,
|
|
336908
|
+
message
|
|
336217
336909
|
}, null);
|
|
336218
336910
|
}
|
|
336219
336911
|
}
|
|
336220
|
-
async function
|
|
336221
|
-
|
|
336222
|
-
|
|
336223
|
-
|
|
336224
|
-
|
|
336225
|
-
|
|
336226
|
-
|
|
336227
|
-
|
|
336228
|
-
|
|
336229
|
-
|
|
336230
|
-
|
|
336231
|
-
|
|
336232
|
-
|
|
336233
|
-
applyVerityToBroker,
|
|
336234
|
-
useVerity,
|
|
336235
|
-
verityProverUrl,
|
|
336236
|
-
otelMetrics
|
|
336237
|
-
} = ctx;
|
|
336238
|
-
const verityProof = verity.proof;
|
|
336912
|
+
async function handleFetchMarketRulesEvidence(ctx) {
|
|
336913
|
+
if (!requireSymbol(ctx, "ValidationError: symbol must be a slash-delimited spot pair")) {
|
|
336914
|
+
return;
|
|
336915
|
+
}
|
|
336916
|
+
if (!/^[^/\s]+\/[^/\s]+$/.test(ctx.symbol.trim())) {
|
|
336917
|
+
return ctx.wrappedCallback({
|
|
336918
|
+
code: grpc8.status.INVALID_ARGUMENT,
|
|
336919
|
+
message: "ValidationError: symbol must be a slash-delimited spot pair"
|
|
336920
|
+
}, null);
|
|
336921
|
+
}
|
|
336922
|
+
if (parsePayloadForAction(ctx, EmptyActionPayloadSchema) === null) {
|
|
336923
|
+
return;
|
|
336924
|
+
}
|
|
336239
336925
|
try {
|
|
336240
|
-
const
|
|
336241
|
-
|
|
336242
|
-
|
|
336243
|
-
|
|
336926
|
+
const identity2 = await resolveSpotMarketIdentity(ctx.broker, ctx.symbol);
|
|
336927
|
+
const precision = isRecord(identity2.market.precision) ? identity2.market.precision : {};
|
|
336928
|
+
const limits = isRecord(identity2.market.limits) ? identity2.market.limits : {};
|
|
336929
|
+
const amountLimits = isRecord(limits.amount) ? limits.amount : {};
|
|
336930
|
+
const priceLimits = isRecord(limits.price) ? limits.price : {};
|
|
336931
|
+
const costLimits = isRecord(limits.cost) ? limits.cost : {};
|
|
336932
|
+
const precisionMode = evidenceExchange(ctx.broker).precisionMode;
|
|
336933
|
+
if (precisionMode === undefined || precisionMode === null) {
|
|
336934
|
+
throw new Error("venue_discovery_unavailable: precision mode is unavailable");
|
|
336935
|
+
}
|
|
336936
|
+
const accountScope = resolveEvidenceAccountScope(ctx.selectedBrokerAccount, ctx.metadata);
|
|
336937
|
+
const evidence = MarketRuleEvidenceSchema.parse({
|
|
336938
|
+
schemaVersion: "cex-market-rule-evidence/v1",
|
|
336939
|
+
exchange: ctx.normalizedCex,
|
|
336940
|
+
marketType: "spot",
|
|
336941
|
+
canonicalPair: identity2.canonicalPair,
|
|
336942
|
+
unifiedSymbol: identity2.unifiedSymbol,
|
|
336943
|
+
sourceSymbol: identity2.sourceSymbol,
|
|
336944
|
+
baseAsset: identity2.baseAsset,
|
|
336945
|
+
quoteAsset: identity2.quoteAsset,
|
|
336946
|
+
active: true,
|
|
336947
|
+
precisionMode,
|
|
336948
|
+
priceIncrement: precisionIncrement(precision.price, precisionMode, "price increment"),
|
|
336949
|
+
amountIncrement: precisionIncrement(precision.amount, precisionMode, "amount increment"),
|
|
336950
|
+
minimumAmount: canonicalNonnegativeDecimal(amountLimits.min, "minimum amount"),
|
|
336951
|
+
minimumNotional: canonicalNonnegativeDecimal(costLimits.min, "minimum notional"),
|
|
336952
|
+
...optionalDecimalFields([
|
|
336953
|
+
["maximumAmount", amountLimits.max, "maximum amount"],
|
|
336954
|
+
["maximumPrice", priceLimits.max, "maximum price"],
|
|
336955
|
+
["maximumNotional", costLimits.max, "maximum notional"]
|
|
336956
|
+
]),
|
|
336957
|
+
...accountScope,
|
|
336958
|
+
observedAt: new Date().toISOString(),
|
|
336959
|
+
sourceMethod: "ccxt.loadMarkets",
|
|
336960
|
+
digestAlgorithm: "sha256-canonical-json-v1",
|
|
336961
|
+
sourceDigest: evidenceSourceDigest({
|
|
336962
|
+
action: "FetchMarketRules",
|
|
336963
|
+
exchange: ctx.normalizedCex,
|
|
336964
|
+
requestedKey: identity2.canonicalPair,
|
|
336965
|
+
accountSelector: accountScope.accountSelector,
|
|
336966
|
+
sourceMethod: "ccxt.loadMarkets",
|
|
336967
|
+
source: identity2.market,
|
|
336968
|
+
broker: ctx.broker
|
|
336969
|
+
})
|
|
336244
336970
|
});
|
|
336971
|
+
successWithProof(ctx, evidence);
|
|
336245
336972
|
} catch (error48) {
|
|
336246
|
-
|
|
336247
|
-
ctx.wrappedCallback({
|
|
336248
|
-
code: grpc7.status.INTERNAL,
|
|
336249
|
-
message: `Error fetching account ID from ${cex3}`
|
|
336250
|
-
}, null);
|
|
336973
|
+
failVenueDiscovery(ctx, error48, `FetchMarketRules ${ctx.normalizedCex}/${ctx.symbol}`);
|
|
336251
336974
|
}
|
|
336252
336975
|
}
|
|
336253
|
-
async function
|
|
336254
|
-
|
|
336255
|
-
|
|
336256
|
-
wrappedCallback,
|
|
336257
|
-
policy,
|
|
336258
|
-
brokers,
|
|
336259
|
-
metadata,
|
|
336260
|
-
normalizedCex,
|
|
336261
|
-
cex: cex3,
|
|
336262
|
-
symbol: symbol2,
|
|
336263
|
-
selectedBrokerAccount,
|
|
336264
|
-
broker,
|
|
336265
|
-
verity,
|
|
336266
|
-
applyVerityToBroker,
|
|
336267
|
-
useVerity,
|
|
336268
|
-
verityProverUrl,
|
|
336269
|
-
otelMetrics
|
|
336270
|
-
} = ctx;
|
|
336271
|
-
const verityProof = verity.proof;
|
|
336272
|
-
if (!symbol2) {
|
|
336273
|
-
return ctx.wrappedCallback({
|
|
336274
|
-
code: grpc7.status.INVALID_ARGUMENT,
|
|
336275
|
-
message: `ValidationError: Symbol required`
|
|
336276
|
-
}, null);
|
|
336976
|
+
async function handleFetchCurrencyEvidence(ctx) {
|
|
336977
|
+
if (!requireSymbol(ctx)) {
|
|
336978
|
+
return;
|
|
336277
336979
|
}
|
|
336278
|
-
const
|
|
336279
|
-
if (
|
|
336980
|
+
const payload = parsePayloadForAction(ctx, FetchCurrencyPayloadSchema);
|
|
336981
|
+
if (payload === null) {
|
|
336280
336982
|
return;
|
|
336281
|
-
|
|
336983
|
+
}
|
|
336984
|
+
const asset = ctx.symbol.trim().toUpperCase();
|
|
336985
|
+
const requestedAlias = payload.network.trim().toUpperCase();
|
|
336282
336986
|
try {
|
|
336283
|
-
|
|
336284
|
-
|
|
336285
|
-
|
|
336286
|
-
|
|
336287
|
-
|
|
336288
|
-
|
|
336289
|
-
|
|
336290
|
-
|
|
336291
|
-
|
|
336292
|
-
|
|
336293
|
-
|
|
336294
|
-
|
|
336295
|
-
|
|
336296
|
-
|
|
336297
|
-
|
|
336298
|
-
|
|
336299
|
-
|
|
336300
|
-
|
|
336301
|
-
|
|
336302
|
-
|
|
336303
|
-
|
|
336304
|
-
|
|
336305
|
-
|
|
336306
|
-
|
|
336307
|
-
|
|
336308
|
-
|
|
336309
|
-
|
|
336310
|
-
|
|
336311
|
-
|
|
336312
|
-
|
|
336313
|
-
|
|
336314
|
-
|
|
336315
|
-
|
|
336316
|
-
|
|
336317
|
-
|
|
336318
|
-
|
|
336319
|
-
|
|
336320
|
-
|
|
336321
|
-
|
|
336322
|
-
|
|
336323
|
-
|
|
336324
|
-
|
|
336325
|
-
|
|
336326
|
-
|
|
336327
|
-
|
|
336328
|
-
|
|
336329
|
-
|
|
336330
|
-
|
|
336331
|
-
|
|
336332
|
-
|
|
336333
|
-
|
|
336334
|
-
|
|
336335
|
-
|
|
336336
|
-
|
|
336337
|
-
|
|
336338
|
-
|
|
336339
|
-
|
|
336340
|
-
}
|
|
336341
|
-
|
|
336342
|
-
|
|
336343
|
-
|
|
336344
|
-
|
|
336345
|
-
|
|
336346
|
-
|
|
336347
|
-
|
|
336348
|
-
|
|
336349
|
-
|
|
336350
|
-
|
|
336351
|
-
|
|
336352
|
-
|
|
336353
|
-
feeScope: "market",
|
|
336354
|
-
generalFee,
|
|
336355
|
-
feeStatus,
|
|
336356
|
-
market
|
|
336357
|
-
})
|
|
336358
|
-
});
|
|
336359
|
-
}
|
|
336360
|
-
const currencyCodes = Array.from(new Set([market.base, market.quote]));
|
|
336361
|
-
const { fundingFeeSource: fundingFeeSource2, fundingFeesByCurrency: fundingFeesByCurrency2 } = await fetchFundingFees(currencyCodes);
|
|
336362
|
-
return ctx.wrappedCallback(null, {
|
|
336363
|
-
proof: ctx.verity.proof,
|
|
336364
|
-
result: JSON.stringify({
|
|
336365
|
-
feeScope: "market+funding",
|
|
336366
|
-
generalFee,
|
|
336367
|
-
feeStatus,
|
|
336368
|
-
market,
|
|
336369
|
-
fundingFeeSource: fundingFeeSource2,
|
|
336370
|
-
fundingFeesByCurrency: fundingFeesByCurrency2
|
|
336371
|
-
})
|
|
336372
|
-
});
|
|
336373
|
-
}
|
|
336374
|
-
const tokenCode = symbol2.toUpperCase();
|
|
336375
|
-
const { fundingFeeSource, fundingFeesByCurrency } = await fetchFundingFees([
|
|
336376
|
-
tokenCode
|
|
336377
|
-
]);
|
|
336987
|
+
const exchange = evidenceExchange(ctx.broker);
|
|
336988
|
+
if (exchange.has?.fetchCurrencies === false || typeof exchange.fetchCurrencies !== "function") {
|
|
336989
|
+
throw new Error(`venue_discovery_unavailable: fetchCurrencies unavailable for ${asset}`);
|
|
336990
|
+
}
|
|
336991
|
+
const currencies = await exchange.fetchCurrencies();
|
|
336992
|
+
const currency = currencies[asset];
|
|
336993
|
+
if (!isRecord(currency)) {
|
|
336994
|
+
throw new Error(`venue_discovery_unavailable: currency not found for ${asset}`);
|
|
336995
|
+
}
|
|
336996
|
+
const networkEvidence = buildTransferNetworkEvidence(currency);
|
|
336997
|
+
const brokerNetworkId = normalizeBrokerNetworkId(requestedAlias);
|
|
336998
|
+
const resolution = networkEvidence.aliases[requestedAlias] ?? networkEvidence.aliases[brokerNetworkId];
|
|
336999
|
+
if (!resolution?.networkKey) {
|
|
337000
|
+
throw new Error(`network_alias_unresolved: ${asset}/${requestedAlias} is not available in discovered transfer networks`);
|
|
337001
|
+
}
|
|
337002
|
+
const networks = isRecord(currency.networks) ? currency.networks : {};
|
|
337003
|
+
const networkCandidate = networks[resolution.networkKey];
|
|
337004
|
+
if (!isRecord(networkCandidate) || typeof networkCandidate.deposit !== "boolean" || typeof networkCandidate.withdraw !== "boolean") {
|
|
337005
|
+
throw new Error(`venue_discovery_unavailable: transfer availability is incomplete for ${asset}/${requestedAlias}`);
|
|
337006
|
+
}
|
|
337007
|
+
const network = networkCandidate;
|
|
337008
|
+
const limits = isRecord(network.limits) ? network.limits : {};
|
|
337009
|
+
const withdrawalLimits = isRecord(limits.withdraw) ? limits.withdraw : {};
|
|
337010
|
+
const accountScope = resolveEvidenceAccountScope(ctx.selectedBrokerAccount, ctx.metadata);
|
|
337011
|
+
const evidence = TransferNetworkEvidenceSchema.parse({
|
|
337012
|
+
schemaVersion: "cex-transfer-network-evidence/v1",
|
|
337013
|
+
exchange: ctx.normalizedCex,
|
|
337014
|
+
asset,
|
|
337015
|
+
operatorNetworkAlias: requestedAlias,
|
|
337016
|
+
brokerNetworkId,
|
|
337017
|
+
exchangeNetworkId: resolution.exchangeNetworkId,
|
|
337018
|
+
depositAvailable: network.deposit,
|
|
337019
|
+
withdrawalAvailable: network.withdraw,
|
|
337020
|
+
withdrawalFee: canonicalOptionalDecimal(network.fee, "withdrawal fee") ?? null,
|
|
337021
|
+
withdrawalLimits: {
|
|
337022
|
+
minimum: canonicalOptionalDecimal(withdrawalLimits.min, "minimum withdrawal") ?? null,
|
|
337023
|
+
maximum: canonicalOptionalDecimal(withdrawalLimits.max, "maximum withdrawal") ?? null
|
|
337024
|
+
},
|
|
337025
|
+
...accountScope,
|
|
337026
|
+
observedAt: new Date().toISOString(),
|
|
337027
|
+
sourceMethod: "ccxt.fetchCurrencies",
|
|
337028
|
+
digestAlgorithm: "sha256-canonical-json-v1",
|
|
337029
|
+
sourceDigest: evidenceSourceDigest({
|
|
337030
|
+
action: "FetchCurrency",
|
|
337031
|
+
exchange: ctx.normalizedCex,
|
|
337032
|
+
requestedKey: `${asset}/${requestedAlias}`,
|
|
337033
|
+
accountSelector: accountScope.accountSelector,
|
|
337034
|
+
sourceMethod: "ccxt.fetchCurrencies",
|
|
337035
|
+
source: network,
|
|
337036
|
+
broker: ctx.broker
|
|
337037
|
+
})
|
|
337038
|
+
});
|
|
337039
|
+
successWithProof(ctx, evidence);
|
|
337040
|
+
} catch (error48) {
|
|
337041
|
+
safeLogRedactedError(`FetchCurrency failed for ${ctx.normalizedCex}/${asset}/${requestedAlias}`, error48);
|
|
337042
|
+
const sanitized = sanitizeVenueError(error48, ctx.broker);
|
|
337043
|
+
const isAliasError = sanitized.startsWith("network_alias_unresolved:");
|
|
337044
|
+
const message = isAliasError || sanitized.startsWith("venue_discovery_unavailable:") ? sanitized : `venue_discovery_unavailable: ${sanitized}`;
|
|
337045
|
+
ctx.wrappedCallback({
|
|
337046
|
+
code: stableGrpcErrorCode(message) ?? mapCcxtErrorToGrpcStatus(error48) ?? grpc8.status.UNIMPLEMENTED,
|
|
337047
|
+
message
|
|
337048
|
+
}, null);
|
|
337049
|
+
}
|
|
337050
|
+
}
|
|
337051
|
+
|
|
337052
|
+
// src/handlers/execute-action/pass-through.ts
|
|
337053
|
+
async function handleFetchAccountId(ctx) {
|
|
337054
|
+
const { cex: cex3, broker } = ctx;
|
|
337055
|
+
try {
|
|
337056
|
+
const accountId = await broker.fetchAccountId();
|
|
336378
337057
|
return ctx.wrappedCallback(null, {
|
|
336379
337058
|
proof: ctx.verity.proof,
|
|
336380
|
-
result: JSON.stringify({
|
|
336381
|
-
feeScope: "token",
|
|
336382
|
-
symbol: tokenCode,
|
|
336383
|
-
fundingFeeSource,
|
|
336384
|
-
fundingFeesByCurrency
|
|
336385
|
-
})
|
|
337059
|
+
result: JSON.stringify({ accountId })
|
|
336386
337060
|
});
|
|
336387
337061
|
} catch (error48) {
|
|
336388
|
-
safeLogError(`Error fetching
|
|
337062
|
+
safeLogError(`Error fetching account ID ${cex3}`, error48);
|
|
336389
337063
|
ctx.wrappedCallback({
|
|
336390
|
-
code:
|
|
336391
|
-
message: `Error fetching
|
|
337064
|
+
code: grpc9.status.INTERNAL,
|
|
337065
|
+
message: `Error fetching account ID from ${cex3}`
|
|
336392
337066
|
}, null);
|
|
336393
337067
|
}
|
|
336394
337068
|
}
|
|
336395
337069
|
async function handleFetchDepositAddresses(ctx) {
|
|
336396
|
-
const {
|
|
336397
|
-
call,
|
|
336398
|
-
wrappedCallback,
|
|
336399
|
-
policy,
|
|
336400
|
-
brokers,
|
|
336401
|
-
metadata,
|
|
336402
|
-
normalizedCex,
|
|
336403
|
-
cex: cex3,
|
|
336404
|
-
symbol: symbol2,
|
|
336405
|
-
selectedBrokerAccount,
|
|
336406
|
-
broker,
|
|
336407
|
-
verity,
|
|
336408
|
-
applyVerityToBroker,
|
|
336409
|
-
useVerity,
|
|
336410
|
-
verityProverUrl,
|
|
336411
|
-
otelMetrics
|
|
336412
|
-
} = ctx;
|
|
336413
|
-
const verityProof = verity.proof;
|
|
337070
|
+
const { policy, cex: cex3, symbol: symbol2, broker } = ctx;
|
|
336414
337071
|
if (!symbol2) {
|
|
336415
337072
|
return ctx.wrappedCallback({
|
|
336416
|
-
code:
|
|
337073
|
+
code: grpc9.status.INVALID_ARGUMENT,
|
|
336417
337074
|
message: `ValidationError: Symbol required`
|
|
336418
337075
|
}, null);
|
|
336419
337076
|
}
|
|
@@ -336426,14 +337083,14 @@ async function handleFetchDepositAddresses(ctx) {
|
|
|
336426
337083
|
} catch (error48) {
|
|
336427
337084
|
const message = getErrorMessage(error48);
|
|
336428
337085
|
return ctx.wrappedCallback({
|
|
336429
|
-
code: stableGrpcErrorCode(message) ??
|
|
337086
|
+
code: stableGrpcErrorCode(message) ?? grpc9.status.INVALID_ARGUMENT,
|
|
336430
337087
|
message
|
|
336431
337088
|
}, null);
|
|
336432
337089
|
}
|
|
336433
337090
|
const depositValidation = validateDeposit(policy, cex3, depositNetwork.brokerNetworkId, symbol2);
|
|
336434
337091
|
if (!depositValidation.valid) {
|
|
336435
337092
|
return ctx.wrappedCallback({
|
|
336436
|
-
code:
|
|
337093
|
+
code: grpc9.status.PERMISSION_DENIED,
|
|
336437
337094
|
message: `policy_deposit_denied: ${depositValidation.error}`
|
|
336438
337095
|
}, null);
|
|
336439
337096
|
}
|
|
@@ -336459,45 +337116,30 @@ async function handleFetchDepositAddresses(ctx) {
|
|
|
336459
337116
|
});
|
|
336460
337117
|
}
|
|
336461
337118
|
ctx.wrappedCallback({
|
|
336462
|
-
code:
|
|
337119
|
+
code: grpc9.status.INTERNAL,
|
|
336463
337120
|
message: "Deposit confirmation failed"
|
|
336464
337121
|
}, null);
|
|
336465
337122
|
} catch (error48) {
|
|
336466
337123
|
safeLogError("Fetch Deposit Addresses confirmation failed", error48);
|
|
336467
337124
|
const message = getErrorMessage(error48);
|
|
336468
337125
|
ctx.wrappedCallback({
|
|
336469
|
-
code:
|
|
337126
|
+
code: grpc9.status.INTERNAL,
|
|
336470
337127
|
message: "Fetch Deposit Addresses confirmation failed: " + message
|
|
336471
337128
|
}, null);
|
|
336472
337129
|
}
|
|
336473
337130
|
}
|
|
336474
337131
|
async function handleFetchBalances(ctx) {
|
|
336475
|
-
const {
|
|
336476
|
-
call,
|
|
336477
|
-
wrappedCallback,
|
|
336478
|
-
policy,
|
|
336479
|
-
brokers,
|
|
336480
|
-
metadata,
|
|
336481
|
-
normalizedCex,
|
|
336482
|
-
cex: cex3,
|
|
336483
|
-
symbol: symbol2,
|
|
336484
|
-
selectedBrokerAccount,
|
|
336485
|
-
broker,
|
|
336486
|
-
verity,
|
|
336487
|
-
applyVerityToBroker,
|
|
336488
|
-
useVerity,
|
|
336489
|
-
verityProverUrl,
|
|
336490
|
-
otelMetrics
|
|
336491
|
-
} = ctx;
|
|
336492
|
-
const verityProof = verity.proof;
|
|
337132
|
+
const { call, cex: cex3, symbol: symbol2, broker } = ctx;
|
|
336493
337133
|
try {
|
|
336494
|
-
const payload =
|
|
336495
|
-
|
|
337134
|
+
const payload = {
|
|
337135
|
+
...call.request.payload ?? {}
|
|
337136
|
+
};
|
|
337137
|
+
const providedBalanceType = typeof payload.balanceType === "string" ? payload.balanceType : undefined;
|
|
336496
337138
|
const balanceType = (providedBalanceType ?? "total").toString();
|
|
336497
337139
|
const validBalanceTypes = new Set(["free", "used", "total"]);
|
|
336498
337140
|
if (!validBalanceTypes.has(balanceType)) {
|
|
336499
337141
|
return ctx.wrappedCallback({
|
|
336500
|
-
code:
|
|
337142
|
+
code: grpc9.status.INVALID_ARGUMENT,
|
|
336501
337143
|
message: `ValidationError: invalid balanceType '${providedBalanceType}'. Expected one of: free | used | total`
|
|
336502
337144
|
}, null);
|
|
336503
337145
|
}
|
|
@@ -336538,33 +337180,16 @@ async function handleFetchBalances(ctx) {
|
|
|
336538
337180
|
} catch (error48) {
|
|
336539
337181
|
safeLogError(`Error fetching balance from ${cex3}`, error48);
|
|
336540
337182
|
ctx.wrappedCallback({
|
|
336541
|
-
code:
|
|
337183
|
+
code: grpc9.status.INTERNAL,
|
|
336542
337184
|
message: `Failed to fetch balance from ${cex3}`
|
|
336543
337185
|
}, null);
|
|
336544
337186
|
}
|
|
336545
337187
|
}
|
|
336546
337188
|
async function handleFetchTicker(ctx) {
|
|
336547
|
-
const {
|
|
336548
|
-
call,
|
|
336549
|
-
wrappedCallback,
|
|
336550
|
-
policy,
|
|
336551
|
-
brokers,
|
|
336552
|
-
metadata,
|
|
336553
|
-
normalizedCex,
|
|
336554
|
-
cex: cex3,
|
|
336555
|
-
symbol: symbol2,
|
|
336556
|
-
selectedBrokerAccount,
|
|
336557
|
-
broker,
|
|
336558
|
-
verity,
|
|
336559
|
-
applyVerityToBroker,
|
|
336560
|
-
useVerity,
|
|
336561
|
-
verityProverUrl,
|
|
336562
|
-
otelMetrics
|
|
336563
|
-
} = ctx;
|
|
336564
|
-
const verityProof = verity.proof;
|
|
337189
|
+
const { cex: cex3, symbol: symbol2, broker } = ctx;
|
|
336565
337190
|
if (!symbol2) {
|
|
336566
337191
|
return ctx.wrappedCallback({
|
|
336567
|
-
code:
|
|
337192
|
+
code: grpc9.status.INVALID_ARGUMENT,
|
|
336568
337193
|
message: `ValidationError: Symbol required`
|
|
336569
337194
|
}, null);
|
|
336570
337195
|
}
|
|
@@ -336577,18 +337202,20 @@ async function handleFetchTicker(ctx) {
|
|
|
336577
337202
|
} catch (error48) {
|
|
336578
337203
|
safeLogError(`Error fetching ticker from ${cex3}`, error48);
|
|
336579
337204
|
ctx.wrappedCallback({
|
|
336580
|
-
code:
|
|
337205
|
+
code: grpc9.status.INTERNAL,
|
|
336581
337206
|
message: `Failed to fetch ticker from ${cex3}`
|
|
336582
337207
|
}, null);
|
|
336583
337208
|
}
|
|
336584
337209
|
}
|
|
336585
337210
|
async function handlePassThrough(ctx) {
|
|
336586
337211
|
if (ctx.action === Action.FetchCurrency)
|
|
336587
|
-
return
|
|
337212
|
+
return handleFetchCurrencyEvidence(ctx);
|
|
336588
337213
|
if (ctx.action === Action.FetchAccountId)
|
|
336589
337214
|
return handleFetchAccountId(ctx);
|
|
336590
337215
|
if (ctx.action === Action.FetchFees)
|
|
336591
|
-
return
|
|
337216
|
+
return handleFetchFeesEvidence(ctx);
|
|
337217
|
+
if (ctx.action === Action.FetchMarketRules)
|
|
337218
|
+
return handleFetchMarketRulesEvidence(ctx);
|
|
336592
337219
|
if (ctx.action === Action.FetchDepositAddresses)
|
|
336593
337220
|
return handleFetchDepositAddresses(ctx);
|
|
336594
337221
|
if (ctx.action === Action.FetchBalances)
|
|
@@ -336598,7 +337225,7 @@ async function handlePassThrough(ctx) {
|
|
|
336598
337225
|
}
|
|
336599
337226
|
|
|
336600
337227
|
// src/handlers/execute-action/perp-config.ts
|
|
336601
|
-
var
|
|
337228
|
+
var grpc10 = __toESM(require_src3(), 1);
|
|
336602
337229
|
function exchangeSupports(broker, capability) {
|
|
336603
337230
|
return broker.has?.[capability] === true;
|
|
336604
337231
|
}
|
|
@@ -336617,14 +337244,14 @@ async function handleGetPerpConfigState(ctx) {
|
|
|
336617
337244
|
}
|
|
336618
337245
|
if (!broker) {
|
|
336619
337246
|
return wrappedCallback({
|
|
336620
|
-
code:
|
|
337247
|
+
code: grpc10.status.INVALID_ARGUMENT,
|
|
336621
337248
|
message: `Invalid CEX key: ${cex3}`
|
|
336622
337249
|
}, null);
|
|
336623
337250
|
}
|
|
336624
337251
|
const exchange = broker;
|
|
336625
337252
|
if (!exchangeSupports(exchange, "fetchPositions")) {
|
|
336626
337253
|
return wrappedCallback({
|
|
336627
|
-
code:
|
|
337254
|
+
code: grpc10.status.UNIMPLEMENTED,
|
|
336628
337255
|
message: `${normalizedCex} does not support fetchPositions`
|
|
336629
337256
|
}, null);
|
|
336630
337257
|
}
|
|
@@ -336641,7 +337268,7 @@ async function handleGetPerpConfigState(ctx) {
|
|
|
336641
337268
|
} catch (error48) {
|
|
336642
337269
|
safeLogError(`GetPerpConfigState failed for ${cex3}`, error48);
|
|
336643
337270
|
ctx.wrappedCallback({
|
|
336644
|
-
code:
|
|
337271
|
+
code: grpc10.status.INTERNAL,
|
|
336645
337272
|
message: `GetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
|
|
336646
337273
|
}, null);
|
|
336647
337274
|
}
|
|
@@ -336654,14 +337281,14 @@ async function handleSetPerpConfigState(ctx) {
|
|
|
336654
337281
|
}
|
|
336655
337282
|
if (!broker) {
|
|
336656
337283
|
return wrappedCallback({
|
|
336657
|
-
code:
|
|
337284
|
+
code: grpc10.status.INVALID_ARGUMENT,
|
|
336658
337285
|
message: `Invalid CEX key: ${cex3}`
|
|
336659
337286
|
}, null);
|
|
336660
337287
|
}
|
|
336661
337288
|
const exchange = broker;
|
|
336662
337289
|
if (!exchangeSupports(exchange, "setLeverage")) {
|
|
336663
337290
|
return wrappedCallback({
|
|
336664
|
-
code:
|
|
337291
|
+
code: grpc10.status.UNIMPLEMENTED,
|
|
336665
337292
|
message: `${normalizedCex} does not support setLeverage`
|
|
336666
337293
|
}, null);
|
|
336667
337294
|
}
|
|
@@ -336682,7 +337309,7 @@ async function handleSetPerpConfigState(ctx) {
|
|
|
336682
337309
|
} catch (error48) {
|
|
336683
337310
|
safeLogError(`SetPerpConfigState failed for ${cex3}`, error48);
|
|
336684
337311
|
ctx.wrappedCallback({
|
|
336685
|
-
code:
|
|
337312
|
+
code: grpc10.status.INTERNAL,
|
|
336686
337313
|
message: `SetPerpConfigState failed: ${sanitizeErrorDetail(error48)}`
|
|
336687
337314
|
}, null);
|
|
336688
337315
|
}
|
|
@@ -336697,7 +337324,7 @@ async function handlePerpConfig(ctx) {
|
|
|
336697
337324
|
}
|
|
336698
337325
|
|
|
336699
337326
|
// src/handlers/execute-action/treasury-call.ts
|
|
336700
|
-
var
|
|
337327
|
+
var grpc11 = __toESM(require_src3(), 1);
|
|
336701
337328
|
async function handleTreasuryCall(ctx) {
|
|
336702
337329
|
const { broker } = ctx;
|
|
336703
337330
|
const callValue = parsePayloadForAction(ctx, CallPayloadSchema);
|
|
@@ -336708,7 +337335,7 @@ async function handleTreasuryCall(ctx) {
|
|
|
336708
337335
|
try {
|
|
336709
337336
|
if (callValue.functionName.startsWith("_") || callValue.functionName.includes("constructor") || callValue.functionName.includes("prototype")) {
|
|
336710
337337
|
return ctx.wrappedCallback({
|
|
336711
|
-
code:
|
|
337338
|
+
code: grpc11.status.PERMISSION_DENIED,
|
|
336712
337339
|
message: "Access to the requested function is denied"
|
|
336713
337340
|
}, null);
|
|
336714
337341
|
}
|
|
@@ -336723,7 +337350,7 @@ async function handleTreasuryCall(ctx) {
|
|
|
336723
337350
|
const fn = broker[callValue.functionName];
|
|
336724
337351
|
if (typeof fn !== "function" || broker.has?.[callValue.functionName] === false) {
|
|
336725
337352
|
return ctx.wrappedCallback({
|
|
336726
|
-
code:
|
|
337353
|
+
code: grpc11.status.INVALID_ARGUMENT,
|
|
336727
337354
|
message: `Function not found on broker: ${callValue.functionName}`
|
|
336728
337355
|
}, null);
|
|
336729
337356
|
}
|
|
@@ -336802,7 +337429,7 @@ function asFiniteNumber(value) {
|
|
|
336802
337429
|
}
|
|
336803
337430
|
|
|
336804
337431
|
// src/handlers/execute-action/withdraw.ts
|
|
336805
|
-
var
|
|
337432
|
+
var grpc12 = __toESM(require_src3(), 1);
|
|
336806
337433
|
async function handleWithdraw(ctx) {
|
|
336807
337434
|
const {
|
|
336808
337435
|
call,
|
|
@@ -336825,7 +337452,7 @@ async function handleWithdraw(ctx) {
|
|
|
336825
337452
|
const verityProof = verity.proof;
|
|
336826
337453
|
if (!symbol2) {
|
|
336827
337454
|
return ctx.wrappedCallback({
|
|
336828
|
-
code:
|
|
337455
|
+
code: grpc12.status.INVALID_ARGUMENT,
|
|
336829
337456
|
message: `ValidationError: Symbol required`
|
|
336830
337457
|
}, null);
|
|
336831
337458
|
}
|
|
@@ -336838,21 +337465,21 @@ async function handleWithdraw(ctx) {
|
|
|
336838
337465
|
} catch (error48) {
|
|
336839
337466
|
const message = getErrorMessage(error48);
|
|
336840
337467
|
return ctx.wrappedCallback({
|
|
336841
|
-
code: stableGrpcErrorCode(message) ??
|
|
337468
|
+
code: stableGrpcErrorCode(message) ?? grpc12.status.INVALID_ARGUMENT,
|
|
336842
337469
|
message
|
|
336843
337470
|
}, null);
|
|
336844
337471
|
}
|
|
336845
337472
|
const transferValidation = validateWithdraw(policy, cex3, withdrawNetwork.brokerNetworkId, transferValue.recipientAddress, transferValue.amount, symbol2);
|
|
336846
337473
|
if (!transferValidation.valid) {
|
|
336847
337474
|
return ctx.wrappedCallback({
|
|
336848
|
-
code:
|
|
337475
|
+
code: grpc12.status.PERMISSION_DENIED,
|
|
336849
337476
|
message: `policy_withdrawal_denied: ${transferValidation.error}`
|
|
336850
337477
|
}, null);
|
|
336851
337478
|
}
|
|
336852
337479
|
const travelRule = resolveTravelRuleDecision(policy, cex3, transferValue.recipientAddress);
|
|
336853
337480
|
if (travelRule.mode === "denied") {
|
|
336854
337481
|
return ctx.wrappedCallback({
|
|
336855
|
-
code:
|
|
337482
|
+
code: grpc12.status.FAILED_PRECONDITION,
|
|
336856
337483
|
message: `travel_rule_denied: ${travelRule.error}`
|
|
336857
337484
|
}, null);
|
|
336858
337485
|
}
|
|
@@ -336919,7 +337546,7 @@ async function handleWithdraw(ctx) {
|
|
|
336919
337546
|
payload: { recipientAddress: transferValue.recipientAddress }
|
|
336920
337547
|
}
|
|
336921
337548
|
});
|
|
336922
|
-
const code = mapCcxtErrorToGrpcStatus(error48) ??
|
|
337549
|
+
const code = mapCcxtErrorToGrpcStatus(error48) ?? grpc12.status.INTERNAL;
|
|
336923
337550
|
ctx.wrappedCallback({
|
|
336924
337551
|
code,
|
|
336925
337552
|
message: `Withdraw failed: ${sanitizeErrorDetail(error48)}`
|
|
@@ -336928,33 +337555,152 @@ async function handleWithdraw(ctx) {
|
|
|
336928
337555
|
}
|
|
336929
337556
|
|
|
336930
337557
|
// src/handlers/execute-action/registry.ts
|
|
336931
|
-
|
|
336932
|
-
|
|
336933
|
-
|
|
336934
|
-
|
|
336935
|
-
|
|
336936
|
-
|
|
336937
|
-
|
|
336938
|
-
|
|
336939
|
-
[
|
|
336940
|
-
|
|
336941
|
-
|
|
336942
|
-
|
|
336943
|
-
|
|
336944
|
-
|
|
336945
|
-
|
|
336946
|
-
|
|
337558
|
+
function valid() {
|
|
337559
|
+
return { valid: true };
|
|
337560
|
+
}
|
|
337561
|
+
function requireSymbol2(request) {
|
|
337562
|
+
return request.symbol?.trim() ? valid() : { valid: false, message: "symbol is required" };
|
|
337563
|
+
}
|
|
337564
|
+
function requireSpotSymbol(request) {
|
|
337565
|
+
const symbol2 = request.symbol?.trim() ?? "";
|
|
337566
|
+
return /^[^/\s]+\/[^/\s]+$/.test(symbol2) ? valid() : {
|
|
337567
|
+
valid: false,
|
|
337568
|
+
message: "symbol must be a slash-delimited spot pair"
|
|
337569
|
+
};
|
|
337570
|
+
}
|
|
337571
|
+
function validatePayload(schema, request) {
|
|
337572
|
+
const parsed = parsePayload(schema, request.payload);
|
|
337573
|
+
return parsed.success ? valid() : { valid: false, message: parsed.message };
|
|
337574
|
+
}
|
|
337575
|
+
function validateSymbolAndPayload(schema) {
|
|
337576
|
+
return (request) => {
|
|
337577
|
+
const symbolValidation = requireSymbol2(request);
|
|
337578
|
+
return symbolValidation.valid ? validatePayload(schema, request) : symbolValidation;
|
|
337579
|
+
};
|
|
337580
|
+
}
|
|
337581
|
+
function validateSpotSymbolAndPayload(schema) {
|
|
337582
|
+
return (request) => {
|
|
337583
|
+
const symbolValidation = requireSpotSymbol(request);
|
|
337584
|
+
return symbolValidation.valid ? validatePayload(schema, request) : symbolValidation;
|
|
337585
|
+
};
|
|
337586
|
+
}
|
|
337587
|
+
function validateFetchBalances(request) {
|
|
337588
|
+
const balanceType = request.payload?.balanceType;
|
|
337589
|
+
if (balanceType !== undefined && !new Set(["free", "used", "total"]).has(balanceType)) {
|
|
337590
|
+
return {
|
|
337591
|
+
valid: false,
|
|
337592
|
+
message: "balanceType must be free, used, or total"
|
|
337593
|
+
};
|
|
337594
|
+
}
|
|
337595
|
+
return valid();
|
|
337596
|
+
}
|
|
337597
|
+
var ACTION_DESCRIPTORS = {
|
|
337598
|
+
[Action.Deposit]: {
|
|
337599
|
+
handler: handleDeposit,
|
|
337600
|
+
access: "write",
|
|
337601
|
+
batchable: false
|
|
337602
|
+
},
|
|
337603
|
+
[Action.Withdraw]: {
|
|
337604
|
+
handler: handleWithdraw,
|
|
337605
|
+
access: "write",
|
|
337606
|
+
batchable: false
|
|
337607
|
+
},
|
|
337608
|
+
[Action.Call]: {
|
|
337609
|
+
handler: handleTreasuryCall,
|
|
337610
|
+
access: "write",
|
|
337611
|
+
batchable: false
|
|
337612
|
+
},
|
|
337613
|
+
[Action.InternalTransfer]: {
|
|
337614
|
+
handler: handleInternalTransfer,
|
|
337615
|
+
access: "write",
|
|
337616
|
+
batchable: false
|
|
337617
|
+
},
|
|
337618
|
+
[Action.CreateOrder]: {
|
|
337619
|
+
handler: handleOrders,
|
|
337620
|
+
access: "write",
|
|
337621
|
+
batchable: false
|
|
337622
|
+
},
|
|
337623
|
+
[Action.GetOrderDetails]: {
|
|
337624
|
+
handler: handleOrders,
|
|
337625
|
+
access: "read",
|
|
337626
|
+
batchable: false
|
|
337627
|
+
},
|
|
337628
|
+
[Action.CancelOrder]: {
|
|
337629
|
+
handler: handleOrders,
|
|
337630
|
+
access: "write",
|
|
337631
|
+
batchable: false
|
|
337632
|
+
},
|
|
337633
|
+
[Action.FetchCurrency]: {
|
|
337634
|
+
handler: handlePassThrough,
|
|
337635
|
+
access: "read",
|
|
337636
|
+
batchable: true,
|
|
337637
|
+
validateBatchRequest: validateSymbolAndPayload(FetchCurrencyPayloadSchema)
|
|
337638
|
+
},
|
|
337639
|
+
[Action.FetchAccountId]: {
|
|
337640
|
+
handler: handlePassThrough,
|
|
337641
|
+
access: "read",
|
|
337642
|
+
batchable: true,
|
|
337643
|
+
validateBatchRequest: (request) => validatePayload(EmptyActionPayloadSchema, request)
|
|
337644
|
+
},
|
|
337645
|
+
[Action.FetchFees]: {
|
|
337646
|
+
handler: handlePassThrough,
|
|
337647
|
+
access: "read",
|
|
337648
|
+
batchable: true,
|
|
337649
|
+
validateBatchRequest: validateSpotSymbolAndPayload(FetchFeesPayloadSchema)
|
|
337650
|
+
},
|
|
337651
|
+
[Action.FetchDepositAddresses]: {
|
|
337652
|
+
handler: handlePassThrough,
|
|
337653
|
+
access: "read",
|
|
337654
|
+
batchable: false
|
|
337655
|
+
},
|
|
337656
|
+
[Action.FetchBalances]: {
|
|
337657
|
+
handler: handlePassThrough,
|
|
337658
|
+
access: "read",
|
|
337659
|
+
batchable: true,
|
|
337660
|
+
validateBatchRequest: validateFetchBalances
|
|
337661
|
+
},
|
|
337662
|
+
[Action.FetchTicker]: {
|
|
337663
|
+
handler: handlePassThrough,
|
|
337664
|
+
access: "read",
|
|
337665
|
+
batchable: true,
|
|
337666
|
+
validateBatchRequest: validateSymbolAndPayload(EmptyActionPayloadSchema)
|
|
337667
|
+
},
|
|
337668
|
+
[Action.GetPerpConfigState]: {
|
|
337669
|
+
handler: handlePerpConfig,
|
|
337670
|
+
access: "read",
|
|
337671
|
+
batchable: true,
|
|
337672
|
+
validateBatchRequest: (request) => validatePayload(GetPerpConfigStatePayloadSchema, request)
|
|
337673
|
+
},
|
|
337674
|
+
[Action.SetPerpConfigState]: {
|
|
337675
|
+
handler: handlePerpConfig,
|
|
337676
|
+
access: "write",
|
|
337677
|
+
batchable: false
|
|
337678
|
+
},
|
|
337679
|
+
[Action.FetchMarketRules]: {
|
|
337680
|
+
handler: handlePassThrough,
|
|
337681
|
+
access: "read",
|
|
337682
|
+
batchable: true,
|
|
337683
|
+
validateBatchRequest: validateSpotSymbolAndPayload(EmptyActionPayloadSchema)
|
|
337684
|
+
},
|
|
337685
|
+
[Action.Batch]: {
|
|
337686
|
+
handler: (ctx) => handleBatch(ctx, getActionDescriptor),
|
|
337687
|
+
access: "read",
|
|
337688
|
+
batchable: false
|
|
337689
|
+
}
|
|
336947
337690
|
};
|
|
337691
|
+
function getActionDescriptor(action) {
|
|
337692
|
+
return ACTION_DESCRIPTORS[action];
|
|
337693
|
+
}
|
|
336948
337694
|
async function dispatchExecuteAction(ctx) {
|
|
336949
|
-
const
|
|
336950
|
-
if (!
|
|
337695
|
+
const descriptor = getActionDescriptor(ctx.action);
|
|
337696
|
+
if (!descriptor) {
|
|
336951
337697
|
ctx.wrappedCallback({
|
|
336952
|
-
code:
|
|
337698
|
+
code: grpc13.status.INVALID_ARGUMENT,
|
|
336953
337699
|
message: "Invalid Action"
|
|
336954
337700
|
}, null);
|
|
336955
337701
|
return;
|
|
336956
337702
|
}
|
|
336957
|
-
await handler(ctx);
|
|
337703
|
+
await descriptor.handler(ctx);
|
|
336958
337704
|
}
|
|
336959
337705
|
|
|
336960
337706
|
// src/handlers/execute-action/handler.ts
|
|
@@ -336970,7 +337716,7 @@ function grpcStatusName(error48) {
|
|
|
336970
337716
|
if (typeof error48.code !== "number") {
|
|
336971
337717
|
return "UNKNOWN";
|
|
336972
337718
|
}
|
|
336973
|
-
return
|
|
337719
|
+
return grpc14.status[error48.code] ?? "UNKNOWN";
|
|
336974
337720
|
}
|
|
336975
337721
|
function createExecuteActionHandler(deps) {
|
|
336976
337722
|
const {
|
|
@@ -337018,7 +337764,7 @@ function createExecuteActionHandler(deps) {
|
|
|
337018
337764
|
otelMetrics?.recordCounter("execute_action_errors_total", 1, {
|
|
337019
337765
|
action: actionName,
|
|
337020
337766
|
cex: cex3 || "unknown",
|
|
337021
|
-
error_type: error48.code ?
|
|
337767
|
+
error_type: error48.code ? grpc14.status[error48.code] || "unknown" : "unknown"
|
|
337022
337768
|
});
|
|
337023
337769
|
} else {
|
|
337024
337770
|
otelMetrics?.recordCounter("execute_action_success_total", 1, {
|
|
@@ -337041,13 +337787,13 @@ function createExecuteActionHandler(deps) {
|
|
|
337041
337787
|
});
|
|
337042
337788
|
if (!authenticateRequest(call, whitelistIps)) {
|
|
337043
337789
|
return wrappedCallback({
|
|
337044
|
-
code:
|
|
337790
|
+
code: grpc14.status.PERMISSION_DENIED,
|
|
337045
337791
|
message: "Access denied: Unauthorized IP"
|
|
337046
337792
|
}, null);
|
|
337047
337793
|
}
|
|
337048
337794
|
if (!action || !cex3) {
|
|
337049
337795
|
return wrappedCallback({
|
|
337050
|
-
code:
|
|
337796
|
+
code: grpc14.status.INVALID_ARGUMENT,
|
|
337051
337797
|
message: "`action` AND `cex` fields are required"
|
|
337052
337798
|
}, null);
|
|
337053
337799
|
}
|
|
@@ -337057,17 +337803,20 @@ function createExecuteActionHandler(deps) {
|
|
|
337057
337803
|
const broker = selectedBrokerAccount?.exchange ?? createBroker(normalizedCex, call.metadata) ?? (isPublicMarketDataAction(action, call.request.payload) ? createPublicBroker(normalizedCex) : null);
|
|
337058
337804
|
if (!broker) {
|
|
337059
337805
|
return wrappedCallback({
|
|
337060
|
-
code:
|
|
337806
|
+
code: grpc14.status.UNAUTHENTICATED,
|
|
337061
337807
|
message: `This Exchange is not registered and No API metadata was found`
|
|
337062
337808
|
}, null);
|
|
337063
337809
|
}
|
|
337064
337810
|
const verity = { proof: "" };
|
|
337065
|
-
const applyVerityToBroker = (targetBroker) => {
|
|
337811
|
+
const applyVerityToBroker = (targetBroker, proofState = verity) => {
|
|
337066
337812
|
if (!useVerity)
|
|
337067
337813
|
return;
|
|
337068
337814
|
const override = buildHttpClientOverrideFromMetadata(metadata, verityProverUrl, (proof, notaryPubKey) => {
|
|
337069
|
-
|
|
337070
|
-
log.debug(`Verity proof
|
|
337815
|
+
proofState.proof = proof;
|
|
337816
|
+
log.debug(`Verity proof received`, {
|
|
337817
|
+
has_proof: proof.length > 0,
|
|
337818
|
+
has_notary_public_key: Boolean(notaryPubKey)
|
|
337819
|
+
});
|
|
337071
337820
|
});
|
|
337072
337821
|
targetBroker.setHttpClientOverride(override, verityHttpClientOverridePredicate);
|
|
337073
337822
|
};
|
|
@@ -337103,7 +337852,7 @@ function createExecuteActionHandler(deps) {
|
|
|
337103
337852
|
} catch (error48) {
|
|
337104
337853
|
safeLogError("ExecuteAction unhandled error", error48);
|
|
337105
337854
|
return wrappedCallback({
|
|
337106
|
-
code:
|
|
337855
|
+
code: grpc14.status.INTERNAL,
|
|
337107
337856
|
message: "ExecuteAction failed unexpectedly"
|
|
337108
337857
|
}, null);
|
|
337109
337858
|
}
|
|
@@ -337163,7 +337912,7 @@ class SubscribeBrokerLifecycle {
|
|
|
337163
337912
|
}
|
|
337164
337913
|
}
|
|
337165
337914
|
// src/handlers/subscribe/handler.ts
|
|
337166
|
-
var
|
|
337915
|
+
var grpc15 = __toESM(require_src3(), 1);
|
|
337167
337916
|
|
|
337168
337917
|
// src/helpers/binance-user-data-normalization.ts
|
|
337169
337918
|
function requireQuantity(entry, key2) {
|
|
@@ -337274,8 +338023,8 @@ async function writeSubscribeError(call, isClosed, frame) {
|
|
|
337274
338023
|
call.end();
|
|
337275
338024
|
}
|
|
337276
338025
|
}
|
|
337277
|
-
function grpcStatusName2(
|
|
337278
|
-
return typeof
|
|
338026
|
+
function grpcStatusName2(status16) {
|
|
338027
|
+
return typeof status16 === "number" ? grpc15.status[status16] ?? "UNKNOWN" : "UNKNOWN";
|
|
337279
338028
|
}
|
|
337280
338029
|
function getBinanceEventMarketId(event) {
|
|
337281
338030
|
const value = event.s;
|
|
@@ -337468,9 +338217,9 @@ function createSubscribeHandler(deps) {
|
|
|
337468
338217
|
log.withMetadata(fields).info("Subscribe ended");
|
|
337469
338218
|
}
|
|
337470
338219
|
};
|
|
337471
|
-
const writeTerminalError = async (frame,
|
|
338220
|
+
const writeTerminalError = async (frame, status16 = grpc15.status.UNKNOWN) => {
|
|
337472
338221
|
terminalOutcome = "error";
|
|
337473
|
-
terminalGrpcStatus = grpcStatusName2(
|
|
338222
|
+
terminalGrpcStatus = grpcStatusName2(status16);
|
|
337474
338223
|
await writeSubscribeError(call, isStreamClosed, frame);
|
|
337475
338224
|
};
|
|
337476
338225
|
log.withMetadata(operationalFields).info("Subscribe started");
|
|
@@ -337506,7 +338255,7 @@ function createSubscribeHandler(deps) {
|
|
|
337506
338255
|
error_type: "permission_denied"
|
|
337507
338256
|
});
|
|
337508
338257
|
call.emit("error", {
|
|
337509
|
-
code:
|
|
338258
|
+
code: grpc15.status.PERMISSION_DENIED,
|
|
337510
338259
|
message: "Access denied: Unauthorized IP"
|
|
337511
338260
|
}, null);
|
|
337512
338261
|
call.destroy(new Error("Access denied: Unauthorized IP"));
|
|
@@ -337527,7 +338276,7 @@ function createSubscribeHandler(deps) {
|
|
|
337527
338276
|
timestamp: Date.now(),
|
|
337528
338277
|
symbol: symbol2 || "",
|
|
337529
338278
|
type: subscriptionType2
|
|
337530
|
-
},
|
|
338279
|
+
}, grpc15.status.INVALID_ARGUMENT);
|
|
337531
338280
|
return;
|
|
337532
338281
|
}
|
|
337533
338282
|
if (isPublicMarketDataSubscription(subscriptionType2)) {
|
|
@@ -337584,7 +338333,7 @@ function createSubscribeHandler(deps) {
|
|
|
337584
338333
|
timestamp: Date.now(),
|
|
337585
338334
|
symbol: symbol2,
|
|
337586
338335
|
type: subscriptionType2
|
|
337587
|
-
},
|
|
338336
|
+
}, grpc15.status.NOT_FOUND);
|
|
337588
338337
|
return;
|
|
337589
338338
|
}
|
|
337590
338339
|
if (!selectedBrokerAccount) {
|
|
@@ -337619,7 +338368,7 @@ function createSubscribeHandler(deps) {
|
|
|
337619
338368
|
timestamp: Date.now(),
|
|
337620
338369
|
symbol: resolvedSymbol,
|
|
337621
338370
|
type: subscriptionType2
|
|
337622
|
-
},
|
|
338371
|
+
}, grpc15.status.FAILED_PRECONDITION);
|
|
337623
338372
|
return;
|
|
337624
338373
|
}
|
|
337625
338374
|
const marketId = subscriptionType2 === SubscriptionType.ORDERS ? await getBinanceMarketId(accountBroker, resolvedSymbol) : undefined;
|
|
@@ -337633,7 +338382,7 @@ function createSubscribeHandler(deps) {
|
|
|
337633
338382
|
timestamp: Date.now(),
|
|
337634
338383
|
symbol: resolvedSymbol,
|
|
337635
338384
|
type: subscriptionType2
|
|
337636
|
-
},
|
|
338385
|
+
}, grpc15.status.FAILED_PRECONDITION);
|
|
337637
338386
|
return;
|
|
337638
338387
|
}
|
|
337639
338388
|
userDataSource = userDataStreamSupervisor.subscribe({
|
|
@@ -337691,7 +338440,7 @@ function createSubscribeHandler(deps) {
|
|
|
337691
338440
|
timestamp: Date.now(),
|
|
337692
338441
|
symbol: symbol2,
|
|
337693
338442
|
type: subscriptionType2
|
|
337694
|
-
},
|
|
338443
|
+
}, grpc15.status.INVALID_ARGUMENT);
|
|
337695
338444
|
}
|
|
337696
338445
|
} catch (error48) {
|
|
337697
338446
|
log.error("Error in Subscribe stream:", error48);
|
|
@@ -337701,7 +338450,7 @@ function createSubscribeHandler(deps) {
|
|
|
337701
338450
|
timestamp: Date.now(),
|
|
337702
338451
|
symbol: "",
|
|
337703
338452
|
type: subscriptionType2
|
|
337704
|
-
},
|
|
338453
|
+
}, grpc15.status.INTERNAL);
|
|
337705
338454
|
} finally {
|
|
337706
338455
|
call.off("cancelled", closeOwnedBrokerOnCallEnd);
|
|
337707
338456
|
call.off("error", closeOwnedBrokerOnCallEnd);
|
|
@@ -337833,7 +338582,9 @@ var descriptor = {
|
|
|
337833
338582
|
FetchFees: 12,
|
|
337834
338583
|
InternalTransfer: 13,
|
|
337835
338584
|
GetPerpConfigState: 14,
|
|
337836
|
-
SetPerpConfigState: 15
|
|
338585
|
+
SetPerpConfigState: 15,
|
|
338586
|
+
FetchMarketRules: 16,
|
|
338587
|
+
Batch: 17
|
|
337837
338588
|
}
|
|
337838
338589
|
}
|
|
337839
338590
|
}
|
|
@@ -337855,10 +338606,10 @@ var PROTO_LOADER_OPTIONS = {
|
|
|
337855
338606
|
var CEX_BROKER_PACKAGE_DEFINITION = protoLoader.fromJSON(node_descriptor_default, PROTO_LOADER_OPTIONS);
|
|
337856
338607
|
|
|
337857
338608
|
// src/server.ts
|
|
337858
|
-
var grpcObj =
|
|
338609
|
+
var grpcObj = grpc16.loadPackageDefinition(CEX_BROKER_PACKAGE_DEFINITION);
|
|
337859
338610
|
var cexNode = grpcObj.cex_broker;
|
|
337860
338611
|
function getServer(policy, brokers, whitelistIps, useVerity, verityProverUrl, otelMetrics, brokerArchiver, orderActivityTracker, withdrawalObservationTracker, subscribeBrokerLifecycle, userDataStreamSupervisor, publicMarketDataFeedSupervisor) {
|
|
337861
|
-
const server = new
|
|
338612
|
+
const server = new grpc16.Server;
|
|
337862
338613
|
server.addService(cexNode.cex_service.service, {
|
|
337863
338614
|
ExecuteAction: createExecuteActionHandler({
|
|
337864
338615
|
policy,
|
|
@@ -338247,7 +338998,10 @@ class CEXBroker {
|
|
|
338247
338998
|
await this.otelMetrics.initialize();
|
|
338248
338999
|
}
|
|
338249
339000
|
if (!this.userDataStreamSupervisor && Object.keys(this.brokers).length > 0) {
|
|
338250
|
-
const publisher = new StreamHealthPublisher(
|
|
339001
|
+
const publisher = new StreamHealthPublisher({
|
|
339002
|
+
...streamHealthPublisherConfigFromEnv(),
|
|
339003
|
+
producerId: USER_DATA_STREAM_HEALTH_PRODUCER_ID
|
|
339004
|
+
});
|
|
338251
339005
|
this.userDataStreamSupervisor = new UserDataStreamSupervisor({
|
|
338252
339006
|
brokers: this.brokers,
|
|
338253
339007
|
publisher
|
|
@@ -338260,7 +339014,7 @@ class CEXBroker {
|
|
|
338260
339014
|
otelMetrics: this.otelMetrics
|
|
338261
339015
|
});
|
|
338262
339016
|
this.server = getServer(this.policy, this.brokers, this.whitelistIps, this.useVerity, this.#verityProverUrl, this.otelMetrics, this.brokerArchiver, this.orderActivityTracker, this.withdrawalObservationTracker, undefined, this.userDataStreamSupervisor, this.publicMarketDataFeedSupervisor);
|
|
338263
|
-
this.server.bindAsync(`0.0.0.0:${this.port}`,
|
|
339017
|
+
this.server.bindAsync(`0.0.0.0:${this.port}`, grpc17.ServerCredentials.createInsecure(), (err2, port) => {
|
|
338264
339018
|
if (err2) {
|
|
338265
339019
|
log.error(err2);
|
|
338266
339020
|
return;
|
|
@@ -338285,7 +339039,8 @@ class CEXBroker {
|
|
|
338285
339039
|
this.depositArchivePoller = new DepositArchivePoller({
|
|
338286
339040
|
brokers: this.brokers,
|
|
338287
339041
|
archiver: this.brokerArchiver,
|
|
338288
|
-
metrics: this.otelMetrics
|
|
339042
|
+
metrics: this.otelMetrics,
|
|
339043
|
+
coveragePublisher: Object.keys(this.brokers).length > 0 ? new StreamHealthPublisher(depositPollerStreamHealthPublisherConfigFromEnv()) : undefined
|
|
338289
339044
|
});
|
|
338290
339045
|
this.depositArchivePoller.start();
|
|
338291
339046
|
if (this.userDataStreamSupervisor) {
|