@usherlabs/cex-broker 0.2.17 → 0.2.18

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.
@@ -313971,6 +313971,10 @@ function authenticateRequest(call, whitelistIps) {
313971
313971
  }
313972
313972
  return true;
313973
313973
  }
313974
+ // src/helpers/travel-rule-deposit-reconciler.ts
313975
+ import { request as httpRequest } from "node:http";
313976
+ import { request as httpsRequest } from "node:https";
313977
+
313974
313978
  // src/helpers/deposit.ts
313975
313979
  function depositField(deposit, fields) {
313976
313980
  for (const field of fields) {
@@ -314051,36 +314055,54 @@ function parseLocalEntityDeposit(raw) {
314051
314055
  };
314052
314056
  }
314053
314057
  var EVM_TX_HASH = /^0x[0-9a-fA-F]{64}$/;
314054
- async function resolveOnChainSender(rpcUrl, txHash, timeoutMs = 1e4) {
314058
+ function resolveOnChainSender(rpcUrl, txHash, timeoutMs = 1e4) {
314055
314059
  if (!EVM_TX_HASH.test(txHash))
314056
- return null;
314057
- const controller = new AbortController;
314058
- const timeout2 = setTimeout(() => controller.abort(), timeoutMs);
314059
- let response;
314060
- try {
314061
- response = await fetch(rpcUrl, {
314060
+ return Promise.resolve(null);
314061
+ const body = JSON.stringify({
314062
+ jsonrpc: "2.0",
314063
+ id: 1,
314064
+ method: "eth_getTransactionByHash",
314065
+ params: [txHash]
314066
+ });
314067
+ const url2 = new URL(rpcUrl);
314068
+ const doRequest = url2.protocol === "http:" ? httpRequest : httpsRequest;
314069
+ return new Promise((resolve, reject) => {
314070
+ const req = doRequest(url2, {
314062
314071
  method: "POST",
314063
- headers: { "content-type": "application/json" },
314064
- signal: controller.signal,
314065
- body: JSON.stringify({
314066
- jsonrpc: "2.0",
314067
- id: 1,
314068
- method: "eth_getTransactionByHash",
314069
- params: [txHash]
314070
- })
314072
+ headers: {
314073
+ "content-type": "application/json",
314074
+ "content-length": Buffer.byteLength(body)
314075
+ },
314076
+ timeout: timeoutMs
314077
+ }, (res) => {
314078
+ const chunks = [];
314079
+ res.on("data", (chunk) => chunks.push(chunk));
314080
+ res.on("end", () => {
314081
+ const status = res.statusCode ?? 0;
314082
+ if (status < 200 || status >= 300) {
314083
+ reject(new Error(`travel_rule_rpc_http_${status}`));
314084
+ return;
314085
+ }
314086
+ try {
314087
+ const json3 = JSON.parse(Buffer.concat(chunks).toString("utf8"));
314088
+ if (json3.error) {
314089
+ reject(new Error(`travel_rule_rpc_error: ${JSON.stringify(json3.error)}`));
314090
+ return;
314091
+ }
314092
+ const from = json3.result?.from;
314093
+ resolve(typeof from === "string" && from.length > 0 ? from.toLowerCase() : null);
314094
+ } catch (parseError) {
314095
+ reject(new Error(`travel_rule_rpc_parse_error: ${String(parseError)}`));
314096
+ }
314097
+ });
314071
314098
  });
314072
- } finally {
314073
- clearTimeout(timeout2);
314074
- }
314075
- if (!response.ok) {
314076
- throw new Error(`travel_rule_rpc_http_${response.status}`);
314077
- }
314078
- const json3 = await response.json();
314079
- if (json3.error) {
314080
- throw new Error(`travel_rule_rpc_error: ${JSON.stringify(json3.error)}`);
314081
- }
314082
- const from = json3.result?.from;
314083
- return typeof from === "string" && from.length > 0 ? from.toLowerCase() : null;
314099
+ req.on("error", reject);
314100
+ req.on("timeout", () => {
314101
+ req.destroy(new Error("travel_rule_rpc_timeout"));
314102
+ });
314103
+ req.write(body);
314104
+ req.end();
314105
+ });
314084
314106
  }
314085
314107
  function errorText(error) {
314086
314108
  if (error instanceof Error)
@@ -314182,17 +314204,19 @@ async function reconcileAccountOnce(deps) {
314182
314204
  if (now3 < state.rateLimitedUntil)
314183
314205
  break;
314184
314206
  let sender = null;
314207
+ let resolveError;
314185
314208
  try {
314186
314209
  sender = await deps.resolveSender(deposit.network, deposit.txId);
314187
314210
  } catch (error) {
314188
314211
  sender = null;
314212
+ resolveError = errorText(error);
314189
314213
  if (isRateLimitError(error)) {
314190
314214
  state.rateLimitedUntil = now3 + deps.rateLimitCooldownMs;
314191
314215
  }
314192
314216
  }
314193
314217
  if (!sender) {
314194
314218
  state.backoffUntil.set(deposit.tranId, now3 + deps.failureBackoffMs);
314195
- outcomes.push({ kind: "unproven-origin", deposit });
314219
+ outcomes.push({ kind: "unproven-origin", deposit, error: resolveError });
314196
314220
  continue;
314197
314221
  }
314198
314222
  const questionnaire = deps.resolveQuestionnaire(deps.depositConfig, sender);
@@ -314448,7 +314472,8 @@ class TravelRuleDepositReconciler {
314448
314472
  tranId: outcome.deposit.tranId,
314449
314473
  coin: outcome.deposit.coin,
314450
314474
  network: outcome.deposit.network,
314451
- txId: outcome.deposit.txId
314475
+ txId: outcome.deposit.txId,
314476
+ rpcError: outcome.error ?? null
314452
314477
  });
314453
314478
  metrics?.recordCounter("travel_rule_deposit_anomalies_total", 1, {
314454
314479
  ...base2,
@@ -46,6 +46,12 @@ export declare function parseLocalEntityDeposit(raw: Record<string, unknown>): L
46
46
  * churn (owner wallet → Binance via a direct ERC20 transfer) that equals the
47
47
  * token originator. A transfer routed through a contract could differ; hardening
48
48
  * to the ERC20 Transfer event's `from` is possible later if that case arises.
49
+ *
50
+ * Uses `node:https` rather than the global `fetch`: inside the Gramine SGX
51
+ * enclave undici (which backs global fetch) fails to establish outbound
52
+ * connections — the same failure mode as the enclave's dead Binance user-data
53
+ * WebSocket — while the ccxt HTTP path (node http/https) works. Using node's
54
+ * request keeps this on the transport that is proven to work in the enclave.
49
55
  */
50
56
  export declare function resolveOnChainSender(rpcUrl: string, txHash: string, timeoutMs?: number): Promise<string | null>;
51
57
  /** Binance rate-limit signals (-1003 / HTTP 429 / DDoS guard). */
@@ -78,6 +84,7 @@ export type ReconcileOutcome = {
78
84
  } | {
79
85
  kind: "unproven-origin";
80
86
  deposit: LocalEntityDeposit;
87
+ error?: string;
81
88
  } | {
82
89
  kind: "entity-drift";
83
90
  deposit: LocalEntityDeposit;
package/dist/index.js CHANGED
@@ -273224,6 +273224,10 @@ function authenticateRequest(call, whitelistIps) {
273224
273224
  }
273225
273225
  return true;
273226
273226
  }
273227
+ // src/helpers/travel-rule-deposit-reconciler.ts
273228
+ import { request as httpRequest } from "http";
273229
+ import { request as httpsRequest } from "https";
273230
+
273227
273231
  // src/helpers/deposit.ts
273228
273232
  function depositField(deposit, fields) {
273229
273233
  for (const field of fields) {
@@ -273304,36 +273308,54 @@ function parseLocalEntityDeposit(raw) {
273304
273308
  };
273305
273309
  }
273306
273310
  var EVM_TX_HASH = /^0x[0-9a-fA-F]{64}$/;
273307
- async function resolveOnChainSender(rpcUrl, txHash, timeoutMs = 1e4) {
273311
+ function resolveOnChainSender(rpcUrl, txHash, timeoutMs = 1e4) {
273308
273312
  if (!EVM_TX_HASH.test(txHash))
273309
- return null;
273310
- const controller = new AbortController;
273311
- const timeout2 = setTimeout(() => controller.abort(), timeoutMs);
273312
- let response;
273313
- try {
273314
- response = await fetch(rpcUrl, {
273313
+ return Promise.resolve(null);
273314
+ const body = JSON.stringify({
273315
+ jsonrpc: "2.0",
273316
+ id: 1,
273317
+ method: "eth_getTransactionByHash",
273318
+ params: [txHash]
273319
+ });
273320
+ const url2 = new URL(rpcUrl);
273321
+ const doRequest = url2.protocol === "http:" ? httpRequest : httpsRequest;
273322
+ return new Promise((resolve, reject) => {
273323
+ const req = doRequest(url2, {
273315
273324
  method: "POST",
273316
- headers: { "content-type": "application/json" },
273317
- signal: controller.signal,
273318
- body: JSON.stringify({
273319
- jsonrpc: "2.0",
273320
- id: 1,
273321
- method: "eth_getTransactionByHash",
273322
- params: [txHash]
273323
- })
273325
+ headers: {
273326
+ "content-type": "application/json",
273327
+ "content-length": Buffer.byteLength(body)
273328
+ },
273329
+ timeout: timeoutMs
273330
+ }, (res) => {
273331
+ const chunks = [];
273332
+ res.on("data", (chunk) => chunks.push(chunk));
273333
+ res.on("end", () => {
273334
+ const status = res.statusCode ?? 0;
273335
+ if (status < 200 || status >= 300) {
273336
+ reject(new Error(`travel_rule_rpc_http_${status}`));
273337
+ return;
273338
+ }
273339
+ try {
273340
+ const json3 = JSON.parse(Buffer.concat(chunks).toString("utf8"));
273341
+ if (json3.error) {
273342
+ reject(new Error(`travel_rule_rpc_error: ${JSON.stringify(json3.error)}`));
273343
+ return;
273344
+ }
273345
+ const from = json3.result?.from;
273346
+ resolve(typeof from === "string" && from.length > 0 ? from.toLowerCase() : null);
273347
+ } catch (parseError) {
273348
+ reject(new Error(`travel_rule_rpc_parse_error: ${String(parseError)}`));
273349
+ }
273350
+ });
273324
273351
  });
273325
- } finally {
273326
- clearTimeout(timeout2);
273327
- }
273328
- if (!response.ok) {
273329
- throw new Error(`travel_rule_rpc_http_${response.status}`);
273330
- }
273331
- const json3 = await response.json();
273332
- if (json3.error) {
273333
- throw new Error(`travel_rule_rpc_error: ${JSON.stringify(json3.error)}`);
273334
- }
273335
- const from = json3.result?.from;
273336
- return typeof from === "string" && from.length > 0 ? from.toLowerCase() : null;
273352
+ req.on("error", reject);
273353
+ req.on("timeout", () => {
273354
+ req.destroy(new Error("travel_rule_rpc_timeout"));
273355
+ });
273356
+ req.write(body);
273357
+ req.end();
273358
+ });
273337
273359
  }
273338
273360
  function errorText(error) {
273339
273361
  if (error instanceof Error)
@@ -273435,17 +273457,19 @@ async function reconcileAccountOnce(deps) {
273435
273457
  if (now3 < state.rateLimitedUntil)
273436
273458
  break;
273437
273459
  let sender = null;
273460
+ let resolveError;
273438
273461
  try {
273439
273462
  sender = await deps.resolveSender(deposit.network, deposit.txId);
273440
273463
  } catch (error) {
273441
273464
  sender = null;
273465
+ resolveError = errorText(error);
273442
273466
  if (isRateLimitError(error)) {
273443
273467
  state.rateLimitedUntil = now3 + deps.rateLimitCooldownMs;
273444
273468
  }
273445
273469
  }
273446
273470
  if (!sender) {
273447
273471
  state.backoffUntil.set(deposit.tranId, now3 + deps.failureBackoffMs);
273448
- outcomes.push({ kind: "unproven-origin", deposit });
273472
+ outcomes.push({ kind: "unproven-origin", deposit, error: resolveError });
273449
273473
  continue;
273450
273474
  }
273451
273475
  const questionnaire = deps.resolveQuestionnaire(deps.depositConfig, sender);
@@ -273701,7 +273725,8 @@ class TravelRuleDepositReconciler {
273701
273725
  tranId: outcome.deposit.tranId,
273702
273726
  coin: outcome.deposit.coin,
273703
273727
  network: outcome.deposit.network,
273704
- txId: outcome.deposit.txId
273728
+ txId: outcome.deposit.txId,
273729
+ rpcError: outcome.error ?? null
273705
273730
  });
273706
273731
  metrics?.recordCounter("travel_rule_deposit_anomalies_total", 1, {
273707
273732
  ...base2,
@@ -277319,11 +277344,11 @@ var JsonMetricsSerializer = {
277319
277344
  }
277320
277345
  };
277321
277346
  // node_modules/@opentelemetry/exporter-logs-otlp-http/build/esm/platform/node/OTLPLogExporter.js
277322
- var import_node_http = __toESM(require_index_node_http(), 1);
277347
+ var import_node_http2 = __toESM(require_index_node_http(), 1);
277323
277348
 
277324
277349
  class OTLPLogExporter extends import_otlp_exporter_base.OTLPExporterBase {
277325
277350
  constructor(config = {}) {
277326
- super(import_node_http.createOtlpHttpExportDelegate(import_node_http.convertLegacyHttpOptions(config, "LOGS", "v1/logs", {
277351
+ super(import_node_http2.createOtlpHttpExportDelegate(import_node_http2.convertLegacyHttpOptions(config, "LOGS", "v1/logs", {
277327
277352
  "Content-Type": "application/json"
277328
277353
  }), JsonLogsSerializer));
277329
277354
  }
@@ -277418,11 +277443,11 @@ class OTLPMetricExporterBase extends import_otlp_exporter_base2.OTLPExporterBase
277418
277443
  }
277419
277444
 
277420
277445
  // node_modules/@opentelemetry/exporter-metrics-otlp-http/build/esm/platform/node/OTLPMetricExporter.js
277421
- var import_node_http2 = __toESM(require_index_node_http(), 1);
277446
+ var import_node_http3 = __toESM(require_index_node_http(), 1);
277422
277447
 
277423
277448
  class OTLPMetricExporter extends OTLPMetricExporterBase {
277424
277449
  constructor(config) {
277425
- super(import_node_http2.createOtlpHttpExportDelegate(import_node_http2.convertLegacyHttpOptions(config ?? {}, "METRICS", "v1/metrics", {
277450
+ super(import_node_http3.createOtlpHttpExportDelegate(import_node_http3.convertLegacyHttpOptions(config ?? {}, "METRICS", "v1/metrics", {
277426
277451
  "Content-Type": "application/json"
277427
277452
  }), JsonMetricsSerializer), config);
277428
277453
  }
@@ -294953,4 +294978,4 @@ export {
294953
294978
  CEXBroker as default
294954
294979
  };
294955
294980
 
294956
- //# debugId=FEB8CDC1C50AA70B64756E2164756E21
294981
+ //# debugId=75EE3175FC68761464756E2164756E21