@@ -566,6 +566,13 @@ export async function getUpdatedCctpTransfer(tx: MergedTransaction): Promise<Mer
566566 return { ...tx , status : WithdrawalStatus . UNCONFIRMED } ;
567567}
568568
569+ // A LiFi entry is cached optimistically once the wallet returns a source tx hash. If that
570+ // transaction never actually lands on-chain (reverted, or dropped from the mempool and never
571+ // mined), the entry would otherwise sit "pending" forever — LiFi's status API keeps reporting
572+ // PENDING for a hash it never saw. We only treat a missing transaction as dropped after a grace
573+ // window, so a tx that is merely still propagating isn't reconciled away prematurely.
574+ const LIFI_SOURCE_TX_DROPPED_THRESHOLD_MS = 1000 * 60 * 30 ; // 30 minutes
575+
569576export async function getUpdatedLifiTransfer (
570577 tx : LifiMergedTransaction ,
571578) : Promise < MergedTransaction > {
@@ -578,6 +585,40 @@ export async function getUpdatedLifiTransfer(
578585 return tx ;
579586 }
580587
588+ // Vet the cached source transaction against the chain before trusting LiFi's status.
589+ try {
590+ const sourceChainProvider = getProviderForChainId ( tx . sourceChainId ) ;
591+ const sourceTxReceipt = await sourceChainProvider . getTransactionReceipt ( tx . txId ) ;
592+
593+ if ( sourceTxReceipt ) {
594+ if ( sourceTxReceipt . status === 0 ) {
595+ // Source transaction reverted on-chain — terminal failure.
596+ return {
597+ ...tx ,
598+ status : WithdrawalStatus . FAILURE ,
599+ destinationStatus : WithdrawalStatus . FAILURE ,
600+ } ;
601+ }
602+ } else {
603+ const pendingSourceTx = await sourceChainProvider . getTransaction ( tx . txId ) ;
604+ const isOlderThanDropThreshold =
605+ typeof tx . createdAt === 'number' &&
606+ dayjs ( ) . valueOf ( ) - tx . createdAt > LIFI_SOURCE_TX_DROPPED_THRESHOLD_MS ;
607+
608+ // No receipt and the node no longer knows the tx: it was dropped and never mined, so the
609+ // funds never moved. Mark terminal so it stops counting as a pending transfer.
610+ if ( ! pendingSourceTx && isOlderThanDropThreshold ) {
611+ return {
612+ ...tx ,
613+ status : WithdrawalStatus . FAILURE ,
614+ destinationStatus : WithdrawalStatus . FAILURE ,
615+ } ;
616+ }
617+ }
618+ } catch {
619+ // RPC hiccup — don't make a terminal decision; fall back to LiFi's status below.
620+ }
621+
581622 const statusResponse = await getStatus ( {
582623 txHash : tx . txId ,
583624 bridge : tx . toolDetails . key ,
0 commit comments