@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.
- package/dist/batch-provider.module.js +0 -2
- package/dist/common/errors.js +0 -2
- package/dist/common/lazy-event-emitter.d.ts +9 -0
- package/dist/common/lazy-event-emitter.js +18 -0
- package/dist/common/networks.js +0 -2
- package/dist/common/promise-limit.js +2 -1
- package/dist/common/queue.js +0 -2
- package/dist/common/retrier.js +2 -3
- package/dist/common/sanitize-error.d.ts +20 -0
- package/dist/common/sanitize-error.js +113 -0
- package/dist/common/sleep.js +0 -2
- package/dist/constants/constants.js +0 -2
- package/dist/error/all-providers-failed.error.js +0 -2
- package/dist/error/codes/error-codes.js +0 -2
- package/dist/error/fetch.error.js +0 -2
- package/dist/error/no-new-blocks-while-polling.error.js +0 -2
- package/dist/error/request-timeout.error.js +0 -2
- package/dist/error/transaction-wait-timeout.error.js +0 -2
- package/dist/ethers/debug-trace-block-by-hash.js +0 -2
- package/dist/ethers/fee-history.js +0 -2
- package/dist/ethers/format-block-number.js +0 -2
- package/dist/ethers/formatter-with-eip1898.js +0 -2
- package/dist/events/index.d.ts +1 -2
- package/dist/fallback-provider.module.js +0 -2
- package/dist/index.js +4 -6
- package/dist/provider/extended-json-rpc-batch-provider.d.ts +2 -1
- package/dist/provider/extended-json-rpc-batch-provider.js +19 -21
- package/dist/provider/simple-fallback-json-rpc-batch-provider.d.ts +13 -2
- package/dist/provider/simple-fallback-json-rpc-batch-provider.js +39 -21
- package/package.json +1 -1
package/dist/common/errors.js
CHANGED
|
@@ -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;
|
package/dist/common/networks.js
CHANGED
|
@@ -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
|
|
66
|
+
exports.default = pLimit;
|
package/dist/common/queue.js
CHANGED
package/dist/common/retrier.js
CHANGED
|
@@ -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;
|
package/dist/common/sleep.js
CHANGED
package/dist/events/index.d.ts
CHANGED
|
@@ -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
|
|
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
|
};
|
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,
|
|
17
|
+
Object.defineProperty(exports, "ExtendedJsonRpcBatchProvider", {
|
|
20
18
|
enumerable: true,
|
|
21
19
|
get: function () { return extendedJsonRpcBatchProvider.ExtendedJsonRpcBatchProvider; }
|
|
22
20
|
});
|
|
23
|
-
Object.defineProperty(exports,
|
|
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,
|
|
26
|
+
Object.defineProperty(exports, "FallbackProviderModule", {
|
|
29
27
|
enumerable: true,
|
|
30
28
|
get: function () { return fallbackProvider_module.FallbackProviderModule; }
|
|
31
29
|
});
|
|
32
|
-
Object.defineProperty(exports,
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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}
|
|
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
|
}
|