@bsv/sdk
Version:
BSV Blockchain Software Development Kit
48 lines • 2.5 kB
JavaScript
import Transaction from '../transaction/Transaction.js';
/**
* Maximum number of retry attempts for double-spend resolution
*/
const MAX_DOUBLE_SPEND_RETRIES = 5;
/**
* Executes an operation with automatic retry logic for double-spend errors.
* When a double-spend is detected, broadcasts the competing transaction to
* update the overlay with missing state, then retries the operation.
*
* @param operation - The async operation to execute (e.g., createAction + signAction)
* @param broadcaster - The TopicBroadcaster to use for syncing missing state
* @param maxRetries - Maximum number of retry attempts (default: MAX_DOUBLE_SPEND_RETRIES)
* @returns The result of the successful operation
* @throws If max retries exceeded or non-double-spend error occurs
*/
export async function withDoubleSpendRetry(operation, broadcaster, maxRetries = MAX_DOUBLE_SPEND_RETRIES) {
let attempts = 0;
while (attempts < maxRetries) {
attempts++;
try {
return await operation();
}
catch (error) {
// Only handle double-spend errors on non-final attempts
if (attempts < maxRetries && error.name === 'WERR_REVIEW_ACTIONS') {
const reviewError = error;
// Check if any action in the batch has a double-spend
const doubleSpendResult = reviewError.reviewActionResults.find((result) => result.status === 'doubleSpend');
if (doubleSpendResult?.competingBeef != null &&
doubleSpendResult?.competingTxs != null &&
doubleSpendResult?.competingTxs.length > 0) {
const competingTx = Transaction.fromBEEF(doubleSpendResult.competingBeef, doubleSpendResult.competingTxs[0]);
// Recursively handle double-spend errors during broadcast
// The broadcast itself might fail if the competing tx depends on another missing tx
await withDoubleSpendRetry(async () => await broadcaster.broadcast(competingTx), broadcaster, maxRetries - attempts // Reduce max retries based on attempts already used
);
continue;
}
}
// Non-double-spend error or max retries exceeded - rethrow
throw error;
}
}
// This should never be reached, but TypeScript requires it
throw new Error('Unexpected end of retry loop');
}
//# sourceMappingURL=withDoubleSpendRetry.js.map