From f08af0a812ad824efb639bbc5b4abcb902211ecc Mon Sep 17 00:00:00 2001 From: Sam Walker Date: Wed, 30 Oct 2024 00:59:35 -0400 Subject: [PATCH 1/3] added new variables to PendingTransactionTracker --- .gitignore | 3 +++ .../src/helpers/PendingTransactionTracker.ts | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5043addaa4..6b778ba904 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ scripts/coverage # typescript packages/*/*.tsbuildinfo + +# Webstorm IDE +.idea diff --git a/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts b/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts index dc4e12d8c7..9247f051b5 100644 --- a/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts +++ b/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts @@ -21,6 +21,9 @@ const RECEIPT_STATUS_SUCCESS = '0x1'; const RECEIPT_STATUS_FAILURE = '0x0'; const MAX_RETRY_BLOCK_DISTANCE = 50; +const MAX_ACCELERATED_POLLS = 5; +const ACCELERATED_POLLING_INTERVAL = 2000; + const KNOWN_TRANSACTION_ERRORS = [ 'replacement transaction underpriced', 'known transaction', @@ -120,7 +123,6 @@ export class PendingTransactionTracker { }; }) { this.hub = new EventEmitter() as PendingTransactionTrackerEventEmitter; - this.#blockTracker = blockTracker; this.#droppedBlockCountByHash = new Map(); this.#getChainId = getChainId; From 4aff41c371b4ef58f5a54147dc3502039d7f013e Mon Sep 17 00:00:00 2001 From: Sam Walker Date: Wed, 30 Oct 2024 01:48:10 -0400 Subject: [PATCH 2/3] Added accelerated pending transaction checks --- .../src/helpers/PendingTransactionTracker.ts | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts b/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts index 9247f051b5..10e732077d 100644 --- a/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts +++ b/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts @@ -95,6 +95,8 @@ export class PendingTransactionTracker { #beforePublish: (transactionMeta: TransactionMeta) => boolean; + #acceleratedPollingCount: number; + constructor({ blockTracker, getChainId, @@ -136,12 +138,14 @@ export class PendingTransactionTracker { this.#beforePublish = hooks?.beforePublish ?? (() => true); this.#beforeCheckPendingTransaction = hooks?.beforeCheckPendingTransaction ?? (() => true); + this.#acceleratedPollingCount = 0; } startIfPendingTransactions = () => { const pendingTransactions = this.#getPendingTransactions(); if (pendingTransactions.length) { + this.#resetAcceleratedPolling(); this.#start(); } else { this.stop(); @@ -171,7 +175,14 @@ export class PendingTransactionTracker { return; } - this.#blockTracker.on('latest', this.#listener); + if (this.#acceleratedPollingCount < MAX_ACCELERATED_POLLS) { + // Start accelerated polling + this.#startAcceleratedPolling(); + } else { + // Fall back to normal block-based polling + this.#startNormalPolling(); + } + this.#running = true; log('Started polling'); @@ -188,6 +199,50 @@ export class PendingTransactionTracker { log('Stopped polling'); } + #startAcceleratedPolling() { + const checkAndSchedule = async () => { + try { + const releaseLock = await this.#getGlobalLock(); + try { + await this.#checkTransactions(); + } finally { + releaseLock(); + } + + // Only continue accelerated polling if we still have pending transactions + // and haven't exceeded our maximum accelerated polls + if ( + this.#getPendingTransactions().length > 0 && + this.#acceleratedPollingCount < MAX_ACCELERATED_POLLS + ) { + this.#acceleratedPollingCount += 1; + // eslint-disable-next-line @typescript-eslint/no-misused-promises + setTimeout(checkAndSchedule, ACCELERATED_POLLING_INTERVAL); + } else { + // Switch to normal polling mode + this.#startNormalPolling(); + } + } catch (error) { + log('Error during accelerated polling', error); + // Switch to normal polling mode on error + this.#startNormalPolling(); + } + }; + + // Start the first aggressive check + // eslint-disable-next-line no-void + void checkAndSchedule(); + } + + #startNormalPolling() { + this.#blockTracker.on('latest', this.#listener); + } + + #resetAcceleratedPolling() { + // Reset counter when transaction list changes + this.#acceleratedPollingCount = 0; + } + async #onLatestBlock(latestBlockNumber: string) { const releaseLock = await this.#getGlobalLock(); From a77fe0f916a7e0a308e17b53990fe891811d11fd Mon Sep 17 00:00:00 2001 From: Sam Walker Date: Wed, 30 Oct 2024 02:01:05 -0400 Subject: [PATCH 3/3] functional adjustments --- .../src/TransactionController.ts | 1 + .../src/helpers/PendingTransactionTracker.ts | 49 ++++++++++--------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/packages/transaction-controller/src/TransactionController.ts b/packages/transaction-controller/src/TransactionController.ts index 64fb2d3064..13c45ac71f 100644 --- a/packages/transaction-controller/src/TransactionController.ts +++ b/packages/transaction-controller/src/TransactionController.ts @@ -3394,6 +3394,7 @@ export class TransactionController extends BaseController< this.publishTransaction(_ethQuery, transactionMeta, { skipSubmitHistory: true, }), + acceleratedPollingEnabled: true, hooks: { beforeCheckPendingTransaction: this.beforeCheckPendingTransaction.bind(this), diff --git a/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts b/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts index 10e732077d..70bedc5dbd 100644 --- a/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts +++ b/packages/transaction-controller/src/helpers/PendingTransactionTracker.ts @@ -97,6 +97,8 @@ export class PendingTransactionTracker { #acceleratedPollingCount: number; + #acceleratedPollingEnabled: boolean; + constructor({ blockTracker, getChainId, @@ -105,6 +107,7 @@ export class PendingTransactionTracker { isResubmitEnabled, getGlobalLock, publishTransaction, + acceleratedPollingEnabled, hooks, }: { blockTracker: BlockTracker; @@ -117,6 +120,7 @@ export class PendingTransactionTracker { ethQuery: EthQuery, transactionMeta: TransactionMeta, ) => Promise; + acceleratedPollingEnabled: boolean; hooks?: { beforeCheckPendingTransaction?: ( transactionMeta: TransactionMeta, @@ -139,6 +143,7 @@ export class PendingTransactionTracker { this.#beforeCheckPendingTransaction = hooks?.beforeCheckPendingTransaction ?? (() => true); this.#acceleratedPollingCount = 0; + this.#acceleratedPollingEnabled = acceleratedPollingEnabled; } startIfPendingTransactions = () => { @@ -175,7 +180,10 @@ export class PendingTransactionTracker { return; } - if (this.#acceleratedPollingCount < MAX_ACCELERATED_POLLS) { + if ( + this.#acceleratedPollingEnabled && + this.#acceleratedPollingCount < MAX_ACCELERATED_POLLS + ) { // Start accelerated polling this.#startAcceleratedPolling(); } else { @@ -201,30 +209,25 @@ export class PendingTransactionTracker { #startAcceleratedPolling() { const checkAndSchedule = async () => { + const releaseLock = await this.#getGlobalLock(); try { - const releaseLock = await this.#getGlobalLock(); - try { - await this.#checkTransactions(); - } finally { - releaseLock(); - } + await this.#checkTransactions(); + } finally { + releaseLock(); + } - // Only continue accelerated polling if we still have pending transactions - // and haven't exceeded our maximum accelerated polls - if ( - this.#getPendingTransactions().length > 0 && - this.#acceleratedPollingCount < MAX_ACCELERATED_POLLS - ) { - this.#acceleratedPollingCount += 1; - // eslint-disable-next-line @typescript-eslint/no-misused-promises - setTimeout(checkAndSchedule, ACCELERATED_POLLING_INTERVAL); - } else { - // Switch to normal polling mode - this.#startNormalPolling(); - } - } catch (error) { - log('Error during accelerated polling', error); - // Switch to normal polling mode on error + // If we no longer have pending transactions, stop polling + if (this.#getPendingTransactions().length === 0) { + this.stop(); + return; + } + // If we still have pending transactions, and we are below the max poll, do an additional accelerated poll + if (this.#acceleratedPollingCount < MAX_ACCELERATED_POLLS) { + this.#acceleratedPollingCount += 1; + // eslint-disable-next-line @typescript-eslint/no-misused-promises + setTimeout(checkAndSchedule, ACCELERATED_POLLING_INTERVAL); + } else { + // If we have reached the max poll, fall back to normal polling this.#startNormalPolling(); } };