@lido-nestjs/execution 1.17.0 → 1.19.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,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var sleep = require('./sleep.js');
6
4
 
7
5
  const retrier = (logger, defaultMaxRetryCount = 3, defaultMinBackoffMs = 1000, defaultMaxBackoffMs = 60000, defaultLogWarning = false, defaultErrorFilter) => {
@@ -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,7 @@ 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 lazyEventEmitter = require('../common/lazy-event-emitter.js');
20
18
 
21
19
  exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extends providers.JsonRpcProvider {
22
20
  constructor(url, network, requestPolicy, fetchMiddlewares = []) {
@@ -24,14 +22,14 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
24
22
  this._batchAggregator = null;
25
23
  this._queue = new queue.Queue();
26
24
  this._tickCounter = 0;
27
- this._eventEmitter = new events.EventEmitter();
25
+ this._eventEmitter = new lazyEventEmitter.LazyEventEmitter();
28
26
  this._domain = networks.getConnectionFQDN(url);
29
27
  this._requestPolicy = requestPolicy !== null && requestPolicy !== void 0 ? requestPolicy : {
30
28
  jsonRpcMaxBatchSize: 200,
31
29
  maxConcurrentRequests: 5,
32
30
  batchAggregationWaitMs: 10,
33
31
  };
34
- this._concurrencyLimiter = promiseLimit["default"](this._requestPolicy.maxConcurrentRequests);
32
+ this._concurrencyLimiter = promiseLimit.default(this._requestPolicy.maxConcurrentRequests);
35
33
  this._fetchMiddlewareService = new middleware.MiddlewareService({
36
34
  middlewares: fetchMiddlewares,
37
35
  });
@@ -51,13 +49,12 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
51
49
  // if queue size is less then 'jsonRpcMaxBatchSize' - dequeue remaining elements
52
50
  const batch = this._queue.dequeueMultiple(this._requestPolicy.jsonRpcMaxBatchSize);
53
51
  const batchRequest = batch.map((intent) => intent.request);
54
- const event = {
52
+ this._eventEmitter.emitLazy('rpc', () => ({
55
53
  action: 'provider:request-batched',
56
54
  request: properties.deepCopy(batchRequest),
57
55
  provider: this,
58
56
  domain: this._domain,
59
- };
60
- this._eventEmitter.emit('rpc', event);
57
+ }));
61
58
  this._concurrencyLimiter(() => {
62
59
  return this._fetchMiddlewareService.go(() => this.fetchJson(this.connection, JSON.stringify(batchRequest)), {
63
60
  provider: this,
@@ -66,14 +63,12 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
66
63
  })
67
64
  .then((batchResult) => {
68
65
  var _a;
69
- const event = {
66
+ this._eventEmitter.emitLazy('rpc', () => ({
70
67
  action: 'provider:response-batched',
71
68
  request: properties.deepCopy(batchRequest),
72
- response: properties.deepCopy(batchResult),
73
69
  provider: this,
74
70
  domain: this._domain,
75
- };
76
- this._eventEmitter.emit('rpc', event);
71
+ }));
77
72
  if (!Array.isArray(batchResult)) {
78
73
  const errMessage = 'Unexpected batch result.';
79
74
  const jsonRpcErrorMessage = (_a = batchResult.error) === null || _a === void 0 ? void 0 : _a.message;
@@ -96,7 +91,11 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
96
91
  if (!payload) {
97
92
  const error = new fetch_error.FetchError(`Partial payload batch result. Response ${inflightRequest.request.id} not found`);
98
93
  error.code = errorCodes.ErrorCode.PARTIAL_BATCH_RESULT;
99
- error.data = batchResult;
94
+ error.data = {
95
+ requestedId: inflightRequest.request.id,
96
+ receivedIds: batchResult.map((r) => r.id),
97
+ batchSize: batchResult.length,
98
+ };
100
99
  inflightRequest.reject(error);
101
100
  }
102
101
  else if (payload.error) {
@@ -110,28 +109,26 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
110
109
  }
111
110
  });
112
111
  }, (error) => {
113
- const event = {
112
+ this._eventEmitter.emitLazy('rpc', () => ({
114
113
  action: 'provider:response-batched:error',
115
114
  error: error,
116
115
  request: properties.deepCopy(batchRequest),
117
116
  provider: this,
118
117
  domain: this._domain,
119
- };
120
- this._eventEmitter.emit('rpc', event);
118
+ }));
121
119
  batch.forEach((inflightRequest) => {
122
120
  inflightRequest.reject(error);
123
121
  });
124
122
  })
125
123
  .catch((error) => {
126
124
  // catch errors happening in the 'then' callback
127
- const event = {
125
+ this._eventEmitter.emitLazy('rpc', () => ({
128
126
  action: 'provider:response-batched:error',
129
127
  error: error,
130
128
  request: properties.deepCopy(batchRequest),
131
129
  provider: this,
132
130
  domain: this._domain,
133
- };
134
- this._eventEmitter.emit('rpc', event);
131
+ }));
135
132
  batch.forEach((inflightRequest) => {
136
133
  inflightRequest.reject(error);
137
134
  });
@@ -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');
@@ -17,7 +15,7 @@ var requestTimeout_error = require('../error/request-timeout.error.js');
17
15
  var transactionWaitTimeout_error = require('../error/transaction-wait-timeout.error.js');
18
16
  var feeHistory = require('../ethers/fee-history.js');
19
17
  var debugTraceBlockByHash = require('../ethers/debug-trace-block-by-hash.js');
20
- var events = require('events');
18
+ var lazyEventEmitter = require('../common/lazy-event-emitter.js');
21
19
 
22
20
  exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchProvider extends providers.BaseProvider {
23
21
  constructor(config, logger) {
@@ -28,7 +26,9 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
28
26
  // it is crucial not to mix these two errors
29
27
  this.lastPerformError = null; // last error for 'perform' operations, is batch-oriented
30
28
  this.lastError = null; // last error for whole provider
31
- this._eventEmitter = new events.EventEmitter();
29
+ this._childRpcListenersAttached = false;
30
+ this._eventEmitter = new lazyEventEmitter.LazyEventEmitter();
31
+ this._setupLazyChildListeners();
32
32
  this.config = Object.assign({ maxRetries: 3, minBackoffMs: 500, maxBackoffMs: 5000, logRetries: true, resetIntervalMs: 10000, maxTimeWithoutNewBlocksMs: 60000 }, config);
33
33
  this.logger = logger;
34
34
  const conns = config.urls.filter((url) => {
@@ -46,10 +46,6 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
46
46
  this.fallbackProviders = conns.map((conn, index) => {
47
47
  var _a;
48
48
  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
49
  return {
54
50
  network: null,
55
51
  provider,
@@ -166,8 +162,10 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
166
162
  ]);
167
163
  }
168
164
  async perform(method, params) {
165
+ var _a, _b;
169
166
  const retry = retrier.retrier(this.logger, this.config.maxRetries, this.config.minBackoffMs, this.config.maxBackoffMs, this.config.logRetries, (e) => this.isNonRetryableError(e));
170
167
  let attempt = 0;
168
+ (_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, this.formatLog(`RPC call: ${method}`));
171
169
  // will perform maximum `this.config.maxRetries` retries for fetching data with single provider
172
170
  // after failure will switch to next provider
173
171
  // maximum number of switching is limited to total fallback provider count
@@ -176,20 +174,19 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
176
174
  let performRetryAttempt = 0;
177
175
  attempt++;
178
176
  // Log which provider we're attempting to use
179
- this.logger.log(this.formatLog(`Attempting request (attempt ${attempt}/${this.fallbackProviders.length})`, this.activeFallbackProviderIndex));
177
+ this.logger.log(this.formatLog(`Attempting ${method} (attempt ${attempt}/${this.fallbackProviders.length})`, this.activeFallbackProviderIndex));
180
178
  // awaiting is extremely important here
181
179
  // without it, the error will not be caught in current try-catch scope
182
180
  const result = await retry(() => {
183
181
  const provider = this.provider;
184
- const event = {
182
+ this._eventEmitter.emitLazy('rpc', () => ({
185
183
  action: 'fallback-provider:request',
186
184
  provider: this,
187
185
  activeFallbackProviderIndex: this.activeFallbackProviderIndex,
188
186
  fallbackProvidersCount: this.fallbackProviders.length,
189
187
  domain: provider.provider.domain,
190
188
  retryAttempt: performRetryAttempt,
191
- };
192
- this._eventEmitter.emit('rpc', event);
189
+ }));
193
190
  performRetryAttempt++;
194
191
  const performPromise = provider.provider.perform(method, params);
195
192
  // Apply timeout if configured
@@ -199,33 +196,32 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
199
196
  return performPromise;
200
197
  });
201
198
  // Log successful request
202
- this.logger.log(this.formatLog(`Request successful after ${performRetryAttempt} retry attempt(s)`, this.activeFallbackProviderIndex));
199
+ this.logger.log(this.formatLog(`${method} successful after ${performRetryAttempt} retry attempt(s)`, this.activeFallbackProviderIndex));
203
200
  return result;
204
201
  }
205
202
  catch (e) {
206
203
  this.lastError = e;
207
204
  // checking that error should not be retried on another provider
208
205
  if (this.isNonRetryableError(e)) {
209
- const event = {
206
+ this._eventEmitter.emitLazy('rpc', () => ({
210
207
  action: 'fallback-provider:request:non-retryable-error',
211
208
  provider: this,
212
209
  error: e,
213
- };
214
- this._eventEmitter.emit('rpc', event);
210
+ }));
215
211
  // Log context (label + provider index) synchronously before error object
216
212
  // to ensure proper ordering in async logging systems
217
- this.logger.error(this.formatLog(`Non-retryable error occurred`, this.activeFallbackProviderIndex));
213
+ this.logger.error(this.formatLog(`${method} non-retryable error occurred`, this.activeFallbackProviderIndex));
218
214
  this.logger.error(e);
219
215
  throw e;
220
216
  }
221
217
  // Log context (label + provider index) synchronously before error object
222
218
  // to ensure proper ordering in async logging systems
223
219
  if (e instanceof requestTimeout_error.RequestTimeoutError) {
224
- this.logger.error(this.formatLog(`Request timeout after ${e.timeoutMs}ms. Will switch to next provider.`, this.activeFallbackProviderIndex));
220
+ this.logger.error(this.formatLog(`${method} timeout after ${e.timeoutMs}ms. Will switch to next provider.`, this.activeFallbackProviderIndex));
225
221
  this.logger.error(e);
226
222
  }
227
223
  else {
228
- this.logger.error(this.formatLog(`Error occurred. Will switch to next provider.`, this.activeFallbackProviderIndex));
224
+ this.logger.error(this.formatLog(`${method} error occurred. Will switch to next provider.`, this.activeFallbackProviderIndex));
229
225
  this.logger.error(e);
230
226
  }
231
227
  // This check is needed to avoid multiple `switchToNextProvider` calls when doing one JSON-RPC batch.
@@ -239,14 +235,13 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
239
235
  }
240
236
  }
241
237
  }
242
- const allProvidersFailedError = new allProvidersFailed_error.AllProvidersFailedError('All attempts to do ETH1 RPC request failed');
238
+ const allProvidersFailedError = new allProvidersFailed_error.AllProvidersFailedError(`All attempts to do ETH1 RPC request failed for ${method}`);
243
239
  allProvidersFailedError.cause = this.lastError;
244
- const event = {
240
+ this._eventEmitter.emitLazy('rpc', () => ({
245
241
  action: 'fallback-provider:request:failed:all',
246
242
  provider: this,
247
243
  error: allProvidersFailedError,
248
- };
249
- this._eventEmitter.emit('rpc', event);
244
+ }));
250
245
  throw allProvidersFailedError;
251
246
  }
252
247
  async detectNetwork() {
@@ -329,6 +324,29 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
329
324
  get eventEmitter() {
330
325
  return this._eventEmitter;
331
326
  }
327
+ /**
328
+ * Sets up lazy subscription to child provider events.
329
+ * Only attaches listeners to child providers when someone subscribes to the parent eventEmitter.
330
+ * This avoids unnecessary event processing when no one is listening.
331
+ */
332
+ _setupLazyChildListeners() {
333
+ this._eventEmitter.on('newListener', (eventName) => {
334
+ if (eventName === 'rpc' && !this._childRpcListenersAttached) {
335
+ this._childRpcListenersAttached = true;
336
+ this._attachChildRpcListeners();
337
+ }
338
+ });
339
+ }
340
+ /**
341
+ * Attaches listeners to all child providers to re-emit their events on the parent eventEmitter.
342
+ */
343
+ _attachChildRpcListeners() {
344
+ for (const fallbackProvider of this.fallbackProviders) {
345
+ fallbackProvider.provider.eventEmitter.on('rpc', (event) => {
346
+ this._eventEmitter.emit('rpc', event);
347
+ });
348
+ }
349
+ }
332
350
  /**
333
351
  * Waits for transaction confirmation with fallback provider support.
334
352
  *
@@ -351,19 +369,29 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
351
369
  try {
352
370
  // Uses perform() which handles fallback switching automatically
353
371
  const receipt = await this.getTransactionReceipt(txHash);
354
- if (receipt && receipt.confirmations >= confirmations) {
355
- const elapsedMs = Date.now() - startTime;
356
- this.logger.log(this.formatLog(`Transaction ${txHash} confirmed after ${pollCount} polls (${elapsedMs}ms, ${receipt.confirmations} confirmations)`));
357
- return { receipt, pollCount, elapsedMs };
372
+ if (!receipt) {
373
+ // Transaction pending in mempool, not yet included in a block
374
+ lastError = null;
375
+ await sleep.sleep(pollInterval);
376
+ continue;
377
+ }
378
+ if (receipt.confirmations < confirmations) {
379
+ // Transaction mined but waiting for more confirmations
380
+ lastError = null;
381
+ await sleep.sleep(pollInterval);
382
+ continue;
358
383
  }
359
- lastError = null;
384
+ // Transaction confirmed with enough confirmations
385
+ const elapsedMs = Date.now() - startTime;
386
+ this.logger.log(this.formatLog(`Transaction ${txHash} confirmed after ${pollCount} polls (${elapsedMs}ms, ${receipt.confirmations} confirmations)`));
387
+ return { receipt, pollCount, elapsedMs };
360
388
  }
361
389
  catch (error) {
362
390
  // All providers failed - log and retry until timeout
363
391
  lastError = error;
364
392
  this.logger.warn(this.formatLog(`waitForTransactionWithFallback poll #${pollCount} failed for ${txHash}: ${error}`));
393
+ await sleep.sleep(pollInterval);
365
394
  }
366
- await sleep.sleep(pollInterval);
367
395
  }
368
396
  const elapsedMs = Date.now() - startTime;
369
397
  const errorContext = lastError
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lido-nestjs/execution",
3
- "version": "1.17.0",
3
+ "version": "1.19.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "license": "MIT",