@lido-nestjs/execution 1.16.0 → 1.17.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.
@@ -2,3 +2,4 @@ export * from './all-providers-failed.error';
2
2
  export * from './fetch.error';
3
3
  export * from './no-new-blocks-while-polling.error';
4
4
  export * from './request-timeout.error';
5
+ export * from './transaction-wait-timeout.error';
@@ -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');
@@ -327,6 +329,49 @@ exports.SimpleFallbackJsonRpcBatchProvider = class SimpleFallbackJsonRpcBatchPro
327
329
  get eventEmitter() {
328
330
  return this._eventEmitter;
329
331
  }
332
+ /**
333
+ * Waits for transaction confirmation with fallback provider support.
334
+ *
335
+ * Use instead of tx.wait() which hangs forever when providers fail:
336
+ * - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1055 - polling calls getTransactionReceipt
337
+ * - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1060 - .catch(e => emit("error", e)) swallows error
338
+ * - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1313 - waits for event that never comes
339
+ * - node_modules/@ethersproject/providers/src.ts/base-provider.ts:1412 - timeout=0 by default, no reject
340
+ *
341
+ * This method polls via perform() with fallback and always returns/throws.
342
+ */
343
+ async waitForTransactionWithFallback(txHash, options = {}) {
344
+ const { timeout = 60000, pollInterval = 3000, confirmations = 1, } = options;
345
+ const startTime = Date.now();
346
+ let lastError = null;
347
+ let pollCount = 0;
348
+ this.logger.log(this.formatLog(`Starting waitForTransactionWithFallback for ${txHash} (timeout: ${timeout}ms, pollInterval: ${pollInterval}ms, confirmations: ${confirmations})`));
349
+ while (Date.now() - startTime < timeout) {
350
+ pollCount++;
351
+ try {
352
+ // Uses perform() which handles fallback switching automatically
353
+ 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 };
358
+ }
359
+ lastError = null;
360
+ }
361
+ catch (error) {
362
+ // All providers failed - log and retry until timeout
363
+ lastError = error;
364
+ this.logger.warn(this.formatLog(`waitForTransactionWithFallback poll #${pollCount} failed for ${txHash}: ${error}`));
365
+ }
366
+ await sleep.sleep(pollInterval);
367
+ }
368
+ const elapsedMs = Date.now() - startTime;
369
+ const errorContext = lastError
370
+ ? `All providers failed on last poll: ${lastError.message}`
371
+ : 'Transaction not found in any block';
372
+ this.logger.error(this.formatLog(`waitForTransactionWithFallback timeout for ${txHash} after ${pollCount} polls (${elapsedMs}ms). ${errorContext}`));
373
+ throw new transactionWaitTimeout_error.TransactionWaitTimeoutError(txHash, timeout, lastError);
374
+ }
330
375
  };
331
376
  exports.SimpleFallbackJsonRpcBatchProvider._formatter = null;
332
377
  exports.SimpleFallbackJsonRpcBatchProvider = tslib.__decorate([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lido-nestjs/execution",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "license": "MIT",