mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-24 22:19:37 -04:00

This diff introduces a new "approvals" mechanism that alters the swap flow by requiring explicit user intervention before the Bitcoin lock transaction is broadcast. Previously, the Bitcoin lock was executed automatically without any user prompt. Now, the backend defines `ApprovalRequestType` (e.g. a `PreBtcLock` variant with details like `btc_lock_amount`, `btc_network_fee`, and `xmr_receive_amount`) and `ApprovalEvent` (with statuses such as `Pending`, `Resolved`, and `Rejected`). The method `request_approval()` in the `TauriHandle` struct uses a oneshot channel and concurrent timeout handling via `tokio::select!` to wait for the user's decision. Based on the outcome—explicit approval or timeout/rejection—the approval event is emitted through the `emit_approval()` helper, thereby gating the subsequent broadcast of the Bitcoin lock transaction. On the UI side, changes have been made to reflect the new flow; the modal (for example, in `SwapSetupInflightPage.tsx`) now displays the swap details along with explicit action buttons that call `resolveApproval()` via RPC when clicked. The Redux store, selectors, and hooks like `usePendingPreBtcLockApproval()` have been updated to track and display these approval events. As a result, the overall functionality now requires the user to explicitly approve the swap offer before proceeding, ensuring they are aware of the swap's key parameters and that the locking of funds occurs only after their confirmation.
91 lines
No EOL
4.2 KiB
TypeScript
91 lines
No EOL
4.2 KiB
TypeScript
import { listen } from "@tauri-apps/api/event";
|
|
import { TauriSwapProgressEventWrapper, TauriContextStatusEvent, TauriLogEvent, BalanceResponse, TauriDatabaseStateEvent, TauriTimelockChangeEvent, TauriBackgroundRefundEvent, ApprovalRequest } from "models/tauriModel";
|
|
import { contextStatusEventReceived, receivedCliLog, rpcSetBalance, timelockChangeEventReceived, rpcSetBackgroundRefundState, approvalEventReceived } from "store/features/rpcSlice";
|
|
import { swapProgressEventReceived } from "store/features/swapSlice";
|
|
import logger from "utils/logger";
|
|
import { updatePublicRegistry, updateRates } from "./api";
|
|
import { checkContextAvailability, getSwapInfo, initializeContext, updateAllNodeStatuses } from "./rpc";
|
|
import { store } from "./store/storeRenderer";
|
|
|
|
// Update the public registry every 5 minutes
|
|
const PROVIDER_UPDATE_INTERVAL = 5 * 60 * 1_000;
|
|
|
|
// Update node statuses every 2 minutes
|
|
const STATUS_UPDATE_INTERVAL = 2 * 60 * 1_000;
|
|
|
|
// Update the exchange rate every 5 minutes
|
|
const UPDATE_RATE_INTERVAL = 5 * 60 * 1_000;
|
|
|
|
function setIntervalImmediate(callback: () => void, interval: number): void {
|
|
callback();
|
|
setInterval(callback, interval);
|
|
}
|
|
|
|
export async function setupBackgroundTasks(): Promise<void> {
|
|
// // Setup periodic fetch tasks
|
|
setIntervalImmediate(updatePublicRegistry, PROVIDER_UPDATE_INTERVAL);
|
|
setIntervalImmediate(updateAllNodeStatuses, STATUS_UPDATE_INTERVAL);
|
|
setIntervalImmediate(updateRates, UPDATE_RATE_INTERVAL);
|
|
|
|
// // Setup Tauri event listeners
|
|
|
|
// Check if the context is already available. This is to prevent unnecessary re-initialization
|
|
if (await checkContextAvailability()) {
|
|
store.dispatch(contextStatusEventReceived({ type: "Available" }));
|
|
} else {
|
|
// Warning: If we reload the page while the Context is being initialized, this function will throw an error
|
|
initializeContext().catch((e) => {
|
|
logger.error(e, "Failed to initialize context on page load. This might be because we reloaded the page while the context was being initialized");
|
|
// Wait a short time before retrying
|
|
setTimeout(() => {
|
|
initializeContext().catch((e) => {
|
|
logger.error(e, "Failed to initialize context even after retry");
|
|
});
|
|
}, 2000);
|
|
});
|
|
}
|
|
|
|
listen<TauriSwapProgressEventWrapper>("swap-progress-update", (event) => {
|
|
logger.info("Received swap progress event", event.payload);
|
|
store.dispatch(swapProgressEventReceived(event.payload));
|
|
});
|
|
|
|
listen<TauriContextStatusEvent>("context-init-progress-update", (event) => {
|
|
logger.info("Received context init progress event", event.payload);
|
|
store.dispatch(contextStatusEventReceived(event.payload));
|
|
});
|
|
|
|
listen<TauriLogEvent>("cli-log-emitted", (event) => {
|
|
store.dispatch(receivedCliLog(event.payload));
|
|
});
|
|
|
|
listen<BalanceResponse>("balance-change", (event) => {
|
|
logger.info("Received balance change event", event.payload);
|
|
store.dispatch(rpcSetBalance(event.payload.balance));
|
|
});
|
|
|
|
listen<TauriDatabaseStateEvent>("swap-database-state-update", (event) => {
|
|
logger.info("Received swap database state update event", event.payload);
|
|
getSwapInfo(event.payload.swap_id);
|
|
|
|
// This is ugly but it's the best we can do for now
|
|
// Sometimes we are too quick to fetch the swap info and the new state is not yet reflected
|
|
// in the database. So we wait a bit before fetching the new state
|
|
setTimeout(() => getSwapInfo(event.payload.swap_id), 3000);
|
|
});
|
|
|
|
listen<TauriTimelockChangeEvent>('timelock-change', (event) => {
|
|
logger.info('Received timelock change event', event.payload);
|
|
store.dispatch(timelockChangeEventReceived(event.payload));
|
|
})
|
|
|
|
listen<TauriBackgroundRefundEvent>('background-refund', (event) => {
|
|
logger.info('Received background refund event', event.payload);
|
|
store.dispatch(rpcSetBackgroundRefundState(event.payload));
|
|
})
|
|
|
|
listen<ApprovalRequest>("approval_event", (event) => {
|
|
logger.info("Received approval_event:", event.payload);
|
|
store.dispatch(approvalEventReceived(event.payload));
|
|
});
|
|
} |