@lido-nestjs/execution 1.18.0 → 1.20.0

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.
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var tslib = require('tslib');
6
4
  var common = require('@nestjs/common');
7
5
  var extendedJsonRpcBatchProvider = require('./provider/extended-json-rpc-batch-provider.js');
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var logger = require('@ethersproject/logger');
6
4
 
7
5
  const nonRetryableErrors = [
@@ -0,0 +1,9 @@
1
+ /// <reference types="node" />
2
+ import { EventEmitter } from 'events';
3
+ /**
4
+ * EventEmitter with lazy event creation.
5
+ * The callback is only called if there are listeners for the event.
6
+ */
7
+ export declare class LazyEventEmitter extends EventEmitter {
8
+ emitLazy<T>(eventName: string, createEvent: () => T): boolean;
9
+ }
@@ -0,0 +1,18 @@
1
+ 'use strict';
2
+
3
+ var events = require('events');
4
+
5
+ /**
6
+ * EventEmitter with lazy event creation.
7
+ * The callback is only called if there are listeners for the event.
8
+ */
9
+ class LazyEventEmitter extends events.EventEmitter {
10
+ emitLazy(eventName, createEvent) {
11
+ if (this.listenerCount(eventName) > 0) {
12
+ return this.emit(eventName, createEvent());
13
+ }
14
+ return false;
15
+ }
16
+ }
17
+
18
+ exports.LazyEventEmitter = LazyEventEmitter;
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  const IP_V4_REGEX = new RegExp(/^(?<domain>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?::(?<port>\d+))?/i);
6
4
  const DOMAIN_REGEX = new RegExp(/^(?<protocol>https?:\/\/)(?=(?<fqdn>[^:/]+))(?:(?<service>www|ww\d|cdn|ftp|mail|pop\d?|ns\d?|git)\.)?(?:(?<subdomain>[^:/]+)\.)*(?<domain>[^:/]+\.[a-z0-9]+)(?::(?<port>\d+))?(?<path>\/[^?]*)?(?:\?(?<query>[^#]*))?(?:#(?<hash>.*))?/i);
7
5
  const networksEqual = (networkA, networkB) => {
@@ -5,6 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var queue = require('./queue.js');
6
6
 
7
7
  /* eslint-disable @typescript-eslint/ban-types */
8
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
9
  function pLimit(concurrency) {
9
10
  if (!((Number.isInteger(concurrency) ||
10
11
  concurrency === Number.POSITIVE_INFINITY) &&
@@ -62,4 +63,4 @@ function pLimit(concurrency) {
62
63
  return generator;
63
64
  }
64
65
 
65
- exports["default"] = pLimit;
66
+ exports.default = pLimit;
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  class Queue {
6
4
  constructor() {
7
5
  this._store = [];
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var sleep = require('./sleep.js');
4
+ var sanitizeError = require('./sanitize-error.js');
6
5
 
7
6
  const retrier = (logger, defaultMaxRetryCount = 3, defaultMinBackoffMs = 1000, defaultMaxBackoffMs = 60000, defaultLogWarning = false, defaultErrorFilter) => {
8
7
  return async (callback, maxRetryCount, minBackoffMs, maxBackoffMs, logWarning, errorFilter) => {
@@ -19,7 +18,7 @@ const retrier = (logger, defaultMaxRetryCount = 3, defaultMinBackoffMs = 1000, d
19
18
  throw err;
20
19
  }
21
20
  if (logger && logWarning) {
22
- logger.warn(err, `Retrying after (${minBackoffMs}ms). Remaining retries [${maxRetryCount}]`);
21
+ logger.warn(sanitizeError.sanitizeError(err), `Retrying after (${minBackoffMs}ms). Remaining retries [${maxRetryCount}]`);
23
22
  }
24
23
  if (maxRetryCount <= 1 || minBackoffMs >= maxBackoffMs) {
25
24
  throw err;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Returns a safe, bounded string representation of an error for logging.
3
+ * inspect() handles everything internally: circular references, throwing
4
+ * getters, frozen objects, huge nested data. No manual field extraction needed.
5
+ */
6
+ export declare function sanitizeError(error: unknown, maxLength?: number): string;
7
+ /**
8
+ * Truncates unknown data to a safe size.
9
+ * Use for FetchError.data assignment to prevent huge RPC payloads
10
+ * from being attached to error objects.
11
+ */
12
+ export declare function sanitizeErrorData(data: unknown, maxLength?: number): unknown;
13
+ /**
14
+ * Mutates the error object in place, truncating heavy fields.
15
+ * Preserves the original error type (instanceof checks still work).
16
+ * Use for error objects that are thrown to consumers (e.g. AllProvidersFailedError.cause).
17
+ */
18
+ export declare function sanitizeErrorInPlace(error: unknown, maxLength?: number,
19
+ /** @internal tracks visited objects to prevent circular-reference stack overflow */
20
+ _seen?: WeakSet<object>): void;
@@ -0,0 +1,113 @@
1
+ 'use strict';
2
+
3
+ var util = require('util');
4
+
5
+ const DEFAULT_MAX_LENGTH = 1000;
6
+ const INSPECT_OPTIONS = (maxLength) => ({
7
+ depth: 4,
8
+ maxStringLength: maxLength,
9
+ maxArrayLength: 20,
10
+ compact: true,
11
+ breakLength: Infinity,
12
+ });
13
+ /**
14
+ * Uses util.inspect to produce a bounded string representation.
15
+ * Never calls JSON.stringify (avoids V8 "RangeError: Invalid string length"
16
+ * on objects >512 MB). inspect() handles circular references, throwing
17
+ * getters, frozen objects, and huge data internally.
18
+ */
19
+ function truncate(value, maxLength) {
20
+ if (value === null || value === undefined)
21
+ return value;
22
+ if (typeof value === 'string') {
23
+ return value.length > maxLength
24
+ ? value.slice(0, maxLength) +
25
+ `...[truncated, total length: ${value.length}]`
26
+ : value;
27
+ }
28
+ if (typeof value !== 'object')
29
+ return value;
30
+ try {
31
+ const inspected = util.inspect(value, INSPECT_OPTIONS(maxLength));
32
+ if (inspected.length <= maxLength)
33
+ return value;
34
+ return (inspected.slice(0, maxLength) +
35
+ `...[truncated, total length: ${inspected.length}]`);
36
+ }
37
+ catch (_a) {
38
+ return '[unserializable]';
39
+ }
40
+ }
41
+ /**
42
+ * Returns a safe, bounded string representation of an error for logging.
43
+ * inspect() handles everything internally: circular references, throwing
44
+ * getters, frozen objects, huge nested data. No manual field extraction needed.
45
+ */
46
+ function sanitizeError(error, maxLength = DEFAULT_MAX_LENGTH) {
47
+ try {
48
+ const result = util.inspect(error, INSPECT_OPTIONS(maxLength));
49
+ if (result.length <= maxLength)
50
+ return result;
51
+ return (result.slice(0, maxLength) +
52
+ `...[truncated, total length: ${result.length}]`);
53
+ }
54
+ catch (_a) {
55
+ return '[unserializable error]';
56
+ }
57
+ }
58
+ /**
59
+ * Truncates unknown data to a safe size.
60
+ * Use for FetchError.data assignment to prevent huge RPC payloads
61
+ * from being attached to error objects.
62
+ */
63
+ function sanitizeErrorData(data, maxLength = DEFAULT_MAX_LENGTH) {
64
+ return truncate(data, maxLength);
65
+ }
66
+ const HEAVY_FIELDS = ['data', 'body', 'requestBody', 'serverError'];
67
+ /**
68
+ * Mutates the error object in place, truncating heavy fields.
69
+ * Preserves the original error type (instanceof checks still work).
70
+ * Use for error objects that are thrown to consumers (e.g. AllProvidersFailedError.cause).
71
+ */
72
+ function sanitizeErrorInPlace(error, maxLength = DEFAULT_MAX_LENGTH,
73
+ /** @internal tracks visited objects to prevent circular-reference stack overflow */
74
+ _seen) {
75
+ if (error === null || error === undefined || typeof error !== 'object') {
76
+ return;
77
+ }
78
+ const seen = _seen !== null && _seen !== void 0 ? _seen : new WeakSet();
79
+ if (seen.has(error))
80
+ return;
81
+ seen.add(error);
82
+ const err = error;
83
+ for (const field of HEAVY_FIELDS) {
84
+ try {
85
+ if (err[field] !== undefined) {
86
+ err[field] = truncate(err[field], maxLength);
87
+ }
88
+ }
89
+ catch (_a) {
90
+ // read-only property or throwing getter
91
+ }
92
+ }
93
+ try {
94
+ if (err.error && typeof err.error === 'object') {
95
+ sanitizeErrorInPlace(err.error, maxLength, seen);
96
+ }
97
+ }
98
+ catch (_b) {
99
+ // throwing getter on .error
100
+ }
101
+ try {
102
+ if (err.cause && typeof err.cause === 'object') {
103
+ sanitizeErrorInPlace(err.cause, maxLength, seen);
104
+ }
105
+ }
106
+ catch (_c) {
107
+ // throwing getter on .cause
108
+ }
109
+ }
110
+
111
+ exports.sanitizeError = sanitizeError;
112
+ exports.sanitizeErrorData = sanitizeErrorData;
113
+ exports.sanitizeErrorInPlace = sanitizeErrorInPlace;
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
6
4
 
7
5
  exports.sleep = sleep;
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  const FALLBACK_PROVIDER_MODULE_OPTIONS = Symbol('fallback-provider-module-options');
6
4
  const BATCH_PROVIDER_MODULE_OPTIONS = Symbol('batch-provider-module-options');
7
5
 
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  class AllProvidersFailedError extends Error {
6
4
  constructor(message) {
7
5
  super(message);
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  exports.ErrorCode = void 0;
6
4
  (function (ErrorCode) {
7
5
  ErrorCode["UNEXPECTED_BATCH_RESULT"] = "UNEXPECTED_BATCH_RESULT";
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  class FetchError extends Error {
6
4
  constructor(message) {
7
5
  super(message);
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  class NoNewBlocksWhilePollingError extends Error {
6
4
  constructor(message, latestObservedBlockNumber) {
7
5
  super(message);
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  class RequestTimeoutError extends Error {
6
4
  constructor(message, timeoutMs) {
7
5
  super(message);
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  /**
6
4
  * Thrown when waitForTransactionWithFallback times out.
7
5
  * lastError distinguishes network issues (present) from slow tx (null).
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  async function getDebugTraceBlockByHash(blockHash, traceConfig) {
6
4
  await this.getNetwork();
7
5
  return (await this.perform('getDebugTraceBlockByHash', {
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var bignumber = require('@ethersproject/bignumber');
6
4
  var bytes = require('@ethersproject/bytes');
7
5
  var formatBlockNumber = require('./format-block-number.js');
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var bytes = require('@ethersproject/bytes');
6
4
 
7
5
  const formatBlockNumber = (blockNumber) => {
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var providers = require('@ethersproject/providers');
6
4
 
7
5
  /* eslint-disable @typescript-eslint/no-explicit-any */
@@ -1,6 +1,6 @@
1
1
  import { SimpleFallbackJsonRpcBatchProvider } from '../provider/simple-fallback-json-rpc-batch-provider';
2
2
  import { AllProvidersFailedError } from '../error';
3
- import { ExtendedJsonRpcBatchProvider, JsonRpcRequest, JsonRpcResponse } from '../provider/extended-json-rpc-batch-provider';
3
+ import { ExtendedJsonRpcBatchProvider, JsonRpcRequest } from '../provider/extended-json-rpc-batch-provider';
4
4
  export declare type FallbackProviderRequestFailedAllEvent = {
5
5
  action: 'fallback-provider:request:failed:all';
6
6
  provider: SimpleFallbackJsonRpcBatchProvider;
@@ -29,7 +29,6 @@ export declare type ProviderResponseBatchedErrorEvent = {
29
29
  export declare type ProviderResponseBatchedEvent = {
30
30
  action: 'provider:response-batched';
31
31
  request: JsonRpcRequest[];
32
- response: JsonRpcResponse[] | JsonRpcResponse;
33
32
  provider: ExtendedJsonRpcBatchProvider;
34
33
  domain: string;
35
34
  };
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var tslib = require('tslib');
6
4
  var common = require('@nestjs/common');
7
5
  var extendedJsonRpcBatchProvider = require('./provider/extended-json-rpc-batch-provider.js');
package/dist/index.js CHANGED
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var extendedJsonRpcBatchProvider = require('./provider/extended-json-rpc-batch-provider.js');
6
4
  var simpleFallbackJsonRpcBatchProvider = require('./provider/simple-fallback-json-rpc-batch-provider.js');
7
5
  var queue = require('./common/queue.js');
@@ -16,20 +14,20 @@ var transactionWaitTimeout_error = require('./error/transaction-wait-timeout.err
16
14
 
17
15
 
18
16
 
19
- Object.defineProperty(exports, 'ExtendedJsonRpcBatchProvider', {
17
+ Object.defineProperty(exports, "ExtendedJsonRpcBatchProvider", {
20
18
  enumerable: true,
21
19
  get: function () { return extendedJsonRpcBatchProvider.ExtendedJsonRpcBatchProvider; }
22
20
  });
23
- Object.defineProperty(exports, 'SimpleFallbackJsonRpcBatchProvider', {
21
+ Object.defineProperty(exports, "SimpleFallbackJsonRpcBatchProvider", {
24
22
  enumerable: true,
25
23
  get: function () { return simpleFallbackJsonRpcBatchProvider.SimpleFallbackJsonRpcBatchProvider; }
26
24
  });
27
25
  exports.Queue = queue.Queue;
28
- Object.defineProperty(exports, 'FallbackProviderModule', {
26
+ Object.defineProperty(exports, "FallbackProviderModule", {
29
27
  enumerable: true,
30
28
  get: function () { return fallbackProvider_module.FallbackProviderModule; }
31
29
  });
32
- Object.defineProperty(exports, 'BatchProviderModule', {
30
+ Object.defineProperty(exports, "BatchProviderModule", {
33
31
  enumerable: true,
34
32
  get: function () { return batchProvider_module.BatchProviderModule; }
35
33
  });
@@ -10,8 +10,9 @@ import { BlockTag } from '../ethers/block-tag';
10
10
  import { MiddlewareCallback, MiddlewareService } from '@lido-nestjs/middleware';
11
11
  import { FeeHistory } from '../ethers/fee-history';
12
12
  import { TraceConfig, TraceResult } from '../interfaces/debug-traces';
13
+ import { LazyEventEmitter } from '../common/lazy-event-emitter';
13
14
  import { ProviderEvents } from '../events';
14
- export interface ExtendedJsonRpcBatchProviderEventEmitter extends NodeJS.EventEmitter {
15
+ export interface ExtendedJsonRpcBatchProviderEventEmitter extends LazyEventEmitter {
15
16
  on(eventName: 'rpc', listener: (event: ProviderEvents) => void): this;
16
17
  once(eventName: 'rpc', listener: (event: ProviderEvents) => void): this;
17
18
  addListener(eventName: 'rpc', listener: (event: ProviderEvents) => void): this;
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var tslib = require('tslib');
6
4
  var properties = require('@ethersproject/properties');
7
5
  var web = require('@ethersproject/web');
@@ -16,7 +14,8 @@ var feeHistory = require('../ethers/fee-history.js');
16
14
  var errorCodes = require('../error/codes/error-codes.js');
17
15
  var debugTraceBlockByHash = require('../ethers/debug-trace-block-by-hash.js');
18
16
  var networks = require('../common/networks.js');
19
- var events = require('events');
17
+ var sanitizeError = require('../common/sanitize-error.js');
18
+ var lazyEventEmitter = require('../common/lazy-event-emitter.js');
20
19
 
21
20
  exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extends providers.JsonRpcProvider {
22
21
  constructor(url, network, requestPolicy, fetchMiddlewares = []) {
@@ -24,14 +23,14 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
24
23
  this._batchAggregator = null;
25
24
  this._queue = new queue.Queue();
26
25
  this._tickCounter = 0;
27
- this._eventEmitter = new events.EventEmitter();
26
+ this._eventEmitter = new lazyEventEmitter.LazyEventEmitter();
28
27
  this._domain = networks.getConnectionFQDN(url);
29
28
  this._requestPolicy = requestPolicy !== null && requestPolicy !== void 0 ? requestPolicy : {
30
29
  jsonRpcMaxBatchSize: 200,
31
30
  maxConcurrentRequests: 5,
32
31
  batchAggregationWaitMs: 10,
33
32
  };
34
- this._concurrencyLimiter = promiseLimit["default"](this._requestPolicy.maxConcurrentRequests);
33
+ this._concurrencyLimiter = promiseLimit.default(this._requestPolicy.maxConcurrentRequests);
35
34
  this._fetchMiddlewareService = new middleware.MiddlewareService({
36
35
  middlewares: fetchMiddlewares,
37
36
  });
@@ -51,13 +50,12 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
51
50
  // if queue size is less then 'jsonRpcMaxBatchSize' - dequeue remaining elements
52
51
  const batch = this._queue.dequeueMultiple(this._requestPolicy.jsonRpcMaxBatchSize);
53
52
  const batchRequest = batch.map((intent) => intent.request);
54
- const event = {
53
+ this._eventEmitter.emitLazy('rpc', () => ({
55
54
  action: 'provider:request-batched',
56
55
  request: properties.deepCopy(batchRequest),
57
56
  provider: this,
58
57
  domain: this._domain,
59
- };
60
- this._eventEmitter.emit('rpc', event);
58
+ }));
61
59
  this._concurrencyLimiter(() => {
62
60
  return this._fetchMiddlewareService.go(() => this.fetchJson(this.connection, JSON.stringify(batchRequest)), {
63
61
  provider: this,
@@ -66,14 +64,12 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
66
64
  })
67
65
  .then((batchResult) => {
68
66
  var _a;
69
- const event = {
67
+ this._eventEmitter.emitLazy('rpc', () => ({
70
68
  action: 'provider:response-batched',
71
69
  request: properties.deepCopy(batchRequest),
72
- response: properties.deepCopy(batchResult),
73
70
  provider: this,
74
71
  domain: this._domain,
75
- };
76
- this._eventEmitter.emit('rpc', event);
72
+ }));
77
73
  if (!Array.isArray(batchResult)) {
78
74
  const errMessage = 'Unexpected batch result.';
79
75
  const jsonRpcErrorMessage = (_a = batchResult.error) === null || _a === void 0 ? void 0 : _a.message;
@@ -82,7 +78,7 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
82
78
  : '';
83
79
  const error = new fetch_error.FetchError(errMessage + detailedMessage);
84
80
  error.code = errorCodes.ErrorCode.UNEXPECTED_BATCH_RESULT;
85
- error.data = batchResult.error;
81
+ error.data = sanitizeError.sanitizeErrorData(batchResult.error);
86
82
  throw error;
87
83
  }
88
84
  const resultMap = batchResult.reduce((resultMap, payload) => {
@@ -96,13 +92,17 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
96
92
  if (!payload) {
97
93
  const error = new fetch_error.FetchError(`Partial payload batch result. Response ${inflightRequest.request.id} not found`);
98
94
  error.code = errorCodes.ErrorCode.PARTIAL_BATCH_RESULT;
99
- error.data = batchResult;
95
+ error.data = {
96
+ requestedId: inflightRequest.request.id,
97
+ receivedIds: batchResult.map((r) => r.id),
98
+ batchSize: batchResult.length,
99
+ };
100
100
  inflightRequest.reject(error);
101
101
  }
102
102
  else if (payload.error) {
103
103
  const error = new fetch_error.FetchError(payload.error.message);
104
104
  error.code = payload.error.code;
105
- error.data = payload.error.data;
105
+ error.data = sanitizeError.sanitizeErrorData(payload.error.data);
106
106
  inflightRequest.reject(error);
107
107
  }
108
108
  else {
@@ -110,28 +110,26 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
110
110
  }
111
111
  });
112
112
  }, (error) => {
113
- const event = {
113
+ this._eventEmitter.emitLazy('rpc', () => ({
114
114
  action: 'provider:response-batched:error',
115
115
  error: error,
116
116
  request: properties.deepCopy(batchRequest),
117
117
  provider: this,
118
118
  domain: this._domain,
119
- };
120
- this._eventEmitter.emit('rpc', event);
119
+ }));
121
120
  batch.forEach((inflightRequest) => {
122
121
  inflightRequest.reject(error);
123
122
  });
124
123
  })
125
124
  .catch((error) => {
126
125
  // catch errors happening in the 'then' callback
127
- const event = {
126
+ this._eventEmitter.emitLazy('rpc', () => ({
128
127
  action: 'provider:response-batched:error',
129
128
  error: error,
130
129
  request: properties.deepCopy(batchRequest),
131
130
  provider: this,
132
131
  domain: this._domain,
133
- };
134
- this._eventEmitter.emit('rpc', event);
132
+ }));
135
133
  batch.forEach((inflightRequest) => {
136
134
  inflightRequest.reject(error);
137
135
  });
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { BaseProvider, Formatter, TransactionRequest } from '@ethersproject/providers';
3
2
  import { CallOverrides as CallOverridesSource } from '@ethersproject/contracts';
4
3
  import { SimpleFallbackProviderConfig } from '../interfaces/simple-fallback-provider-config';
@@ -12,6 +11,7 @@ import { EventType, Listener } from '@ethersproject/abstract-provider';
12
11
  import { FeeHistory } from '../ethers/fee-history';
13
12
  import { WaitForTransactionOptions, WaitForTransactionResult } from '../interfaces/wait-for-transaction';
14
13
  import { TraceConfig, TraceResult } from '../interfaces/debug-traces';
14
+ import { LazyEventEmitter } from '../common/lazy-event-emitter';
15
15
  import { FallbackProviderEvents } from '../events';
16
16
  /**
17
17
  * EIP-1898 support
@@ -29,7 +29,7 @@ declare module '@ethersproject/providers' {
29
29
  blockTag?: BlockTag;
30
30
  }
31
31
  }
32
- export interface SimpleFallbackJsonRpcBatchProviderEventEmitter extends NodeJS.EventEmitter {
32
+ export interface SimpleFallbackJsonRpcBatchProviderEventEmitter extends LazyEventEmitter {
33
33
  on(eventName: 'rpc', listener: (event: FallbackProviderEvents) => void): this;
34
34
  once(eventName: 'rpc', listener: (event: FallbackProviderEvents) => void): this;
35
35
  addListener(eventName: 'rpc', listener: (event: FallbackProviderEvents) => void): this;
@@ -44,6 +44,7 @@ export declare class SimpleFallbackJsonRpcBatchProvider extends BaseProvider {
44
44
  protected lastPerformError: Error | null | unknown;
45
45
  protected lastError: Error | null | unknown;
46
46
  protected _eventEmitter: SimpleFallbackJsonRpcBatchProviderEventEmitter;
47
+ protected _childRpcListenersAttached: boolean;
47
48
  constructor(config: SimpleFallbackProviderConfig, logger: LoggerService);
48
49
  static _formatter: Formatter | null;
49
50
  static getFormatter(): Formatter;
@@ -63,6 +64,16 @@ export declare class SimpleFallbackJsonRpcBatchProvider extends BaseProvider {
63
64
  protected networksEqual(networkA: Network, networkB: Network): boolean;
64
65
  get activeProviderIndex(): number;
65
66
  get eventEmitter(): SimpleFallbackJsonRpcBatchProviderEventEmitter;
67
+ /**
68
+ * Sets up lazy subscription to child provider events.
69
+ * Only attaches listeners to child providers when someone subscribes to the parent eventEmitter.
70
+ * This avoids unnecessary event processing when no one is listening.
71
+ */
72
+ protected _setupLazyChildListeners(): void;
73
+ /**
74
+ * Attaches listeners to all child providers to re-emit their events on the parent eventEmitter.
75
+ */
76
+ protected _attachChildRpcListeners(): void;
66
77
  /**
67
78
  * Waits for transaction confirmation with fallback provider support.
68
79
  *
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var tslib = require('tslib');
6
4
  var providers = require('@ethersproject/providers');
7
5
  var extendedJsonRpcBatchProvider = require('./extended-json-rpc-batch-provider.js');
@@ -14,10 +12,11 @@ var noNewBlocksWhilePolling_error = require('../error/no-new-blocks-while-pollin
14
12
  var errors = require('../common/errors.js');
15
13
  var allProvidersFailed_error = require('../error/all-providers-failed.error.js');
16
14
  var requestTimeout_error = require('../error/request-timeout.error.js');
15
+ var sanitizeError = require('../common/sanitize-error.js');
17
16
  var transactionWaitTimeout_error = require('../error/transaction-wait-timeout.error.js');
18
17
  var feeHistory = require('../ethers/fee-history.js');
19
18
  var debugTraceBlockByHash = require('../ethers/debug-trace-block-by-hash.js');
20
- var events = require('events');
19
+ var lazyEventEmitter = require('../common/lazy-event-emitter.js');
21
20
 
22
21
  exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchProvider extends providers.BaseProvider {
23
22
  constructor(config, logger) {
@@ -28,7 +27,9 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
28
27
  // it is crucial not to mix these two errors
29
28
  this.lastPerformError = null; // last error for 'perform' operations, is batch-oriented
30
29
  this.lastError = null; // last error for whole provider
31
- this._eventEmitter = new events.EventEmitter();
30
+ this._childRpcListenersAttached = false;
31
+ this._eventEmitter = new lazyEventEmitter.LazyEventEmitter();
32
+ this._setupLazyChildListeners();
32
33
  this.config = Object.assign({ maxRetries: 3, minBackoffMs: 500, maxBackoffMs: 5000, logRetries: true, resetIntervalMs: 10000, maxTimeWithoutNewBlocksMs: 60000 }, config);
33
34
  this.logger = logger;
34
35
  const conns = config.urls.filter((url) => {
@@ -46,10 +47,6 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
46
47
  this.fallbackProviders = conns.map((conn, index) => {
47
48
  var _a;
48
49
  const provider = new extendedJsonRpcBatchProvider.ExtendedJsonRpcBatchProvider(conn, undefined, config.requestPolicy, (_a = config.fetchMiddlewares) !== null && _a !== void 0 ? _a : []);
49
- // re-emitting events from fallback-providers
50
- provider.eventEmitter.on('rpc', (event) => {
51
- this._eventEmitter.emit('rpc', event);
52
- });
53
50
  return {
54
51
  network: null,
55
52
  provider,
@@ -183,15 +180,14 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
183
180
  // without it, the error will not be caught in current try-catch scope
184
181
  const result = await retry(() => {
185
182
  const provider = this.provider;
186
- const event = {
183
+ this._eventEmitter.emitLazy('rpc', () => ({
187
184
  action: 'fallback-provider:request',
188
185
  provider: this,
189
186
  activeFallbackProviderIndex: this.activeFallbackProviderIndex,
190
187
  fallbackProvidersCount: this.fallbackProviders.length,
191
188
  domain: provider.provider.domain,
192
189
  retryAttempt: performRetryAttempt,
193
- };
194
- this._eventEmitter.emit('rpc', event);
190
+ }));
195
191
  performRetryAttempt++;
196
192
  const performPromise = provider.provider.perform(method, params);
197
193
  // Apply timeout if configured
@@ -208,27 +204,26 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
208
204
  this.lastError = e;
209
205
  // checking that error should not be retried on another provider
210
206
  if (this.isNonRetryableError(e)) {
211
- const event = {
207
+ this._eventEmitter.emitLazy('rpc', () => ({
212
208
  action: 'fallback-provider:request:non-retryable-error',
213
209
  provider: this,
214
210
  error: e,
215
- };
216
- this._eventEmitter.emit('rpc', event);
211
+ }));
217
212
  // Log context (label + provider index) synchronously before error object
218
213
  // to ensure proper ordering in async logging systems
219
214
  this.logger.error(this.formatLog(`${method} non-retryable error occurred`, this.activeFallbackProviderIndex));
220
- this.logger.error(e);
215
+ this.logger.error(sanitizeError.sanitizeError(e));
221
216
  throw e;
222
217
  }
223
218
  // Log context (label + provider index) synchronously before error object
224
219
  // to ensure proper ordering in async logging systems
225
220
  if (e instanceof requestTimeout_error.RequestTimeoutError) {
226
221
  this.logger.error(this.formatLog(`${method} timeout after ${e.timeoutMs}ms. Will switch to next provider.`, this.activeFallbackProviderIndex));
227
- this.logger.error(e);
222
+ this.logger.error(sanitizeError.sanitizeError(e));
228
223
  }
229
224
  else {
230
225
  this.logger.error(this.formatLog(`${method} error occurred. Will switch to next provider.`, this.activeFallbackProviderIndex));
231
- this.logger.error(e);
226
+ this.logger.error(sanitizeError.sanitizeError(e));
232
227
  }
233
228
  // This check is needed to avoid multiple `switchToNextProvider` calls when doing one JSON-RPC batch.
234
229
  // This can happen when multiple N calls to `perform` are batched in one JSON-RPC request and
@@ -242,13 +237,13 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
242
237
  }
243
238
  }
244
239
  const allProvidersFailedError = new allProvidersFailed_error.AllProvidersFailedError(`All attempts to do ETH1 RPC request failed for ${method}`);
240
+ sanitizeError.sanitizeErrorInPlace(this.lastError);
245
241
  allProvidersFailedError.cause = this.lastError;
246
- const event = {
242
+ this._eventEmitter.emitLazy('rpc', () => ({
247
243
  action: 'fallback-provider:request:failed:all',
248
244
  provider: this,
249
245
  error: allProvidersFailedError,
250
- };
251
- this._eventEmitter.emit('rpc', event);
246
+ }));
252
247
  throw allProvidersFailedError;
253
248
  }
254
249
  async detectNetwork() {
@@ -331,6 +326,29 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
331
326
  get eventEmitter() {
332
327
  return this._eventEmitter;
333
328
  }
329
+ /**
330
+ * Sets up lazy subscription to child provider events.
331
+ * Only attaches listeners to child providers when someone subscribes to the parent eventEmitter.
332
+ * This avoids unnecessary event processing when no one is listening.
333
+ */
334
+ _setupLazyChildListeners() {
335
+ this._eventEmitter.on('newListener', (eventName) => {
336
+ if (eventName === 'rpc' && !this._childRpcListenersAttached) {
337
+ this._childRpcListenersAttached = true;
338
+ this._attachChildRpcListeners();
339
+ }
340
+ });
341
+ }
342
+ /**
343
+ * Attaches listeners to all child providers to re-emit their events on the parent eventEmitter.
344
+ */
345
+ _attachChildRpcListeners() {
346
+ for (const fallbackProvider of this.fallbackProviders) {
347
+ fallbackProvider.provider.eventEmitter.on('rpc', (event) => {
348
+ this._eventEmitter.emit('rpc', event);
349
+ });
350
+ }
351
+ }
334
352
  /**
335
353
  * Waits for transaction confirmation with fallback provider support.
336
354
  *
@@ -373,7 +391,7 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
373
391
  catch (error) {
374
392
  // All providers failed - log and retry until timeout
375
393
  lastError = error;
376
- this.logger.warn(this.formatLog(`waitForTransactionWithFallback poll #${pollCount} failed for ${txHash}: ${error}`));
394
+ this.logger.warn(this.formatLog(`waitForTransactionWithFallback poll #${pollCount} failed for ${txHash}`), sanitizeError.sanitizeError(error));
377
395
  await sleep.sleep(pollInterval);
378
396
  }
379
397
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lido-nestjs/execution",
3
- "version": "1.18.0",
3
+ "version": "1.20.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "license": "MIT",