feat(gui): Migrate to Tauri events

- Replace Electron IPC with Tauri invoke() for API calls
- Implement TauriSwapProgressEvent for state management
- Remove IpcInvokeButton, replace with PromiseInvokeButton
- Update models: new tauriModel.ts, refactor rpcModel.ts
- Simplify SwapSlice state, remove processRunning flag
- Refactor SwapStatePage to use TauriSwapProgressEvent
- Update HistoryRow and HistoryRowActions for new data structures
- Remove unused Electron-specific components (e.g., RpcStatusAlert)
- Update dependencies: React 18, Material-UI v4 to v5
- Implement typeshare for Rust/TypeScript type synchronization
- Add BobStateName enum for more precise swap state tracking
- Refactor utility functions for Tauri compatibility
- Remove JSONStream and other Electron-specific dependencies
This commit is contained in:
binarybaron 2024-08-26 15:32:28 +02:00
parent d54f5c6c77
commit cf641bc8bb
No known key found for this signature in database
GPG key ID: 99B75D3E1476A26E
77 changed files with 2484 additions and 2167 deletions

View file

@ -18,396 +18,3 @@ export interface CliLog {
[index: string]: unknown;
}[];
}
export function isCliLog(log: unknown): log is CliLog {
if (log && typeof log === "object") {
return (
"timestamp" in (log as CliLog) &&
"level" in (log as CliLog) &&
"fields" in (log as CliLog) &&
typeof (log as CliLog).fields?.message === "string"
);
}
return false;
}
export interface CliLogStartedRpcServer extends CliLog {
fields: {
message: "Started RPC server";
addr: string;
};
}
export function isCliLogStartedRpcServer(
log: CliLog,
): log is CliLogStartedRpcServer {
return log.fields.message === "Started RPC server";
}
export interface CliLogReleasingSwapLockLog extends CliLog {
fields: {
message: "Releasing swap lock";
swap_id: string;
};
}
export function isCliLogReleasingSwapLockLog(
log: CliLog,
): log is CliLogReleasingSwapLockLog {
return log.fields.message === "Releasing swap lock";
}
export interface CliLogApiCallError extends CliLog {
fields: {
message: "API call resulted in an error";
err: string;
};
}
export function isCliLogApiCallError(log: CliLog): log is CliLogApiCallError {
return log.fields.message === "API call resulted in an error";
}
export interface CliLogAcquiringSwapLockLog extends CliLog {
fields: {
message: "Acquiring swap lock";
swap_id: string;
};
}
export function isCliLogAcquiringSwapLockLog(
log: CliLog,
): log is CliLogAcquiringSwapLockLog {
return log.fields.message === "Acquiring swap lock";
}
export interface CliLogReceivedQuote extends CliLog {
fields: {
message: "Received quote";
price: string;
minimum_amount: string;
maximum_amount: string;
};
}
export function isCliLogReceivedQuote(log: CliLog): log is CliLogReceivedQuote {
return log.fields.message === "Received quote";
}
export interface CliLogWaitingForBtcDeposit extends CliLog {
fields: {
message: "Waiting for Bitcoin deposit";
deposit_address: string;
min_deposit_until_swap_will_start: string;
max_deposit_until_maximum_amount_is_reached: string;
max_giveable: string;
minimum_amount: string;
maximum_amount: string;
min_bitcoin_lock_tx_fee: string;
price: string;
};
}
export function isCliLogWaitingForBtcDeposit(
log: CliLog,
): log is CliLogWaitingForBtcDeposit {
return log.fields.message === "Waiting for Bitcoin deposit";
}
export interface CliLogReceivedBtc extends CliLog {
fields: {
message: "Received Bitcoin";
max_giveable: string;
new_balance: string;
};
}
export function isCliLogReceivedBtc(log: CliLog): log is CliLogReceivedBtc {
return log.fields.message === "Received Bitcoin";
}
export interface CliLogDeterminedSwapAmount extends CliLog {
fields: {
message: "Determined swap amount";
amount: string;
fees: string;
};
}
export function isCliLogDeterminedSwapAmount(
log: CliLog,
): log is CliLogDeterminedSwapAmount {
return log.fields.message === "Determined swap amount";
}
export interface CliLogStartedSwap extends CliLog {
fields: {
message: "Starting new swap";
swap_id: string;
};
}
export function isCliLogStartedSwap(log: CliLog): log is CliLogStartedSwap {
return log.fields.message === "Starting new swap";
}
export interface CliLogPublishedBtcTx extends CliLog {
fields: {
message: "Published Bitcoin transaction";
txid: string;
kind: "lock" | "cancel" | "withdraw" | "refund";
};
}
export function isCliLogPublishedBtcTx(
log: CliLog,
): log is CliLogPublishedBtcTx {
return log.fields.message === "Published Bitcoin transaction";
}
export interface CliLogBtcTxFound extends CliLog {
fields: {
message: "Found relevant Bitcoin transaction";
txid: string;
status: string;
};
}
export function isCliLogBtcTxFound(log: CliLog): log is CliLogBtcTxFound {
return log.fields.message === "Found relevant Bitcoin transaction";
}
export interface CliLogBtcTxStatusChanged extends CliLog {
fields: {
message: "Bitcoin transaction status changed";
txid: string;
new_status: string;
};
}
export function isCliLogBtcTxStatusChanged(
log: CliLog,
): log is CliLogBtcTxStatusChanged {
return log.fields.message === "Bitcoin transaction status changed";
}
export interface CliLogAliceLockedXmr extends CliLog {
fields: {
message: "Alice locked Monero";
txid: string;
};
}
export function isCliLogAliceLockedXmr(
log: CliLog,
): log is CliLogAliceLockedXmr {
return log.fields.message === "Alice locked Monero";
}
export interface CliLogReceivedXmrLockTxConfirmation extends CliLog {
fields: {
message: "Received new confirmation for Monero lock tx";
txid: string;
seen_confirmations: string;
needed_confirmations: string;
};
}
export function isCliLogReceivedXmrLockTxConfirmation(
log: CliLog,
): log is CliLogReceivedXmrLockTxConfirmation {
return log.fields.message === "Received new confirmation for Monero lock tx";
}
export interface CliLogAdvancingState extends CliLog {
fields: {
message: "Advancing state";
state:
| "quote has been requested"
| "execution setup done"
| "btc is locked"
| "XMR lock transaction transfer proof received"
| "xmr is locked"
| "encrypted signature is sent"
| "btc is redeemed"
| "cancel timelock is expired"
| "btc is cancelled"
| "btc is refunded"
| "xmr is redeemed"
| "btc is punished"
| "safely aborted";
};
}
export function isCliLogAdvancingState(
log: CliLog,
): log is CliLogAdvancingState {
return log.fields.message === "Advancing state";
}
export interface CliLogRedeemedXmr extends CliLog {
fields: {
message: "Successfully transferred XMR to wallet";
monero_receive_address: string;
txid: string;
};
}
export function isCliLogRedeemedXmr(log: CliLog): log is CliLogRedeemedXmr {
return log.fields.message === "Successfully transferred XMR to wallet";
}
export interface YouHaveBeenPunishedCliLog extends CliLog {
fields: {
message: "You have been punished for not refunding in time";
};
}
export function isYouHaveBeenPunishedCliLog(
log: CliLog,
): log is YouHaveBeenPunishedCliLog {
return (
log.fields.message === "You have been punished for not refunding in time"
);
}
function getCliLogSpanAttribute<T>(log: CliLog, key: string): T | null {
const span = log.spans?.find((s) => s[key]);
if (!span) {
return null;
}
return span[key] as T;
}
export function getCliLogSpanSwapId(log: CliLog): string | null {
return getCliLogSpanAttribute<string>(log, "swap_id");
}
export function getCliLogSpanLogReferenceId(log: CliLog): string | null {
return (
getCliLogSpanAttribute<string>(log, "log_reference_id")?.replace(
/"/g,
"",
) || null
);
}
export function hasCliLogOneOfMultipleSpans(
log: CliLog,
spanNames: string[],
): boolean {
return log.spans?.some((s) => spanNames.includes(s.name)) ?? false;
}
export interface CliLogStartedSyncingMoneroWallet extends CliLog {
fields: {
message: "Syncing Monero wallet";
current_sync_height?: boolean;
};
}
export function isCliLogStartedSyncingMoneroWallet(
log: CliLog,
): log is CliLogStartedSyncingMoneroWallet {
return log.fields.message === "Syncing Monero wallet";
}
export interface CliLogFinishedSyncingMoneroWallet extends CliLog {
fields: {
message: "Synced Monero wallet";
};
}
export interface CliLogFailedToSyncMoneroWallet extends CliLog {
fields: {
message: "Failed to sync Monero wallet";
error: string;
};
}
export function isCliLogFailedToSyncMoneroWallet(
log: CliLog,
): log is CliLogFailedToSyncMoneroWallet {
return log.fields.message === "Failed to sync Monero wallet";
}
export function isCliLogFinishedSyncingMoneroWallet(
log: CliLog,
): log is CliLogFinishedSyncingMoneroWallet {
return log.fields.message === "Monero wallet synced";
}
export interface CliLogDownloadingMoneroWalletRpc extends CliLog {
fields: {
message: "Downloading monero-wallet-rpc";
progress: string;
size: string;
download_url: string;
};
}
export function isCliLogDownloadingMoneroWalletRpc(
log: CliLog,
): log is CliLogDownloadingMoneroWalletRpc {
return log.fields.message === "Downloading monero-wallet-rpc";
}
export interface CliLogStartedSyncingMoneroWallet extends CliLog {
fields: {
message: "Syncing Monero wallet";
current_sync_height?: boolean;
};
}
export interface CliLogDownloadingMoneroWalletRpc extends CliLog {
fields: {
message: "Downloading monero-wallet-rpc";
progress: string;
size: string;
download_url: string;
};
}
export interface CliLogGotNotificationForNewBlock extends CliLog {
fields: {
message: "Got notification for new block";
block_height: string;
};
}
export function isCliLogGotNotificationForNewBlock(
log: CliLog,
): log is CliLogGotNotificationForNewBlock {
return log.fields.message === "Got notification for new block";
}
export interface CliLogAttemptingToCooperativelyRedeemXmr extends CliLog {
fields: {
message: "Attempting to cooperatively redeem XMR after being punished";
};
}
export function isCliLogAttemptingToCooperativelyRedeemXmr(
log: CliLog,
): log is CliLogAttemptingToCooperativelyRedeemXmr {
return (
log.fields.message ===
"Attempting to cooperatively redeem XMR after being punished"
);
}
export interface CliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr
extends CliLog {
fields: {
message: "Alice has accepted our request to cooperatively redeem the XMR";
};
}
export function isCliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr(
log: CliLog,
): log is CliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr {
return (
log.fields.message ===
"Alice has accepted our request to cooperatively redeem the XMR"
);
}

View file

@ -1,4 +0,0 @@
export interface Binary {
dirPath: string; // Path without filename appended
fileName: string;
}

View file

@ -1,6 +1,3 @@
import { piconerosToXmr, satsToBtc } from "utils/conversionUtils";
import { exhaustiveGuard } from "utils/typescriptUtils";
export enum RpcMethod {
GET_BTC_BALANCE = "get_bitcoin_balance",
WITHDRAW_BTC = "withdraw_btc",
@ -110,227 +107,9 @@ export type SwapSellerInfo = {
addresses: string[];
};
export interface GetSwapInfoResponse {
swap_id: string;
completed: boolean;
seller: SwapSellerInfo;
start_date: string;
state_name: SwapStateName;
timelock: null | SwapTimelockInfo;
tx_lock_id: string;
tx_cancel_fee: number;
tx_refund_fee: number;
tx_lock_fee: number;
btc_amount: number;
xmr_amount: number;
btc_refund_address: string;
cancel_timelock: number;
punish_timelock: number;
}
export type MoneroRecoveryResponse = {
address: string;
spend_key: string;
view_key: string;
restore_height: number;
};
export interface BalanceBitcoinResponse {
balance: number;
}
export interface GetHistoryResponse {
swaps: [swapId: string, stateName: SwapStateName][];
}
export enum SwapStateName {
Started = "quote has been requested",
SwapSetupCompleted = "execution setup done",
BtcLocked = "btc is locked",
XmrLockProofReceived = "XMR lock transaction transfer proof received",
XmrLocked = "xmr is locked",
EncSigSent = "encrypted signature is sent",
BtcRedeemed = "btc is redeemed",
CancelTimelockExpired = "cancel timelock is expired",
BtcCancelled = "btc is cancelled",
BtcRefunded = "btc is refunded",
XmrRedeemed = "xmr is redeemed",
BtcPunished = "btc is punished",
SafelyAborted = "safely aborted",
}
export type SwapStateNameRunningSwap = Exclude<
SwapStateName,
| SwapStateName.Started
| SwapStateName.SwapSetupCompleted
| SwapStateName.BtcRefunded
| SwapStateName.BtcPunished
| SwapStateName.SafelyAborted
| SwapStateName.XmrRedeemed
>;
export type GetSwapInfoResponseRunningSwap = GetSwapInfoResponse & {
stateName: SwapStateNameRunningSwap;
};
export function isSwapStateNameRunningSwap(
state: SwapStateName,
): state is SwapStateNameRunningSwap {
return ![
SwapStateName.Started,
SwapStateName.SwapSetupCompleted,
SwapStateName.BtcRefunded,
SwapStateName.BtcPunished,
SwapStateName.SafelyAborted,
SwapStateName.XmrRedeemed,
].includes(state);
}
export type SwapStateNameCompletedSwap =
| SwapStateName.XmrRedeemed
| SwapStateName.BtcRefunded
| SwapStateName.BtcPunished
| SwapStateName.SafelyAborted;
export function isSwapStateNameCompletedSwap(
state: SwapStateName,
): state is SwapStateNameCompletedSwap {
return [
SwapStateName.XmrRedeemed,
SwapStateName.BtcRefunded,
SwapStateName.BtcPunished,
SwapStateName.SafelyAborted,
].includes(state);
}
export type SwapStateNamePossiblyCancellableSwap =
| SwapStateName.BtcLocked
| SwapStateName.XmrLockProofReceived
| SwapStateName.XmrLocked
| SwapStateName.EncSigSent
| SwapStateName.CancelTimelockExpired;
/**
Checks if a swap is in a state where it can possibly be cancelled
The following conditions must be met:
- The bitcoin must be locked
- The bitcoin must not be redeemed
- The bitcoin must not be cancelled
- The bitcoin must not be refunded
- The bitcoin must not be punished
See: https://github.com/comit-network/xmr-btc-swap/blob/7023e75bb51ab26dff4c8fcccdc855d781ca4b15/swap/src/cli/cancel.rs#L16-L35
*/
export function isSwapStateNamePossiblyCancellableSwap(
state: SwapStateName,
): state is SwapStateNamePossiblyCancellableSwap {
return [
SwapStateName.BtcLocked,
SwapStateName.XmrLockProofReceived,
SwapStateName.XmrLocked,
SwapStateName.EncSigSent,
SwapStateName.CancelTimelockExpired,
].includes(state);
}
export type SwapStateNamePossiblyRefundableSwap =
| SwapStateName.BtcLocked
| SwapStateName.XmrLockProofReceived
| SwapStateName.XmrLocked
| SwapStateName.EncSigSent
| SwapStateName.CancelTimelockExpired
| SwapStateName.BtcCancelled;
/**
Checks if a swap is in a state where it can possibly be refunded (meaning it's not impossible)
The following conditions must be met:
- The bitcoin must be locked
- The bitcoin must not be redeemed
- The bitcoin must not be refunded
- The bitcoin must not be punished
See: https://github.com/comit-network/xmr-btc-swap/blob/7023e75bb51ab26dff4c8fcccdc855d781ca4b15/swap/src/cli/refund.rs#L16-L34
*/
export function isSwapStateNamePossiblyRefundableSwap(
state: SwapStateName,
): state is SwapStateNamePossiblyRefundableSwap {
return [
SwapStateName.BtcLocked,
SwapStateName.XmrLockProofReceived,
SwapStateName.XmrLocked,
SwapStateName.EncSigSent,
SwapStateName.CancelTimelockExpired,
SwapStateName.BtcCancelled,
].includes(state);
}
/**
* Type guard for GetSwapInfoResponseRunningSwap
* "running" means the swap is in progress and not yet completed
* If a swap is not "running" it means it is either completed or no Bitcoin have been locked yet
* @param response
*/
export function isGetSwapInfoResponseRunningSwap(
response: GetSwapInfoResponse,
): response is GetSwapInfoResponseRunningSwap {
return isSwapStateNameRunningSwap(response.state_name);
}
export function isSwapMoneroRecoverable(swapStateName: SwapStateName): boolean {
return [SwapStateName.BtcRedeemed].includes(swapStateName);
}
// See https://github.com/comit-network/xmr-btc-swap/blob/50ae54141255e03dba3d2b09036b1caa4a63e5a3/swap/src/protocol/bob/state.rs#L55
export function getHumanReadableDbStateType(type: SwapStateName): string {
switch (type) {
case SwapStateName.Started:
return "Quote has been requested";
case SwapStateName.SwapSetupCompleted:
return "Swap has been initiated";
case SwapStateName.BtcLocked:
return "Bitcoin has been locked";
case SwapStateName.XmrLockProofReceived:
return "Monero lock transaction transfer proof has been received";
case SwapStateName.XmrLocked:
return "Monero has been locked";
case SwapStateName.EncSigSent:
return "Encrypted signature has been sent";
case SwapStateName.BtcRedeemed:
return "Bitcoin has been redeemed";
case SwapStateName.CancelTimelockExpired:
return "Cancel timelock has expired";
case SwapStateName.BtcCancelled:
return "Swap has been cancelled";
case SwapStateName.BtcRefunded:
return "Bitcoin has been refunded";
case SwapStateName.XmrRedeemed:
return "Monero has been redeemed";
case SwapStateName.BtcPunished:
return "Bitcoin has been punished";
case SwapStateName.SafelyAborted:
return "Swap has been safely aborted";
default:
return exhaustiveGuard(type);
}
}
export function getSwapTxFees(swap: GetSwapInfoResponse): number {
return satsToBtc(swap.tx_lock_fee);
}
export function getSwapBtcAmount(swap: GetSwapInfoResponse): number {
return satsToBtc(swap.btc_amount);
}
export function getSwapXmrAmount(swap: GetSwapInfoResponse): number {
return piconerosToXmr(swap.xmr_amount);
}
export function getSwapExchangeRate(swap: GetSwapInfoResponse): number {
const btcAmount = getSwapBtcAmount(swap);
const xmrAmount = getSwapXmrAmount(swap);
return btcAmount / xmrAmount;
}

View file

@ -1,218 +1,12 @@
import { CliLog, SwapSpawnType } from "./cliModel";
import { Provider } from "./apiModel";
import { TauriSwapProgressEvent } from "./tauriModel";
export interface SwapSlice {
state: SwapState | null;
logs: CliLog[];
processRunning: boolean;
provider: Provider | null;
spawnType: SwapSpawnType | null;
swapId: string | null;
}
export type MoneroWalletRpcUpdateState = {
progress: string;
downloadUrl: string;
};
export interface SwapState {
type: SwapStateType;
}
export enum SwapStateType {
INITIATED = "initiated",
RECEIVED_QUOTE = "received quote",
WAITING_FOR_BTC_DEPOSIT = "waiting for btc deposit",
STARTED = "started",
BTC_LOCK_TX_IN_MEMPOOL = "btc lock tx is in mempool",
XMR_LOCK_TX_IN_MEMPOOL = "xmr lock tx is in mempool",
XMR_LOCKED = "xmr is locked",
BTC_REDEEMED = "btc redeemed",
XMR_REDEEM_IN_MEMPOOL = "xmr redeem tx is in mempool",
PROCESS_EXITED = "process exited",
BTC_CANCELLED = "btc cancelled",
BTC_REFUNDED = "btc refunded",
BTC_PUNISHED = "btc punished",
ATTEMPTING_COOPERATIVE_REDEEM = "attempting cooperative redeem",
COOPERATIVE_REDEEM_REJECTED = "cooperative redeem rejected",
}
export function isSwapState(state?: SwapState | null): state is SwapState {
return state?.type != null;
}
export interface SwapStateInitiated extends SwapState {
type: SwapStateType.INITIATED;
}
export function isSwapStateInitiated(
state?: SwapState | null,
): state is SwapStateInitiated {
return state?.type === SwapStateType.INITIATED;
}
export interface SwapStateReceivedQuote extends SwapState {
type: SwapStateType.RECEIVED_QUOTE;
price: number;
minimumSwapAmount: number;
maximumSwapAmount: number;
}
export function isSwapStateReceivedQuote(
state?: SwapState | null,
): state is SwapStateReceivedQuote {
return state?.type === SwapStateType.RECEIVED_QUOTE;
}
export interface SwapStateWaitingForBtcDeposit extends SwapState {
type: SwapStateType.WAITING_FOR_BTC_DEPOSIT;
depositAddress: string;
maxGiveable: number;
minimumAmount: number;
maximumAmount: number;
minDeposit: number;
maxDeposit: number;
minBitcoinLockTxFee: number;
price: number | null;
}
export function isSwapStateWaitingForBtcDeposit(
state?: SwapState | null,
): state is SwapStateWaitingForBtcDeposit {
return state?.type === SwapStateType.WAITING_FOR_BTC_DEPOSIT;
}
export interface SwapStateStarted extends SwapState {
type: SwapStateType.STARTED;
txLockDetails: {
amount: number;
fees: number;
state: {
curr: TauriSwapProgressEvent;
prev: TauriSwapProgressEvent | null;
swapId: string;
} | null;
}
export function isSwapStateStarted(
state?: SwapState | null,
): state is SwapStateStarted {
return state?.type === SwapStateType.STARTED;
}
export interface SwapStateBtcLockInMempool extends SwapState {
type: SwapStateType.BTC_LOCK_TX_IN_MEMPOOL;
bobBtcLockTxId: string;
bobBtcLockTxConfirmations: number;
}
export function isSwapStateBtcLockInMempool(
state?: SwapState | null,
): state is SwapStateBtcLockInMempool {
return state?.type === SwapStateType.BTC_LOCK_TX_IN_MEMPOOL;
}
export interface SwapStateXmrLockInMempool extends SwapState {
type: SwapStateType.XMR_LOCK_TX_IN_MEMPOOL;
aliceXmrLockTxId: string;
aliceXmrLockTxConfirmations: number;
}
export function isSwapStateXmrLockInMempool(
state?: SwapState | null,
): state is SwapStateXmrLockInMempool {
return state?.type === SwapStateType.XMR_LOCK_TX_IN_MEMPOOL;
}
export interface SwapStateXmrLocked extends SwapState {
type: SwapStateType.XMR_LOCKED;
}
export function isSwapStateXmrLocked(
state?: SwapState | null,
): state is SwapStateXmrLocked {
return state?.type === SwapStateType.XMR_LOCKED;
}
export interface SwapStateBtcRedemeed extends SwapState {
type: SwapStateType.BTC_REDEEMED;
}
export function isSwapStateBtcRedemeed(
state?: SwapState | null,
): state is SwapStateBtcRedemeed {
return state?.type === SwapStateType.BTC_REDEEMED;
}
export interface SwapStateAttemptingCooperativeRedeeem extends SwapState {
type: SwapStateType.ATTEMPTING_COOPERATIVE_REDEEM;
}
export function isSwapStateAttemptingCooperativeRedeeem(
state?: SwapState | null,
): state is SwapStateAttemptingCooperativeRedeeem {
return state?.type === SwapStateType.ATTEMPTING_COOPERATIVE_REDEEM;
}
export interface SwapStateCooperativeRedeemRejected extends SwapState {
type: SwapStateType.COOPERATIVE_REDEEM_REJECTED;
reason: string;
}
export function isSwapStateCooperativeRedeemRejected(
state?: SwapState | null,
): state is SwapStateCooperativeRedeemRejected {
return state?.type === SwapStateType.COOPERATIVE_REDEEM_REJECTED;
}
export interface SwapStateXmrRedeemInMempool extends SwapState {
type: SwapStateType.XMR_REDEEM_IN_MEMPOOL;
bobXmrRedeemTxId: string;
bobXmrRedeemAddress: string;
}
export function isSwapStateXmrRedeemInMempool(
state?: SwapState | null,
): state is SwapStateXmrRedeemInMempool {
return state?.type === SwapStateType.XMR_REDEEM_IN_MEMPOOL;
}
export interface SwapStateBtcCancelled extends SwapState {
type: SwapStateType.BTC_CANCELLED;
btcCancelTxId: string;
}
export function isSwapStateBtcCancelled(
state?: SwapState | null,
): state is SwapStateBtcCancelled {
return state?.type === SwapStateType.BTC_CANCELLED;
}
export interface SwapStateBtcRefunded extends SwapState {
type: SwapStateType.BTC_REFUNDED;
bobBtcRefundTxId: string;
}
export function isSwapStateBtcRefunded(
state?: SwapState | null,
): state is SwapStateBtcRefunded {
return state?.type === SwapStateType.BTC_REFUNDED;
}
export interface SwapStateBtcPunished extends SwapState {
type: SwapStateType.BTC_PUNISHED;
}
export function isSwapStateBtcPunished(
state?: SwapState | null,
): state is SwapStateBtcPunished {
return state?.type === SwapStateType.BTC_PUNISHED;
}
export interface SwapStateProcessExited extends SwapState {
type: SwapStateType.PROCESS_EXITED;
prevState: SwapState | null;
rpcError: string | null;
}
export function isSwapStateProcessExited(
state?: SwapState | null,
): state is SwapStateProcessExited {
return state?.type === SwapStateType.PROCESS_EXITED;
logs: CliLog[];
spawnType: SwapSpawnType | null;
}

View file

@ -0,0 +1,155 @@
import {
ExpiredTimelocks,
GetSwapInfoResponse,
TauriSwapProgressEvent,
} from "./tauriModel";
export type TauriSwapProgressEventContent<
T extends TauriSwapProgressEvent["type"],
> = Extract<TauriSwapProgressEvent, { type: T }>["content"];
// See /swap/src/protocol/bob/state.rs#L57
// TODO: Replace this with a typeshare definition
export enum BobStateName {
Started = "quote has been requested",
SwapSetupCompleted = "execution setup done",
BtcLocked = "btc is locked",
XmrLockProofReceived = "XMR lock transaction transfer proof received",
XmrLocked = "xmr is locked",
EncSigSent = "encrypted signature is sent",
BtcRedeemed = "btc is redeemed",
CancelTimelockExpired = "cancel timelock is expired",
BtcCancelled = "btc is cancelled",
BtcRefunded = "btc is refunded",
XmrRedeemed = "xmr is redeemed",
BtcPunished = "btc is punished",
SafelyAborted = "safely aborted",
}
// TODO: This is a temporary solution until we have a typeshare definition for BobStateName
export type GetSwapInfoResponseExt = GetSwapInfoResponse & {
state_name: BobStateName;
};
export type TimelockNone = Extract<ExpiredTimelocks, { type: "None" }>;
export type TimelockCancel = Extract<ExpiredTimelocks, { type: "Cancel" }>;
export type TimelockPunish = Extract<ExpiredTimelocks, { type: "Punish" }>;
export type BobStateNameRunningSwap = Exclude<
BobStateName,
| BobStateName.Started
| BobStateName.SwapSetupCompleted
| BobStateName.BtcRefunded
| BobStateName.BtcPunished
| BobStateName.SafelyAborted
| BobStateName.XmrRedeemed
>;
export type GetSwapInfoResponseExtRunningSwap = GetSwapInfoResponseExt & {
stateName: BobStateNameRunningSwap;
};
export function isBobStateNameRunningSwap(
state: BobStateName,
): state is BobStateNameRunningSwap {
return ![
BobStateName.Started,
BobStateName.SwapSetupCompleted,
BobStateName.BtcRefunded,
BobStateName.BtcPunished,
BobStateName.SafelyAborted,
BobStateName.XmrRedeemed,
].includes(state);
}
export type BobStateNameCompletedSwap =
| BobStateName.XmrRedeemed
| BobStateName.BtcRefunded
| BobStateName.BtcPunished
| BobStateName.SafelyAborted;
export function isBobStateNameCompletedSwap(
state: BobStateName,
): state is BobStateNameCompletedSwap {
return [
BobStateName.XmrRedeemed,
BobStateName.BtcRefunded,
BobStateName.BtcPunished,
BobStateName.SafelyAborted,
].includes(state);
}
export type BobStateNamePossiblyCancellableSwap =
| BobStateName.BtcLocked
| BobStateName.XmrLockProofReceived
| BobStateName.XmrLocked
| BobStateName.EncSigSent
| BobStateName.CancelTimelockExpired;
/**
Checks if a swap is in a state where it can possibly be cancelled
The following conditions must be met:
- The bitcoin must be locked
- The bitcoin must not be redeemed
- The bitcoin must not be cancelled
- The bitcoin must not be refunded
- The bitcoin must not be punished
See: https://github.com/comit-network/xmr-btc-swap/blob/7023e75bb51ab26dff4c8fcccdc855d781ca4b15/swap/src/cli/cancel.rs#L16-L35
*/
export function isBobStateNamePossiblyCancellableSwap(
state: BobStateName,
): state is BobStateNamePossiblyCancellableSwap {
return [
BobStateName.BtcLocked,
BobStateName.XmrLockProofReceived,
BobStateName.XmrLocked,
BobStateName.EncSigSent,
BobStateName.CancelTimelockExpired,
].includes(state);
}
export type BobStateNamePossiblyRefundableSwap =
| BobStateName.BtcLocked
| BobStateName.XmrLockProofReceived
| BobStateName.XmrLocked
| BobStateName.EncSigSent
| BobStateName.CancelTimelockExpired
| BobStateName.BtcCancelled;
/**
Checks if a swap is in a state where it can possibly be refunded (meaning it's not impossible)
The following conditions must be met:
- The bitcoin must be locked
- The bitcoin must not be redeemed
- The bitcoin must not be refunded
- The bitcoin must not be punished
See: https://github.com/comit-network/xmr-btc-swap/blob/7023e75bb51ab26dff4c8fcccdc855d781ca4b15/swap/src/cli/refund.rs#L16-L34
*/
export function isBobStateNamePossiblyRefundableSwap(
state: BobStateName,
): state is BobStateNamePossiblyRefundableSwap {
return [
BobStateName.BtcLocked,
BobStateName.XmrLockProofReceived,
BobStateName.XmrLocked,
BobStateName.EncSigSent,
BobStateName.CancelTimelockExpired,
BobStateName.BtcCancelled,
].includes(state);
}
/**
* Type guard for GetSwapInfoResponseExt
* "running" means the swap is in progress and not yet completed
* If a swap is not "running" it means it is either completed or no Bitcoin have been locked yet
* @param response
*/
export function isGetSwapInfoResponseRunningSwap(
response: GetSwapInfoResponseExt,
): response is GetSwapInfoResponseExtRunningSwap {
return isBobStateNameRunningSwap(response.state_name);
}