mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-08-18 03:00:53 -04:00
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:
parent
d54f5c6c77
commit
cf641bc8bb
77 changed files with 2484 additions and 2167 deletions
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
export interface Binary {
|
||||
dirPath: string; // Path without filename appended
|
||||
fileName: string;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
155
src-gui/src/models/tauriModelExt.ts
Normal file
155
src-gui/src/models/tauriModelExt.ts
Normal 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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue