@lido-nestjs/execution 1.10.0 → 1.11.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/common/errors.d.ts +14 -2
- package/dist/common/errors.js +23 -4
- package/dist/error/all-providers-failed.error.d.ts +2 -2
- package/dist/error/all-providers-failed.error.js +6 -2
- package/dist/error/codes/error-codes.d.ts +4 -0
- package/dist/error/codes/error-codes.js +9 -0
- package/dist/error/fetch.error.d.ts +1 -2
- package/dist/error/fetch.error.js +2 -2
- package/dist/error/index.d.ts +3 -0
- package/dist/error/no-new-blocks-while-polling.error.d.ts +0 -1
- package/dist/error/no-new-blocks-while-polling.error.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +6 -0
- package/dist/provider/extended-json-rpc-batch-provider.js +20 -1
- package/dist/provider/simple-fallback-json-rpc-batch-provider.d.ts +3 -1
- package/dist/provider/simple-fallback-json-rpc-batch-provider.js +19 -11
- package/package.json +2 -2
package/dist/common/errors.d.ts
CHANGED
|
@@ -2,8 +2,20 @@ export declare const nonRetryableErrors: (string | number)[];
|
|
|
2
2
|
export declare type ErrorWithCode = Error & {
|
|
3
3
|
code: number | string;
|
|
4
4
|
};
|
|
5
|
-
export declare type
|
|
5
|
+
export declare type HasErrorProperty = {
|
|
6
|
+
error: object;
|
|
7
|
+
};
|
|
8
|
+
export declare type HasServerErrorProperty = {
|
|
6
9
|
serverError: object;
|
|
7
10
|
};
|
|
8
11
|
export declare const isErrorHasCode: (error: unknown) => error is ErrorWithCode;
|
|
9
|
-
export declare const
|
|
12
|
+
export declare const hasErrorProperty: (error: unknown) => error is HasErrorProperty;
|
|
13
|
+
export declare const hasServerErrorProperty: (error: unknown) => error is HasServerErrorProperty;
|
|
14
|
+
/**
|
|
15
|
+
* Detect that ethers error is a server error.
|
|
16
|
+
*
|
|
17
|
+
* Note: Ethers v5 error management is not very clean,
|
|
18
|
+
* and ethers error can be nested.
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
export declare const isEthersServerError: (error: unknown) => boolean;
|
package/dist/common/errors.js
CHANGED
|
@@ -69,11 +69,30 @@ const isErrorHasCode = (error) => {
|
|
|
69
69
|
return (error instanceof Error &&
|
|
70
70
|
Object.prototype.hasOwnProperty.call(error, 'code'));
|
|
71
71
|
};
|
|
72
|
-
const
|
|
73
|
-
return
|
|
74
|
-
|
|
72
|
+
const hasErrorProperty = (error) => {
|
|
73
|
+
return Object.prototype.hasOwnProperty.call(error, 'error');
|
|
74
|
+
};
|
|
75
|
+
const hasServerErrorProperty = (error) => {
|
|
76
|
+
return Object.prototype.hasOwnProperty.call(error, 'serverError');
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Detect that ethers error is a server error.
|
|
80
|
+
*
|
|
81
|
+
* Note: Ethers v5 error management is not very clean,
|
|
82
|
+
* and ethers error can be nested.
|
|
83
|
+
*
|
|
84
|
+
*/
|
|
85
|
+
const isEthersServerError = (error) => {
|
|
86
|
+
return (hasServerErrorProperty(error) || // nesting level 0
|
|
87
|
+
(hasErrorProperty(error) && hasServerErrorProperty(error.error)) || // nesting level 1
|
|
88
|
+
(hasErrorProperty(error) &&
|
|
89
|
+
hasErrorProperty(error.error) &&
|
|
90
|
+
hasServerErrorProperty(error.error.error)) // nesting level 2
|
|
91
|
+
);
|
|
75
92
|
};
|
|
76
93
|
|
|
77
|
-
exports.
|
|
94
|
+
exports.hasErrorProperty = hasErrorProperty;
|
|
95
|
+
exports.hasServerErrorProperty = hasServerErrorProperty;
|
|
78
96
|
exports.isErrorHasCode = isErrorHasCode;
|
|
97
|
+
exports.isEthersServerError = isEthersServerError;
|
|
79
98
|
exports.nonRetryableErrors = nonRetryableErrors;
|
|
@@ -4,10 +4,14 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
class AllProvidersFailedError extends Error {
|
|
6
6
|
constructor(message) {
|
|
7
|
-
super(
|
|
7
|
+
super(message);
|
|
8
8
|
this.name = 'AllProvidersFailedError';
|
|
9
9
|
this.code = 0;
|
|
10
|
-
this.
|
|
10
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
11
|
+
}
|
|
12
|
+
// for backward-compatibility
|
|
13
|
+
get originalError() {
|
|
14
|
+
return this.cause;
|
|
11
15
|
}
|
|
12
16
|
}
|
|
13
17
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
exports.ErrorCode = void 0;
|
|
6
|
+
(function (ErrorCode) {
|
|
7
|
+
ErrorCode["UNEXPECTED_BATCH_RESULT"] = "UNEXPECTED_BATCH_RESULT";
|
|
8
|
+
ErrorCode["PARTIAL_BATCH_RESULT"] = "PARTIAL_BATCH_RESULT";
|
|
9
|
+
})(exports.ErrorCode || (exports.ErrorCode = {}));
|
|
@@ -4,10 +4,10 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
class FetchError extends Error {
|
|
6
6
|
constructor(message) {
|
|
7
|
-
super(
|
|
7
|
+
super(message);
|
|
8
8
|
this.name = 'FetchError';
|
|
9
9
|
this.code = 0;
|
|
10
|
-
this.
|
|
10
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
@@ -4,9 +4,9 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
class NoNewBlocksWhilePollingError extends Error {
|
|
6
6
|
constructor(message, latestObservedBlockNumber) {
|
|
7
|
-
super(
|
|
7
|
+
super(message);
|
|
8
8
|
this.name = 'NoNewBlocksWhilePollingError';
|
|
9
|
-
this.
|
|
9
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
10
10
|
this.latestObservedBlockNumber = latestObservedBlockNumber;
|
|
11
11
|
}
|
|
12
12
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -8,6 +8,9 @@ var queue = require('./common/queue.js');
|
|
|
8
8
|
var fallbackProvider_module = require('./fallback-provider.module.js');
|
|
9
9
|
var batchProvider_module = require('./batch-provider.module.js');
|
|
10
10
|
var feeHistory = require('./ethers/fee-history.js');
|
|
11
|
+
var allProvidersFailed_error = require('./error/all-providers-failed.error.js');
|
|
12
|
+
var fetch_error = require('./error/fetch.error.js');
|
|
13
|
+
var noNewBlocksWhilePolling_error = require('./error/no-new-blocks-while-polling.error.js');
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
|
|
@@ -31,3 +34,6 @@ Object.defineProperty(exports, 'BatchProviderModule', {
|
|
|
31
34
|
exports.MAX_BLOCKCOUNT = feeHistory.MAX_BLOCKCOUNT;
|
|
32
35
|
exports.MIN_BLOCKCOUNT = feeHistory.MIN_BLOCKCOUNT;
|
|
33
36
|
exports.getFeeHistory = feeHistory.getFeeHistory;
|
|
37
|
+
exports.AllProvidersFailedError = allProvidersFailed_error.AllProvidersFailedError;
|
|
38
|
+
exports.FetchError = fetch_error.FetchError;
|
|
39
|
+
exports.NoNewBlocksWhilePollingError = noNewBlocksWhilePolling_error.NoNewBlocksWhilePollingError;
|
|
@@ -13,6 +13,7 @@ var promiseLimit = require('../common/promise-limit.js');
|
|
|
13
13
|
var formatterWithEip1898 = require('../ethers/formatter-with-eip1898.js');
|
|
14
14
|
var middleware = require('@lido-nestjs/middleware');
|
|
15
15
|
var feeHistory = require('../ethers/fee-history.js');
|
|
16
|
+
var errorCodes = require('../error/codes/error-codes.js');
|
|
16
17
|
|
|
17
18
|
exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extends providers.JsonRpcProvider {
|
|
18
19
|
constructor(url, network, requestPolicy, fetchMiddlewares = []) {
|
|
@@ -56,12 +57,24 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
|
|
|
56
57
|
});
|
|
57
58
|
})
|
|
58
59
|
.then((batchResult) => {
|
|
60
|
+
var _a;
|
|
59
61
|
this.emit('debug', {
|
|
60
62
|
action: 'response',
|
|
61
63
|
request: properties.deepCopy(batchRequest),
|
|
62
64
|
response: properties.deepCopy(batchResult),
|
|
63
65
|
provider: this,
|
|
64
66
|
});
|
|
67
|
+
if (!Array.isArray(batchResult)) {
|
|
68
|
+
const errMessage = 'Unexpected batch result.';
|
|
69
|
+
const jsonRpcErrorMessage = (_a = batchResult.error) === null || _a === void 0 ? void 0 : _a.message;
|
|
70
|
+
const detailedMessage = jsonRpcErrorMessage
|
|
71
|
+
? ` Possible reason: "${jsonRpcErrorMessage}".`
|
|
72
|
+
: '';
|
|
73
|
+
const error = new fetch_error.FetchError(errMessage + detailedMessage);
|
|
74
|
+
error.code = errorCodes.ErrorCode.UNEXPECTED_BATCH_RESULT;
|
|
75
|
+
error.data = batchResult.error;
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
65
78
|
const resultMap = batchResult.reduce((resultMap, payload) => {
|
|
66
79
|
resultMap[payload.id] = payload;
|
|
67
80
|
return resultMap;
|
|
@@ -70,7 +83,13 @@ exports.ExtendedJsonRpcBatchProvider = class ExtendedJsonRpcBatchProvider extend
|
|
|
70
83
|
// on whether it was a success or error
|
|
71
84
|
batch.forEach((inflightRequest) => {
|
|
72
85
|
const payload = resultMap[inflightRequest.request.id];
|
|
73
|
-
if (payload
|
|
86
|
+
if (!payload) {
|
|
87
|
+
const error = new fetch_error.FetchError(`Partial payload batch result. Response ${inflightRequest.request.id} not found`);
|
|
88
|
+
error.code = errorCodes.ErrorCode.PARTIAL_BATCH_RESULT;
|
|
89
|
+
error.data = batchResult;
|
|
90
|
+
inflightRequest.reject(error);
|
|
91
|
+
}
|
|
92
|
+
else if (payload.error) {
|
|
74
93
|
const error = new fetch_error.FetchError(payload.error.message);
|
|
75
94
|
error.code = payload.error.code;
|
|
76
95
|
error.data = payload.error.data;
|
|
@@ -34,6 +34,7 @@ export declare class SimpleFallbackJsonRpcBatchProvider extends BaseProvider {
|
|
|
34
34
|
protected detectNetworkFirstRun: boolean;
|
|
35
35
|
protected resetTimer: ReturnType<typeof setTimeout> | null;
|
|
36
36
|
protected lastPerformError: Error | null | unknown;
|
|
37
|
+
protected lastError: Error | null | unknown;
|
|
37
38
|
constructor(config: SimpleFallbackProviderConfig, logger: LoggerService);
|
|
38
39
|
static _formatter: Formatter | null;
|
|
39
40
|
static getFormatter(): Formatter;
|
|
@@ -41,11 +42,12 @@ export declare class SimpleFallbackJsonRpcBatchProvider extends BaseProvider {
|
|
|
41
42
|
getFeeHistory(blockCount: number, newestBlock?: string | null | number, rewardPercentiles?: number[]): Promise<FeeHistory>;
|
|
42
43
|
protected get provider(): FallbackProvider;
|
|
43
44
|
protected switchToNextProvider(): void;
|
|
44
|
-
protected
|
|
45
|
+
protected isNonRetryableError(error: Error | unknown): boolean;
|
|
45
46
|
perform(method: string, params: {
|
|
46
47
|
[name: string]: unknown;
|
|
47
48
|
}): Promise<unknown>;
|
|
48
49
|
detectNetwork(): Promise<Network>;
|
|
49
50
|
protected resetFallbacks(): void;
|
|
50
51
|
protected networksEqual(networkA: Network, networkB: Network): boolean;
|
|
52
|
+
get activeProviderIndex(): number;
|
|
51
53
|
}
|
|
@@ -19,7 +19,9 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
19
19
|
super(config.network);
|
|
20
20
|
this.detectNetworkFirstRun = true;
|
|
21
21
|
this.resetTimer = null;
|
|
22
|
-
|
|
22
|
+
// it is crucial not to mix these two errors
|
|
23
|
+
this.lastPerformError = null; // last error for 'perform' operations, is batch-oriented
|
|
24
|
+
this.lastError = null; // last error for whole provider
|
|
23
25
|
this.config = Object.assign({ maxRetries: 3, minBackoffMs: 500, maxBackoffMs: 5000, logRetries: true, resetIntervalMs: 10000, maxTimeWithoutNewBlocksMs: 60000 }, config);
|
|
24
26
|
this.logger = logger;
|
|
25
27
|
const conns = config.urls.filter((url) => {
|
|
@@ -103,18 +105,17 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
103
105
|
this.activeFallbackProviderIndex++;
|
|
104
106
|
this.logger.log(`Switched to next provider for execution layer`);
|
|
105
107
|
}
|
|
106
|
-
|
|
107
|
-
return (errors.
|
|
108
|
-
errors.
|
|
109
|
-
|
|
108
|
+
isNonRetryableError(error) {
|
|
109
|
+
return (!errors.isEthersServerError(error) &&
|
|
110
|
+
errors.isErrorHasCode(error) &&
|
|
111
|
+
errors.nonRetryableErrors.includes(error.code));
|
|
110
112
|
}
|
|
111
113
|
async perform(method, params) {
|
|
112
|
-
const retry = retrier.retrier(this.logger, this.config.maxRetries, this.config.minBackoffMs, this.config.maxBackoffMs, this.config.logRetries, (e) => this.
|
|
114
|
+
const retry = retrier.retrier(this.logger, this.config.maxRetries, this.config.minBackoffMs, this.config.maxBackoffMs, this.config.logRetries, (e) => this.isNonRetryableError(e));
|
|
113
115
|
let attempt = 0;
|
|
114
116
|
// will perform maximum `this.config.maxRetries` retries for fetching data with single provider
|
|
115
117
|
// after failure will switch to next provider
|
|
116
118
|
// maximum number of switching is limited to total fallback provider count
|
|
117
|
-
let lastError;
|
|
118
119
|
while (attempt < this.fallbackProviders.length) {
|
|
119
120
|
try {
|
|
120
121
|
attempt++;
|
|
@@ -123,11 +124,12 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
123
124
|
return await retry(() => this.provider.provider.perform(method, params));
|
|
124
125
|
}
|
|
125
126
|
catch (e) {
|
|
126
|
-
|
|
127
|
+
this.lastError = e;
|
|
128
|
+
// checking that error should not be retried on another provider
|
|
129
|
+
if (this.isNonRetryableError(e)) {
|
|
127
130
|
throw e;
|
|
128
131
|
}
|
|
129
132
|
this.logger.error('Error while doing ETH1 RPC request. Will try to switch to another provider');
|
|
130
|
-
lastError = e;
|
|
131
133
|
this.logger.error(e);
|
|
132
134
|
// This check is needed to avoid multiple `switchToNextProvider` calls when doing one JSON-RPC batch.
|
|
133
135
|
// This can happen when multiple N calls to `perform` are batched in one JSON-RPC request and
|
|
@@ -141,7 +143,7 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
141
143
|
}
|
|
142
144
|
}
|
|
143
145
|
const allProvidersFailedError = new allProvidersFailed_error.AllProvidersFailedError('All attempts to do ETH1 RPC request failed');
|
|
144
|
-
allProvidersFailedError.
|
|
146
|
+
allProvidersFailedError.cause = this.lastError;
|
|
145
147
|
throw allProvidersFailedError;
|
|
146
148
|
}
|
|
147
149
|
async detectNetwork() {
|
|
@@ -156,6 +158,7 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
156
158
|
else {
|
|
157
159
|
this.fallbackProviders[index].network = null;
|
|
158
160
|
this.fallbackProviders[index].unreachable = true;
|
|
161
|
+
this.lastError = result.reason;
|
|
159
162
|
}
|
|
160
163
|
});
|
|
161
164
|
let previousNetwork = null;
|
|
@@ -187,7 +190,9 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
187
190
|
}
|
|
188
191
|
});
|
|
189
192
|
if (!previousNetwork) {
|
|
190
|
-
|
|
193
|
+
const error = new allProvidersFailed_error.AllProvidersFailedError('All fallback endpoints are unreachable or all fallback networks differ between each other');
|
|
194
|
+
error.cause = this.lastError;
|
|
195
|
+
throw error;
|
|
191
196
|
}
|
|
192
197
|
if (this.detectNetworkFirstRun) {
|
|
193
198
|
this.detectNetworkFirstRun = false;
|
|
@@ -215,6 +220,9 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
215
220
|
networksEqual(networkA, networkB) {
|
|
216
221
|
return networks.networksEqual(networkA, networkB);
|
|
217
222
|
}
|
|
223
|
+
get activeProviderIndex() {
|
|
224
|
+
return this.activeFallbackProviderIndex;
|
|
225
|
+
}
|
|
218
226
|
};
|
|
219
227
|
exports.SimpleFallbackJsonRpcBatchProvider._formatter = null;
|
|
220
228
|
exports.SimpleFallbackJsonRpcBatchProvider = tslib.__decorate([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lido-nestjs/execution",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@ethersproject/properties": "^5.5.0",
|
|
42
42
|
"@ethersproject/providers": "^5.5.3",
|
|
43
43
|
"@ethersproject/web": "^5.5.1",
|
|
44
|
-
"@lido-nestjs/logger": "1.3.
|
|
44
|
+
"@lido-nestjs/logger": "1.3.2",
|
|
45
45
|
"@lido-nestjs/middleware": "1.2.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|