Skip to content

Commit aacb5bf

Browse files
committed
fix: add NOT_FOUND status for withdrawal idempotency check
Add explicit NOT_FOUND status to WithdrawStatus enum to distinguish between "never submitted" and "in progress" withdrawals. This prevents double cBTC burning if the process restarts after withdrawSend() but before correlationId is persisted.
1 parent 87c0e61 commit aacb5bf

2 files changed

Lines changed: 17 additions & 1 deletion

File tree

src/integration/blockchain/clementine/clementine-client.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export enum DepositStatus {
2929
}
3030

3131
export enum WithdrawStatus {
32+
NOT_FOUND = 'not_found',
3233
PENDING = 'pending',
3334
SCANNING = 'scanning',
3435
SIGNING = 'signing',
@@ -314,9 +315,20 @@ export class ClementineClient {
314315
* Get the status of a withdrawal operation
315316
* Command: clementine-cli withdraw status <WITHDRAWAL_UTXO>
316317
* @param withdrawalUtxo The withdrawal UTXO to check (format: txid:vout)
318+
* @returns Status result with NOT_FOUND if no withdrawal exists for this UTXO
317319
*/
318320
async withdrawStatus(withdrawalUtxo: string): Promise<WithdrawStatusResult> {
319321
const output = await this.executeCommand(['withdraw', 'status', withdrawalUtxo]);
322+
323+
// Check if no withdrawal exists for this UTXO
324+
// CLI outputs "No withdrawals found for OutPoint ..." if never submitted
325+
if (output.toLowerCase().includes('no withdrawals found')) {
326+
return {
327+
withdrawalUtxo,
328+
status: WithdrawStatus.NOT_FOUND,
329+
};
330+
}
331+
320332
return this.parseWithdrawStatus(output, withdrawalUtxo);
321333
}
322334

src/subdomains/core/liquidity-management/adapters/actions/clementine-bridge.adapter.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
CLEMENTINE_WITHDRAWAL_DUST_BTC,
1414
ClementineClient,
1515
ClementineNetwork,
16+
WithdrawStatus,
1617
} from 'src/integration/blockchain/clementine/clementine-client';
1718
import { ClementineService } from 'src/integration/blockchain/clementine/clementine.service';
1819
import { Blockchain } from 'src/integration/blockchain/shared/enums/blockchain.enum';
@@ -406,7 +407,10 @@ export class ClementineBridgeAdapter extends LiquidityActionAdapter {
406407
// This prevents double cBTC burning if the process crashes after withdrawSend()
407408
// but before the correlationId is persisted
408409
const existingStatus = await this.clementineClient.withdrawStatus(data.withdrawalUtxo);
409-
if (existingStatus.status !== 'pending' && existingStatus.status !== 'failed') {
410+
411+
// Only proceed with withdrawSend() if NO withdrawal exists for this UTXO
412+
// If status is anything other than NOT_FOUND, the withdrawal was already submitted
413+
if (existingStatus.status !== WithdrawStatus.NOT_FOUND) {
410414
this.logger.info(
411415
`Withdrawal: already submitted to bridge (status: ${existingStatus.status}), skipping withdrawSend()`,
412416
);

0 commit comments

Comments
 (0)