From 478aeda399b25c57d9ec633277879954c3dbf840 Mon Sep 17 00:00:00 2001 From: Juan Cazala Date: Mon, 13 Jan 2025 16:40:26 -0300 Subject: [PATCH] fix: apply fibonacci delay and threshold --- src/modules/transaction/sagas.ts | 61 +++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/src/modules/transaction/sagas.ts b/src/modules/transaction/sagas.ts index 21fbfa41..7452b512 100644 --- a/src/modules/transaction/sagas.ts +++ b/src/modules/transaction/sagas.ts @@ -83,7 +83,8 @@ const BLOCKS_DEPTH = 100 const TRANSACTION_FETCH_RETIES = 120 const PENDING_TRANSACTION_THRESHOLD = 72 * 60 * 60 * 1000 // 72 hours const REVERTED_TRANSACTION_THRESHOLD = 24 * 60 * 60 * 1000 // 24 hours -const TRANSACTION_FETCH_DELAY = 2 * 1000 // 2 seconds +const DROPPED_TRANSACTION_THRESHOLD = 24 * 60 * 60 * 1000 // 24 hours +const INITIAL_BACKOFF_DELAY = 2 * 1000 // 2 seconds const isExpired = (transaction: Transaction, threshold: number) => Date.now() - transaction.timestamp > threshold @@ -128,6 +129,7 @@ function* handleCrossChainTransactionRequest( let statusResponse: StatusResponse | undefined let txInState: Transaction + let attempt = 0 let squidNotFoundRetries: number = config.crossChainProviderNotFoundRetries ?? TRANSACTION_FETCH_RETIES while ( @@ -175,7 +177,10 @@ function* handleCrossChainTransactionRequest( ) return } - yield delay(config.crossChainProviderRetryDelay ?? TRANSACTION_FETCH_DELAY) + + const fibonacciDelay: number = yield call(getFibonacciDelay, attempt) + yield delay(config.crossChainProviderRetryDelay ?? fibonacciDelay) + attempt++ } txInState = yield select(state => @@ -222,6 +227,7 @@ function* handleRegularTransactionRequest( try { watchPendingIndex[hash] = true + let attempt = 0 let tx: AnyTransaction = yield call( getTransactionFromChain, @@ -266,8 +272,10 @@ function* handleRegularTransactionRequest( yield put(updateTransactionStatus(hash, statusInNetwork)) } - // sleep - yield delay(TRANSACTION_FETCH_DELAY) + // Apply fibonacci backoff delay before next iteration + const fibonacciDelay: number = yield call(getFibonacciDelay, attempt) + yield delay(fibonacciDelay) + attempt++ // update tx status from network tx = yield call( @@ -309,6 +317,19 @@ function* handleRegularTransactionRequest( } } +function* getFibonacciDelay(attempt: number) { + if (attempt <= 1) return INITIAL_BACKOFF_DELAY + + let prev = 1 + let current = 1 + for (let i = 2; i <= attempt; i++) { + const next = prev + current + prev = current + current = next + } + return current * INITIAL_BACKOFF_DELAY +} + function* handleReplaceTransactionRequest( action: ReplaceTransactionRequestAction ) { @@ -321,10 +342,25 @@ function* handleReplaceTransactionRequest( return } + // Check if transaction is already expired before starting to poll + if (isExpired(transaction, DROPPED_TRANSACTION_THRESHOLD)) { + yield put(updateTransactionStatus(hash, TransactionStatus.DROPPED)) + return + } + let checkpoint = null + let attempt = 0 watchDroppedIndex[hash] = true + const startTime = Date.now() + while (true) { + // Check if we've exceeded the time threshold during polling + if (Date.now() - startTime > DROPPED_TRANSACTION_THRESHOLD) { + yield put(updateTransactionStatus(hash, TransactionStatus.DROPPED)) + break + } + const eth: ethers.providers.Web3Provider = yield call( getNetworkWeb3Provider, transaction.chainId @@ -398,7 +434,7 @@ function* handleReplaceTransactionRequest( break } - // if there was nonce higher to than the one in the tx, we can mark it as replaced (altough we don't know which tx replaced it) + // if there was nonce higher to than the one in the tx, we can mark it as replaced (although we don't know which tx replaced it) if (highestNonce >= nonce) { yield put( updateTransactionStatus(action.payload.hash, TransactionStatus.REPLACED) @@ -406,8 +442,10 @@ function* handleReplaceTransactionRequest( break } - // sleep - yield delay(TRANSACTION_FETCH_DELAY) + // Apply fibonacci backoff delay before next iteration + const fibonacciDelay: number = yield call(getFibonacciDelay, attempt) + yield delay(fibonacciDelay) + attempt++ } delete watchDroppedIndex[action.payload.hash] @@ -440,7 +478,8 @@ function* handleWatchDroppedTransactions() { const droppedTransactions = transactions.filter( transaction => transaction.status === TransactionStatus.DROPPED && - transaction.nonce != null + transaction.nonce != null && + !isExpired(transaction, DROPPED_TRANSACTION_THRESHOLD) ) for (const tx of droppedTransactions) { @@ -468,9 +507,13 @@ function* handleWatchRevertedTransaction( } const address: string = yield select(state => getAddress(state)) + let attempt = 0 do { - yield delay(TRANSACTION_FETCH_DELAY) + const fibonacciDelay: number = yield call(getFibonacciDelay, attempt) + yield delay(fibonacciDelay) + attempt++ + const txInNetwork: AnyTransaction | null = yield call(() => getTransactionFromChain(address, txInState.chainId, hash) )