diff --git a/CHANGELOG.md b/CHANGELOG.md index a7b6c79e7..08bb87715 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- ASB+GUI: Skip publishing the Monero redeem/refund transaction if it is already present on chain (e.g. after a restart) + ## [4.8.4] - 2026-06-09 ## [4.8.3] - 2026-06-08 diff --git a/monero-wallet/src/wallets.rs b/monero-wallet/src/wallets.rs index 46306b85e..ee053168d 100644 --- a/monero-wallet/src/wallets.rs +++ b/monero-wallet/src/wallets.rs @@ -292,6 +292,20 @@ impl Wallets { rpc_client } + pub async fn is_transaction_present(&self, tx_hash: &TxHash) -> Result { + use monero_wallet_ng::rpc::{ProvidesTransactionStatus, TransactionStatus}; + + let rpc_client = self.rpc_client().await; + let tx_id = tx_hash_to_bytes(tx_hash)?; + + let status = rpc_client + .transaction_status(tx_id) + .await + .context("Failed to query Monero transaction status")?; + + Ok(!matches!(status, TransactionStatus::Unknown)) + } + pub async fn direct_rpc_block_height(&self) -> Result { use monero_daemon_rpc::prelude::ProvidesBlockchainMeta; let rpc_client = self.rpc_client().await; diff --git a/swap/src/protocol/alice/swap.rs b/swap/src/protocol/alice/swap.rs index a5120940f..cada39cb4 100644 --- a/swap/src/protocol/alice/swap.rs +++ b/swap/src/protocol/alice/swap.rs @@ -753,6 +753,17 @@ where retry( "Publishing Monero refund transaction", || async { + let is_present = monero_wallet + .is_transaction_present(&xmr_refund_tx_hash) + .await + .context("Failed to check whether Monero refund transaction is already present on chain") + .map_err(backoff::Error::transient)?; + + if is_present { + tracing::info!(%swap_id, %xmr_refund_tx_hash, "Monero refund transaction is already present on chain, skipping publish"); + return Ok(()); + } + monero_wallet .rpc_client() .await diff --git a/swap/src/protocol/bob/swap.rs b/swap/src/protocol/bob/swap.rs index 4b73740a4..9119e76e4 100644 --- a/swap/src/protocol/bob/swap.rs +++ b/swap/src/protocol/bob/swap.rs @@ -793,6 +793,17 @@ async fn next_state( retry( "Publishing Monero redeem transaction", || async { + let is_present = monero_wallet + .is_transaction_present(&xmr_redeem_tx_hash) + .await + .context("Failed to check whether Monero redeem transaction is already present on chain") + .map_err(backoff::Error::transient)?; + + if is_present { + tracing::info!(%swap_id, %xmr_redeem_tx_hash, "Monero redeem transaction is already present on chain, skipping publish"); + return Ok(()); + } + monero_wallet .rpc_client() .await