@lido-nestjs/execution 1.12.0 → 1.13.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,5 +1,7 @@
1
1
  import { Network } from '@ethersproject/networks';
2
2
  import { Networkish } from '../interfaces/networkish';
3
+ import { ConnectionInfo } from '@ethersproject/web';
3
4
  export declare const networksEqual: (networkA: Network, networkB: Network) => boolean;
4
5
  export declare const getNetworkChain: (networkish: Networkish) => number;
5
6
  export declare const networksChainsEqual: (networkA: Network, networkB: Networkish) => boolean;
7
+ export declare const getConnectionFQDN: (connectionInfo: ConnectionInfo | string) => string;
@@ -2,6 +2,8 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
+ const IP_V4_REGEX = new RegExp(/^(?<domain>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?::(?<port>\d+))?/i);
6
+ 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);
5
7
  const networksEqual = (networkA, networkB) => {
6
8
  return (networkA.name === networkB.name &&
7
9
  networkA.chainId === networkB.chainId &&
@@ -11,8 +13,21 @@ const networksEqual = (networkA, networkB) => {
11
13
  const getNetworkChain = (networkish) => typeof networkish === 'object' && networkish != null
12
14
  ? networkish.chainId
13
15
  : networkish;
14
- const networksChainsEqual = (networkA, networkB) => networkA.chainId === getNetworkChain(networkB);
16
+ const networksChainsEqual = (networkA, networkB) => networkA.chainId === getNetworkChain(networkB);
17
+ const getConnectionFQDN = (connectionInfo) => {
18
+ var _a, _b, _c, _d;
19
+ const urlLike = typeof connectionInfo === 'string' ? connectionInfo : connectionInfo.url;
20
+ const ipGroups = (_a = urlLike.match(IP_V4_REGEX)) === null || _a === void 0 ? void 0 : _a.groups;
21
+ if (ipGroups) {
22
+ /* istanbul ignore next */
23
+ return (_b = ipGroups.domain) !== null && _b !== void 0 ? _b : '';
24
+ }
25
+ const groups = (_c = urlLike.match(DOMAIN_REGEX)) === null || _c === void 0 ? void 0 : _c.groups;
26
+ /* istanbul ignore next */
27
+ return (_d = groups === null || groups === void 0 ? void 0 : groups.fqdn) !== null && _d !== void 0 ? _d : '';
28
+ };
15
29
 
30
+ exports.getConnectionFQDN = getConnectionFQDN;
16
31
  exports.getNetworkChain = getNetworkChain;
17
32
  exports.networksChainsEqual = networksChainsEqual;
18
33
  exports.networksEqual = networksEqual;
@@ -0,0 +1,43 @@
1
+ import { SimpleFallbackJsonRpcBatchProvider } from '../provider/simple-fallback-json-rpc-batch-provider';
2
+ import { AllProvidersFailedError } from '../error';
3
+ import { ExtendedJsonRpcBatchProvider, JsonRpcRequest, JsonRpcResponse } from '../provider/extended-json-rpc-batch-provider';
4
+ export declare type FallbackProviderRequestFailedAllEvent = {
5
+ action: 'fallback-provider:request:failed:all';
6
+ provider: SimpleFallbackJsonRpcBatchProvider;
7
+ error: AllProvidersFailedError;
8
+ };
9
+ export declare type FallbackProviderRequestNonRetryableErrorEvent = {
10
+ action: 'fallback-provider:request:non-retryable-error';
11
+ provider: SimpleFallbackJsonRpcBatchProvider;
12
+ error: Error | unknown;
13
+ };
14
+ export declare type FallbackProviderRequestEvent = {
15
+ action: 'fallback-provider:request';
16
+ provider: SimpleFallbackJsonRpcBatchProvider;
17
+ activeFallbackProviderIndex: number;
18
+ fallbackProvidersCount: number;
19
+ domain: string;
20
+ retryAttempt: number;
21
+ };
22
+ export declare type ProviderResponseBatchedErrorEvent = {
23
+ action: 'provider:response-batched:error';
24
+ error: Error;
25
+ request: JsonRpcRequest[];
26
+ provider: ExtendedJsonRpcBatchProvider;
27
+ domain: string;
28
+ };
29
+ export declare type ProviderResponseBatchedEvent = {
30
+ action: 'provider:response-batched';
31
+ request: JsonRpcRequest[];
32
+ response: JsonRpcResponse[] | JsonRpcResponse;
33
+ provider: ExtendedJsonRpcBatchProvider;
34
+ domain: string;
35
+ };
36
+ export declare type ProviderRequestBatchedEvent = {
37
+ action: 'provider:request-batched';
38
+ request: JsonRpcRequest[];
39
+ provider: ExtendedJsonRpcBatchProvider;
40
+ domain: string;
41
+ };
42
+ export declare type ProviderEvents = ProviderRequestBatchedEvent | ProviderResponseBatchedEvent | ProviderResponseBatchedErrorEvent;
43
+ export declare type FallbackProviderEvents = ProviderEvents | FallbackProviderRequestEvent | FallbackProviderRequestNonRetryableErrorEvent | FallbackProviderRequestFailedAllEvent;
package/dist/index.d.ts CHANGED
@@ -9,3 +9,4 @@ export * from './interfaces/module.options';
9
9
  export * from './interfaces/non-empty-array';
10
10
  export * from './ethers/fee-history';
11
11
  export * from './error';
12
+ export * from './events';
@@ -11,6 +11,12 @@ import { TransactionRequest } from '@ethersproject/abstract-provider/src.ts/inde
11
11
  import { MiddlewareCallback, MiddlewareService } from '@lido-nestjs/middleware';
12
12
  import { FeeHistory } from '../ethers/fee-history';
13
13
  import { TraceConfig, TraceResult } from '../interfaces/debug-traces';
14
+ import { ProviderEvents } from '../events';
15
+ export interface ExtendedJsonRpcBatchProviderEventEmitter extends NodeJS.EventEmitter {
16
+ on(eventName: 'rpc', listener: (event: ProviderEvents) => void): this;
17
+ once(eventName: 'rpc', listener: (event: ProviderEvents) => void): this;
18
+ addListener(eventName: 'rpc', listener: (event: ProviderEvents) => void): this;
19
+ }
14
20
  export interface RequestPolicy {
15
21
  jsonRpcMaxBatchSize: number;
16
22
  maxConcurrentRequests: number;
@@ -67,6 +73,8 @@ export declare class ExtendedJsonRpcBatchProvider extends JsonRpcProvider {
67
73
  protected _concurrencyLimiter: LimitFunction;
68
74
  protected _tickCounter: number;
69
75
  protected _fetchMiddlewareService: MiddlewareService<Promise<any>>;
76
+ protected _domain: string;
77
+ protected _eventEmitter: ExtendedJsonRpcBatchProviderEventEmitter;
70
78
  constructor(url: ConnectionInfo | string, network?: Networkish, requestPolicy?: RequestPolicy, fetchMiddlewares?: MiddlewareCallback<Promise<any>>[]);
71
79
  static _formatter: Formatter | null;
72
80
  static getFormatter(): Formatter;
@@ -76,6 +84,8 @@ export declare class ExtendedJsonRpcBatchProvider extends JsonRpcProvider {
76
84
  getDebugTraceBlockByHash(blockHash: string, traceConfig: TraceConfig): Promise<TraceResult[]>;
77
85
  prepareRequest(method: string, params: any): [string, Array<any>];
78
86
  use(callback: MiddlewareCallback<Promise<any>>): void;
87
+ get domain(): string;
88
+ get eventEmitter(): ExtendedJsonRpcBatchProviderEventEmitter;
79
89
  send(method: string, params: Array<unknown>): Promise<unknown>;
80
90
  detectNetwork(): Promise<Network>;
81
91
  protected fetchJson(connection: string | ConnectionInfo, json?: string, processFunc?: (value: any, response: FetchJsonResponse) => any): Promise<any>;
@@ -15,6 +15,8 @@ var middleware = require('@lido-nestjs/middleware');
15
15
  var feeHistory = require('../ethers/fee-history.js');
16
16
  var errorCodes = require('../error/codes/error-codes.js');
17
17
  var debugTraceBlockByHash = require('../ethers/debug-trace-block-by-hash.js');
18
+ var networks = require('../common/networks.js');
19
+ var events = require('events');
18
20
 
19
21
  exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extends providers.JsonRpcProvider {
20
22
  constructor(url, network, requestPolicy, fetchMiddlewares = []) {
@@ -22,6 +24,8 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
22
24
  this._batchAggregator = null;
23
25
  this._queue = new queue.Queue();
24
26
  this._tickCounter = 0;
27
+ this._eventEmitter = new events.EventEmitter();
28
+ this._domain = networks.getConnectionFQDN(url);
25
29
  this._requestPolicy = requestPolicy !== null && requestPolicy !== void 0 ? requestPolicy : {
26
30
  jsonRpcMaxBatchSize: 200,
27
31
  maxConcurrentRequests: 5,
@@ -47,24 +51,29 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
47
51
  // if queue size is less then 'jsonRpcMaxBatchSize' - dequeue remaining elements
48
52
  const batch = this._queue.dequeueMultiple(this._requestPolicy.jsonRpcMaxBatchSize);
49
53
  const batchRequest = batch.map((intent) => intent.request);
50
- this.emit('debug', {
51
- action: 'requestBatch',
54
+ const event = {
55
+ action: 'provider:request-batched',
52
56
  request: properties.deepCopy(batchRequest),
53
57
  provider: this,
54
- });
58
+ domain: this._domain,
59
+ };
60
+ this._eventEmitter.emit('rpc', event);
55
61
  this._concurrencyLimiter(() => {
56
62
  return this._fetchMiddlewareService.go(() => this.fetchJson(this.connection, JSON.stringify(batchRequest)), {
57
63
  provider: this,
64
+ domain: this._domain,
58
65
  });
59
66
  })
60
67
  .then((batchResult) => {
61
68
  var _a;
62
- this.emit('debug', {
63
- action: 'response',
69
+ const event = {
70
+ action: 'provider:response-batched',
64
71
  request: properties.deepCopy(batchRequest),
65
72
  response: properties.deepCopy(batchResult),
66
73
  provider: this,
67
- });
74
+ domain: this._domain,
75
+ };
76
+ this._eventEmitter.emit('rpc', event);
68
77
  if (!Array.isArray(batchResult)) {
69
78
  const errMessage = 'Unexpected batch result.';
70
79
  const jsonRpcErrorMessage = (_a = batchResult.error) === null || _a === void 0 ? void 0 : _a.message;
@@ -101,24 +110,28 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
101
110
  }
102
111
  });
103
112
  }, (error) => {
104
- this.emit('debug', {
105
- action: 'response',
113
+ const event = {
114
+ action: 'provider:response-batched:error',
106
115
  error: error,
107
116
  request: properties.deepCopy(batchRequest),
108
117
  provider: this,
109
- });
118
+ domain: this._domain,
119
+ };
120
+ this._eventEmitter.emit('rpc', event);
110
121
  batch.forEach((inflightRequest) => {
111
122
  inflightRequest.reject(error);
112
123
  });
113
124
  })
114
125
  .catch((error) => {
115
126
  // catch errors happening in the 'then' callback
116
- this.emit('debug', {
117
- action: 'response',
127
+ const event = {
128
+ action: 'provider:response-batched:error',
118
129
  error: error,
119
130
  request: properties.deepCopy(batchRequest),
120
131
  provider: this,
121
- });
132
+ domain: this._domain,
133
+ };
134
+ this._eventEmitter.emit('rpc', event);
122
135
  batch.forEach((inflightRequest) => {
123
136
  inflightRequest.reject(error);
124
137
  });
@@ -163,6 +176,12 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
163
176
  use(callback) {
164
177
  this._fetchMiddlewareService.use(callback);
165
178
  }
179
+ get domain() {
180
+ return this._domain;
181
+ }
182
+ get eventEmitter() {
183
+ return this._eventEmitter;
184
+ }
166
185
  send(method, params) {
167
186
  const request = {
168
187
  method: method,
@@ -1,3 +1,4 @@
1
+ /// <reference types="node" />
1
2
  import { BaseProvider, Formatter } from '@ethersproject/providers';
2
3
  import { CallOverrides as CallOverridesSource } from '@ethersproject/contracts';
3
4
  import { SimpleFallbackProviderConfig } from '../interfaces/simple-fallback-provider-config';
@@ -11,6 +12,7 @@ import { TransactionRequest } from '@ethersproject/abstract-provider/src.ts/inde
11
12
  import { EventType, Listener } from '@ethersproject/abstract-provider';
12
13
  import { FeeHistory } from '../ethers/fee-history';
13
14
  import { TraceConfig, TraceResult } from '../interfaces/debug-traces';
15
+ import { FallbackProviderEvents } from '../events';
14
16
  /**
15
17
  * EIP-1898 support
16
18
  * https://eips.ethereum.org/EIPS/eip-1898
@@ -27,6 +29,11 @@ declare module '@ethersproject/providers' {
27
29
  blockTag?: BlockTag;
28
30
  }
29
31
  }
32
+ export interface SimpleFallbackJsonRpcBatchProviderEventEmitter extends NodeJS.EventEmitter {
33
+ on(eventName: 'rpc', listener: (event: FallbackProviderEvents) => void): this;
34
+ once(eventName: 'rpc', listener: (event: FallbackProviderEvents) => void): this;
35
+ addListener(eventName: 'rpc', listener: (event: FallbackProviderEvents) => void): this;
36
+ }
30
37
  export declare class SimpleFallbackJsonRpcBatchProvider extends BaseProvider {
31
38
  protected config: SimpleFallbackProviderConfig;
32
39
  protected logger: LoggerService;
@@ -36,6 +43,7 @@ export declare class SimpleFallbackJsonRpcBatchProvider extends BaseProvider {
36
43
  protected resetTimer: ReturnType<typeof setTimeout> | null;
37
44
  protected lastPerformError: Error | null | unknown;
38
45
  protected lastError: Error | null | unknown;
46
+ protected _eventEmitter: SimpleFallbackJsonRpcBatchProviderEventEmitter;
39
47
  constructor(config: SimpleFallbackProviderConfig, logger: LoggerService);
40
48
  static _formatter: Formatter | null;
41
49
  static getFormatter(): Formatter;
@@ -52,4 +60,5 @@ export declare class SimpleFallbackJsonRpcBatchProvider extends BaseProvider {
52
60
  protected resetFallbacks(): void;
53
61
  protected networksEqual(networkA: Network, networkB: Network): boolean;
54
62
  get activeProviderIndex(): number;
63
+ get eventEmitter(): SimpleFallbackJsonRpcBatchProviderEventEmitter;
55
64
  }
@@ -14,6 +14,7 @@ var errors = require('../common/errors.js');
14
14
  var allProvidersFailed_error = require('../error/all-providers-failed.error.js');
15
15
  var feeHistory = require('../ethers/fee-history.js');
16
16
  var debugTraceBlockByHash = require('../ethers/debug-trace-block-by-hash.js');
17
+ var events = require('events');
17
18
 
18
19
  exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchProvider extends providers.BaseProvider {
19
20
  constructor(config, logger) {
@@ -23,6 +24,7 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
23
24
  // it is crucial not to mix these two errors
24
25
  this.lastPerformError = null; // last error for 'perform' operations, is batch-oriented
25
26
  this.lastError = null; // last error for whole provider
27
+ this._eventEmitter = new events.EventEmitter();
26
28
  this.config = Object.assign({ maxRetries: 3, minBackoffMs: 500, maxBackoffMs: 5000, logRetries: true, resetIntervalMs: 10000, maxTimeWithoutNewBlocksMs: 60000 }, config);
27
29
  this.logger = logger;
28
30
  const conns = config.urls.filter((url) => {
@@ -40,6 +42,10 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
40
42
  this.fallbackProviders = conns.map((conn, index) => {
41
43
  var _a;
42
44
  const provider = new extendedJsonRpcBatchProvider.ExtendedJsonRpcBatchProvider(conn, undefined, config.requestPolicy, (_a = config.fetchMiddlewares) !== null && _a !== void 0 ? _a : []);
45
+ // re-emitting events from fallback-providers
46
+ provider.eventEmitter.on('rpc', (event) => {
47
+ this._eventEmitter.emit('rpc', event);
48
+ });
43
49
  return {
44
50
  network: null,
45
51
  provider,
@@ -122,15 +128,35 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
122
128
  // maximum number of switching is limited to total fallback provider count
123
129
  while (attempt < this.fallbackProviders.length) {
124
130
  try {
131
+ let performRetryAttempt = 0;
125
132
  attempt++;
126
133
  // awaiting is extremely important here
127
134
  // without it, the error will not be caught in current try-catch scope
128
- return await retry(() => this.provider.provider.perform(method, params));
135
+ return await retry(() => {
136
+ const provider = this.provider;
137
+ const event = {
138
+ action: 'fallback-provider:request',
139
+ provider: this,
140
+ activeFallbackProviderIndex: this.activeFallbackProviderIndex,
141
+ fallbackProvidersCount: this.fallbackProviders.length,
142
+ domain: provider.provider.domain,
143
+ retryAttempt: performRetryAttempt,
144
+ };
145
+ this._eventEmitter.emit('rpc', event);
146
+ performRetryAttempt++;
147
+ return provider.provider.perform(method, params);
148
+ });
129
149
  }
130
150
  catch (e) {
131
151
  this.lastError = e;
132
152
  // checking that error should not be retried on another provider
133
153
  if (this.isNonRetryableError(e)) {
154
+ const event = {
155
+ action: 'fallback-provider:request:non-retryable-error',
156
+ provider: this,
157
+ error: e,
158
+ };
159
+ this._eventEmitter.emit('rpc', event);
134
160
  throw e;
135
161
  }
136
162
  this.logger.error('Error while doing ETH1 RPC request. Will try to switch to another provider');
@@ -148,6 +174,12 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
148
174
  }
149
175
  const allProvidersFailedError = new allProvidersFailed_error.AllProvidersFailedError('All attempts to do ETH1 RPC request failed');
150
176
  allProvidersFailedError.cause = this.lastError;
177
+ const event = {
178
+ action: 'fallback-provider:request:failed:all',
179
+ provider: this,
180
+ error: allProvidersFailedError,
181
+ };
182
+ this._eventEmitter.emit('rpc', event);
151
183
  throw allProvidersFailedError;
152
184
  }
153
185
  async detectNetwork() {
@@ -227,6 +259,9 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
227
259
  get activeProviderIndex() {
228
260
  return this.activeFallbackProviderIndex;
229
261
  }
262
+ get eventEmitter() {
263
+ return this._eventEmitter;
264
+ }
230
265
  };
231
266
  exports.SimpleFallbackJsonRpcBatchProvider._formatter = null;
232
267
  exports.SimpleFallbackJsonRpcBatchProvider = tslib.__decorate([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lido-nestjs/execution",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "license": "MIT",