fixing abort controller tx bug

This commit is contained in:
Blake Kaufman
2026-02-09 11:36:54 -05:00
parent 012b962d57
commit c5ceda4915
2 changed files with 78 additions and 41 deletions
+60 -20
View File
@@ -62,23 +62,41 @@ export const createPollingManager = ({
// Re-check abort conditions before executing
if (abortController?.signal?.aborted || !shouldContinue()) {
cleanup();
resolve({ success: false, reason: 'aborted' });
resolve({
success: false,
reason: 'aborted',
});
return;
}
// Execute the poll function
globalResult = await pollFn(delayIndex);
// Check abort AFTER async operation
if (abortController?.signal?.aborted || !shouldContinue()) {
cleanup();
resolve({
success: false,
reason: 'aborted',
});
return;
}
// Update previous result BEFORE validation
if (globalResult != null) {
previousResult = globalResult;
}
// Validate if we should continue
if (validateResult(globalResult, previousResult)) {
// Call update callback
if (onUpdate) {
onUpdate(globalResult, delayIndex);
await onUpdate(globalResult, delayIndex);
}
// Success - stop polling
cleanup();
resolve({ success: true, globalResult });
resolve({ success: true, result: globalResult });
return;
}
@@ -87,14 +105,6 @@ export const createPollingManager = ({
} catch (err) {
console.log('Error in polling iteration, continuing:', err);
poll(delayIndex + 1, resolve, reject);
} finally {
if (
globalResult != null &&
(previousResult == null || globalResult !== previousResult)
) {
// Only update if balance changes
previousResult = globalResult;
}
}
}, delays[delayIndex]);
} catch (err) {
@@ -105,7 +115,18 @@ export const createPollingManager = ({
};
return {
start: () => new Promise((resolve, reject) => poll(0, resolve, reject)),
start: () => {
if (abortController?.signal?.aborted) {
console.log('Poller: Already aborted before start');
return Promise.resolve({
success: false,
reason: 'aborted',
result: previousResult,
});
}
return new Promise((resolve, reject) => poll(0, resolve, reject));
},
cleanup,
};
};
@@ -119,6 +140,13 @@ export const createBalancePoller = (
) => {
let hasIncreasedAtLeastOnce = false;
let sameValueIndex = 0;
// Wrap initialBalance in the expected format
const wrappedInitialBalance =
typeof initialBalance === 'number'
? { didWork: true, balance: initialBalance, tokensObj: {} }
: initialBalance;
return createPollingManager({
pollFn: async () => {
return await getSparkBalance(mnemonic);
@@ -126,13 +154,24 @@ export const createBalancePoller = (
shouldContinue: () => mnemonic === currentMnemonicRef.current,
validateResult: (newResult, previousResult) => {
console.log(
newResult,
previousResult,
hasIncreasedAtLeastOnce,
sameValueIndex,
'Validate:',
{
newBalance: newResult?.balance,
prevBalance: previousResult?.balance,
},
{ hasIncreasedAtLeastOnce, sameValueIndex },
);
if (!newResult?.didWork || !previousResult?.didWork) return false;
if (!newResult?.didWork) {
console.log('New result invalid');
return false;
}
// previousResult might be null on first run
if (!previousResult?.didWork) {
console.log('Previous result invalid, continuing');
return false;
}
const newBalance = Number(newResult.balance);
const previousBalance = Number(previousResult.balance);
@@ -145,6 +184,7 @@ export const createBalancePoller = (
console.log('Balance changed — resetting');
hasIncreasedAtLeastOnce = true;
sameValueIndex = 0;
return false;
}
sameValueIndex++;
@@ -158,18 +198,18 @@ export const createBalancePoller = (
return false;
},
onUpdate: (balanceResult, delayIndex) => {
onUpdate: async (balanceResult, delayIndex) => {
console.log(
`Balance updated to ${balanceResult.balance} after ${delayIndex} attempts`,
);
onBalanceUpdate(balanceResult);
await onBalanceUpdate(balanceResult);
},
abortController,
delays: [
1000, 1500, 1500, 1500, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000,
2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000,
],
initialBalance,
initialBalance: wrappedInitialBalance,
});
};
+18 -21
View File
@@ -543,12 +543,6 @@ const SparkWalletProvider = ({ children }) => {
}));
}
} else if (updateType === 'incomingPayment') {
// incomingPayment has authoritative balance - clear ALL pending flags
if (balancePollingAbortControllerRef.current) {
balancePollingAbortControllerRef.current.abort();
balancePollingAbortControllerRef.current = null;
}
pendingSparkTxIds.current.clear();
pendingTxRange.current = { start: -1, end: -1 };
pendingTxCount.current = 0;
@@ -581,12 +575,6 @@ const SparkWalletProvider = ({ children }) => {
updateType === 'fullUpdate-waitBalance' ||
updateType === 'paymentWrapperTx'
) {
// Balance may have changed (e.g. restore found new txs, gift received)
// Poll until balance stabilizes, then set confirmed value
if (balancePollingAbortControllerRef.current) {
balancePollingAbortControllerRef.current.abort();
}
balancePollingAbortControllerRef.current = new AbortController();
currentPollingMnemonicRef.current = mnemonic;
const pollingMnemonic = mnemonic;
@@ -668,10 +656,6 @@ const SparkWalletProvider = ({ children }) => {
return;
}
} else {
if (balancePollingAbortControllerRef.current) {
balancePollingAbortControllerRef.current.abort();
}
const MAX_RETRIES = 3;
const RETRY_DELAY = 2000;
let balanceResponse = await getSparkBalance(mnemonic);
@@ -961,6 +945,24 @@ const SparkWalletProvider = ({ children }) => {
const handleUpdate = useCallback(
(...args) => {
const [updateType] = args;
if (
!(
updateType === 'lrc20Payments' ||
updateType === 'txStatusUpdate' ||
updateType === 'transactions' ||
updateType === 'contactDetailsUpdate'
)
) {
console.log(`Aborting any existing poller for incoming ${updateType}`);
if (balancePollingAbortControllerRef.current) {
balancePollingAbortControllerRef.current.abort();
balancePollingAbortControllerRef.current = null;
}
}
// Then queue the actual work
handleUpdateQueueRef.current = handleUpdateQueueRef.current
.then(() => handleUpdateInternal(...args))
.catch(err =>
@@ -998,11 +1000,6 @@ const SparkWalletProvider = ({ children }) => {
console.log('adding web view listeners');
sparkTransactionsEventEmitter.removeAllListeners(
SPARK_TX_UPDATE_ENVENT_NAME,
);
incomingSparkTransaction.removeAllListeners(INCOMING_SPARK_TX_NAME);
sparkTransactionsEventEmitter.on(SPARK_TX_UPDATE_ENVENT_NAME, handleUpdate);
incomingSparkTransaction.on(INCOMING_SPARK_TX_NAME, transferHandler);