@lido-nestjs/execution 1.16.0 → 1.18.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/error/index.d.ts +1 -0
- package/dist/error/transaction-wait-timeout.error.d.ts +11 -0
- package/dist/error/transaction-wait-timeout.error.js +24 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/interfaces/wait-for-transaction.d.ts +14 -0
- package/dist/provider/simple-fallback-json-rpc-batch-provider.d.ts +13 -0
- package/dist/provider/simple-fallback-json-rpc-batch-provider.js +63 -6
- package/package.json +1 -1
package/dist/error/index.d.ts
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when waitForTransactionWithFallback times out.
|
|
3
|
+
* lastError distinguishes network issues (present) from slow tx (null).
|
|
4
|
+
*/
|
|
5
|
+
export declare class TransactionWaitTimeoutError extends Error {
|
|
6
|
+
name: string;
|
|
7
|
+
txHash: string;
|
|
8
|
+
timeoutMs: number;
|
|
9
|
+
lastError: Error | null;
|
|
10
|
+
constructor(txHash: string, timeoutMs: number, lastError: Error | null);
|
|
11
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Thrown when waitForTransactionWithFallback times out.
|
|
7
|
+
* lastError distinguishes network issues (present) from slow tx (null).
|
|
8
|
+
*/
|
|
9
|
+
class TransactionWaitTimeoutError extends Error {
|
|
10
|
+
constructor(txHash, timeoutMs, lastError) {
|
|
11
|
+
const baseMessage = `Transaction ${txHash} not confirmed within ${timeoutMs}ms`;
|
|
12
|
+
const errorContext = lastError
|
|
13
|
+
? `. Last provider error: ${lastError.message}`
|
|
14
|
+
: ' (no provider errors, transaction may still be pending)';
|
|
15
|
+
super(baseMessage + errorContext);
|
|
16
|
+
this.name = 'TransactionWaitTimeoutError';
|
|
17
|
+
this.txHash = txHash;
|
|
18
|
+
this.timeoutMs = timeoutMs;
|
|
19
|
+
this.lastError = lastError;
|
|
20
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
exports.TransactionWaitTimeoutError = TransactionWaitTimeoutError;
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './interfaces/fallback-provider';
|
|
|
7
7
|
export * from './interfaces/simple-fallback-provider-config';
|
|
8
8
|
export * from './interfaces/module.options';
|
|
9
9
|
export * from './interfaces/non-empty-array';
|
|
10
|
+
export * from './interfaces/wait-for-transaction';
|
|
10
11
|
export * from './ethers/fee-history';
|
|
11
12
|
export * from './ethers/block-tag';
|
|
12
13
|
export * from './error';
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ var allProvidersFailed_error = require('./error/all-providers-failed.error.js');
|
|
|
12
12
|
var fetch_error = require('./error/fetch.error.js');
|
|
13
13
|
var noNewBlocksWhilePolling_error = require('./error/no-new-blocks-while-polling.error.js');
|
|
14
14
|
var requestTimeout_error = require('./error/request-timeout.error.js');
|
|
15
|
+
var transactionWaitTimeout_error = require('./error/transaction-wait-timeout.error.js');
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
|
|
@@ -39,3 +40,4 @@ exports.AllProvidersFailedError = allProvidersFailed_error.AllProvidersFailedErr
|
|
|
39
40
|
exports.FetchError = fetch_error.FetchError;
|
|
40
41
|
exports.NoNewBlocksWhilePollingError = noNewBlocksWhilePolling_error.NoNewBlocksWhilePollingError;
|
|
41
42
|
exports.RequestTimeoutError = requestTimeout_error.RequestTimeoutError;
|
|
43
|
+
exports.TransactionWaitTimeoutError = transactionWaitTimeout_error.TransactionWaitTimeoutError;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TransactionReceipt } from '@ethersproject/abstract-provider';
|
|
2
|
+
export interface WaitForTransactionOptions {
|
|
3
|
+
/** Max wait time in ms. @default 60000 */
|
|
4
|
+
timeout?: number;
|
|
5
|
+
/** Polling interval in ms. @default 3000 */
|
|
6
|
+
pollInterval?: number;
|
|
7
|
+
/** Required confirmations. @default 1 */
|
|
8
|
+
confirmations?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface WaitForTransactionResult {
|
|
11
|
+
receipt: TransactionReceipt;
|
|
12
|
+
pollCount: number;
|
|
13
|
+
elapsedMs: number;
|
|
14
|
+
}
|
|
@@ -10,6 +10,7 @@ import { BigNumber, BigNumberish } from '@ethersproject/bignumber';
|
|
|
10
10
|
import { Deferrable } from '@ethersproject/properties';
|
|
11
11
|
import { EventType, Listener } from '@ethersproject/abstract-provider';
|
|
12
12
|
import { FeeHistory } from '../ethers/fee-history';
|
|
13
|
+
import { WaitForTransactionOptions, WaitForTransactionResult } from '../interfaces/wait-for-transaction';
|
|
13
14
|
import { TraceConfig, TraceResult } from '../interfaces/debug-traces';
|
|
14
15
|
import { FallbackProviderEvents } from '../events';
|
|
15
16
|
/**
|
|
@@ -62,4 +63,16 @@ export declare class SimpleFallbackJsonRpcBatchProvider extends BaseProvider {
|
|
|
62
63
|
protected networksEqual(networkA: Network, networkB: Network): boolean;
|
|
63
64
|
get activeProviderIndex(): number;
|
|
64
65
|
get eventEmitter(): SimpleFallbackJsonRpcBatchProviderEventEmitter;
|
|
66
|
+
/**
|
|
67
|
+
* Waits for transaction confirmation with fallback provider support.
|
|
68
|
+
*
|
|
69
|
+
* Use instead of tx.wait() which hangs forever when providers fail:
|
|
70
|
+
* - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1055 - polling calls getTransactionReceipt
|
|
71
|
+
* - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1060 - .catch(e => emit("error", e)) swallows error
|
|
72
|
+
* - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1313 - waits for event that never comes
|
|
73
|
+
* - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1412 - timeout=0 by default, no reject
|
|
74
|
+
*
|
|
75
|
+
* This method polls via perform() with fallback and always returns/throws.
|
|
76
|
+
*/
|
|
77
|
+
waitForTransactionWithFallback(txHash: string, options?: WaitForTransactionOptions): Promise<WaitForTransactionResult>;
|
|
65
78
|
}
|
|
@@ -7,12 +7,14 @@ var providers = require('@ethersproject/providers');
|
|
|
7
7
|
var extendedJsonRpcBatchProvider = require('./extended-json-rpc-batch-provider.js');
|
|
8
8
|
var common = require('@nestjs/common');
|
|
9
9
|
var retrier = require('../common/retrier.js');
|
|
10
|
+
var sleep = require('../common/sleep.js');
|
|
10
11
|
var formatterWithEip1898 = require('../ethers/formatter-with-eip1898.js');
|
|
11
12
|
var networks = require('../common/networks.js');
|
|
12
13
|
var noNewBlocksWhilePolling_error = require('../error/no-new-blocks-while-polling.error.js');
|
|
13
14
|
var errors = require('../common/errors.js');
|
|
14
15
|
var allProvidersFailed_error = require('../error/all-providers-failed.error.js');
|
|
15
16
|
var requestTimeout_error = require('../error/request-timeout.error.js');
|
|
17
|
+
var transactionWaitTimeout_error = require('../error/transaction-wait-timeout.error.js');
|
|
16
18
|
var feeHistory = require('../ethers/fee-history.js');
|
|
17
19
|
var debugTraceBlockByHash = require('../ethers/debug-trace-block-by-hash.js');
|
|
18
20
|
var events = require('events');
|
|
@@ -164,8 +166,10 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
164
166
|
]);
|
|
165
167
|
}
|
|
166
168
|
async perform(method, params) {
|
|
169
|
+
var _a, _b;
|
|
167
170
|
const retry = retrier.retrier(this.logger, this.config.maxRetries, this.config.minBackoffMs, this.config.maxBackoffMs, this.config.logRetries, (e) => this.isNonRetryableError(e));
|
|
168
171
|
let attempt = 0;
|
|
172
|
+
(_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, this.formatLog(`RPC call: ${method}`));
|
|
169
173
|
// will perform maximum `this.config.maxRetries` retries for fetching data with single provider
|
|
170
174
|
// after failure will switch to next provider
|
|
171
175
|
// maximum number of switching is limited to total fallback provider count
|
|
@@ -174,7 +178,7 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
174
178
|
let performRetryAttempt = 0;
|
|
175
179
|
attempt++;
|
|
176
180
|
// Log which provider we're attempting to use
|
|
177
|
-
this.logger.log(this.formatLog(`Attempting
|
|
181
|
+
this.logger.log(this.formatLog(`Attempting ${method} (attempt ${attempt}/${this.fallbackProviders.length})`, this.activeFallbackProviderIndex));
|
|
178
182
|
// awaiting is extremely important here
|
|
179
183
|
// without it, the error will not be caught in current try-catch scope
|
|
180
184
|
const result = await retry(() => {
|
|
@@ -197,7 +201,7 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
197
201
|
return performPromise;
|
|
198
202
|
});
|
|
199
203
|
// Log successful request
|
|
200
|
-
this.logger.log(this.formatLog(
|
|
204
|
+
this.logger.log(this.formatLog(`${method} successful after ${performRetryAttempt} retry attempt(s)`, this.activeFallbackProviderIndex));
|
|
201
205
|
return result;
|
|
202
206
|
}
|
|
203
207
|
catch (e) {
|
|
@@ -212,18 +216,18 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
212
216
|
this._eventEmitter.emit('rpc', event);
|
|
213
217
|
// Log context (label + provider index) synchronously before error object
|
|
214
218
|
// to ensure proper ordering in async logging systems
|
|
215
|
-
this.logger.error(this.formatLog(
|
|
219
|
+
this.logger.error(this.formatLog(`${method} non-retryable error occurred`, this.activeFallbackProviderIndex));
|
|
216
220
|
this.logger.error(e);
|
|
217
221
|
throw e;
|
|
218
222
|
}
|
|
219
223
|
// Log context (label + provider index) synchronously before error object
|
|
220
224
|
// to ensure proper ordering in async logging systems
|
|
221
225
|
if (e instanceof requestTimeout_error.RequestTimeoutError) {
|
|
222
|
-
this.logger.error(this.formatLog(
|
|
226
|
+
this.logger.error(this.formatLog(`${method} timeout after ${e.timeoutMs}ms. Will switch to next provider.`, this.activeFallbackProviderIndex));
|
|
223
227
|
this.logger.error(e);
|
|
224
228
|
}
|
|
225
229
|
else {
|
|
226
|
-
this.logger.error(this.formatLog(
|
|
230
|
+
this.logger.error(this.formatLog(`${method} error occurred. Will switch to next provider.`, this.activeFallbackProviderIndex));
|
|
227
231
|
this.logger.error(e);
|
|
228
232
|
}
|
|
229
233
|
// This check is needed to avoid multiple `switchToNextProvider` calls when doing one JSON-RPC batch.
|
|
@@ -237,7 +241,7 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
237
241
|
}
|
|
238
242
|
}
|
|
239
243
|
}
|
|
240
|
-
const allProvidersFailedError = new allProvidersFailed_error.AllProvidersFailedError(
|
|
244
|
+
const allProvidersFailedError = new allProvidersFailed_error.AllProvidersFailedError(`All attempts to do ETH1 RPC request failed for ${method}`);
|
|
241
245
|
allProvidersFailedError.cause = this.lastError;
|
|
242
246
|
const event = {
|
|
243
247
|
action: 'fallback-provider:request:failed:all',
|
|
@@ -327,6 +331,59 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
|
|
|
327
331
|
get eventEmitter() {
|
|
328
332
|
return this._eventEmitter;
|
|
329
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* Waits for transaction confirmation with fallback provider support.
|
|
336
|
+
*
|
|
337
|
+
* Use instead of tx.wait() which hangs forever when providers fail:
|
|
338
|
+
* - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1055 - polling calls getTransactionReceipt
|
|
339
|
+
* - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1060 - .catch(e => emit("error", e)) swallows error
|
|
340
|
+
* - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1313 - waits for event that never comes
|
|
341
|
+
* - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1412 - timeout=0 by default, no reject
|
|
342
|
+
*
|
|
343
|
+
* This method polls via perform() with fallback and always returns/throws.
|
|
344
|
+
*/
|
|
345
|
+
async waitForTransactionWithFallback(txHash, options = {}) {
|
|
346
|
+
const { timeout = 60000, pollInterval = 3000, confirmations = 1, } = options;
|
|
347
|
+
const startTime = Date.now();
|
|
348
|
+
let lastError = null;
|
|
349
|
+
let pollCount = 0;
|
|
350
|
+
this.logger.log(this.formatLog(`Starting waitForTransactionWithFallback for ${txHash} (timeout: ${timeout}ms, pollInterval: ${pollInterval}ms, confirmations: ${confirmations})`));
|
|
351
|
+
while (Date.now() - startTime < timeout) {
|
|
352
|
+
pollCount++;
|
|
353
|
+
try {
|
|
354
|
+
// Uses perform() which handles fallback switching automatically
|
|
355
|
+
const receipt = await this.getTransactionReceipt(txHash);
|
|
356
|
+
if (!receipt) {
|
|
357
|
+
// Transaction pending in mempool, not yet included in a block
|
|
358
|
+
lastError = null;
|
|
359
|
+
await sleep.sleep(pollInterval);
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
if (receipt.confirmations < confirmations) {
|
|
363
|
+
// Transaction mined but waiting for more confirmations
|
|
364
|
+
lastError = null;
|
|
365
|
+
await sleep.sleep(pollInterval);
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
// Transaction confirmed with enough confirmations
|
|
369
|
+
const elapsedMs = Date.now() - startTime;
|
|
370
|
+
this.logger.log(this.formatLog(`Transaction ${txHash} confirmed after ${pollCount} polls (${elapsedMs}ms, ${receipt.confirmations} confirmations)`));
|
|
371
|
+
return { receipt, pollCount, elapsedMs };
|
|
372
|
+
}
|
|
373
|
+
catch (error) {
|
|
374
|
+
// All providers failed - log and retry until timeout
|
|
375
|
+
lastError = error;
|
|
376
|
+
this.logger.warn(this.formatLog(`waitForTransactionWithFallback poll #${pollCount} failed for ${txHash}: ${error}`));
|
|
377
|
+
await sleep.sleep(pollInterval);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const elapsedMs = Date.now() - startTime;
|
|
381
|
+
const errorContext = lastError
|
|
382
|
+
? `All providers failed on last poll: ${lastError.message}`
|
|
383
|
+
: 'Transaction not found in any block';
|
|
384
|
+
this.logger.error(this.formatLog(`waitForTransactionWithFallback timeout for ${txHash} after ${pollCount} polls (${elapsedMs}ms). ${errorContext}`));
|
|
385
|
+
throw new transactionWaitTimeout_error.TransactionWaitTimeoutError(txHash, timeout, lastError);
|
|
386
|
+
}
|
|
330
387
|
};
|
|
331
388
|
exports.SimpleFallbackJsonRpcBatchProvider._formatter = null;
|
|
332
389
|
exports.SimpleFallbackJsonRpcBatchProvider = tslib.__decorate([
|