diff --git a/Cargo.lock b/Cargo.lock index 7cf215c3..1472fe53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1601,15 +1601,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", -] - [[package]] name = "enum-as-inner" version = "0.3.4" @@ -2763,25 +2754,6 @@ dependencies = [ "nom", ] -[[package]] -name = "is-docker" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" -dependencies = [ - "once_cell", -] - -[[package]] -name = "is-wsl" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" -dependencies = [ - "is-docker", - "once_cell", -] - [[package]] name = "itertools" version = "0.10.5" @@ -4089,17 +4061,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" -[[package]] -name = "open" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d2c909a3fce3bd80efef4cd1c6c056bd9376a8fe06fcfdbebaf32cb485a7e37" -dependencies = [ - "is-wsl", - "libc", - "pathdiff", -] - [[package]] name = "open-metrics-client" version = "0.14.0" @@ -4135,16 +4096,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "os_pipe" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d73ba8daf8fac13b0501d1abeddcfe21ba7401ada61a819144b6c2a4f32209" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "overload" version = "0.1.1" @@ -5932,16 +5883,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shared_child" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d94659ad3c2137fef23ae75b03d5241d633f8acded53d672decfa0e6e0caef" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "shell-words" version = "1.1.0" @@ -6729,44 +6670,6 @@ dependencies = [ "tauri-utils", ] -[[package]] -name = "tauri-plugin" -version = "2.0.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51a5c65ab8536a7e27b70ecbb0713ab42e8508acd9af1bc4a0817ccf7caf3165" -dependencies = [ - "anyhow", - "glob", - "plist", - "schemars", - "serde", - "serde_json", - "tauri-utils", - "toml 0.8.2", - "walkdir", -] - -[[package]] -name = "tauri-plugin-shell" -version = "2.0.0-rc.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9209f6c32caec61e156a5616f7d80ba7683ca4a0a5641cbe5d3086ab371aaab2" -dependencies = [ - "encoding_rs", - "log", - "open", - "os_pipe", - "regex", - "schemars", - "serde", - "serde_json", - "shared_child", - "tauri", - "tauri-plugin", - "thiserror", - "tokio", -] - [[package]] name = "tauri-runtime" version = "2.0.0-rc.1" @@ -7683,7 +7586,7 @@ dependencies = [ "swap", "tauri", "tauri-build", - "tauri-plugin-shell", + "uuid", ] [[package]] @@ -7731,9 +7634,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.9.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" dependencies = [ "getrandom 0.2.15", "serde", diff --git a/src-gui/src/models/rpcModel.ts b/src-gui/src/models/rpcModel.ts index 506fb399..b1524b90 100644 --- a/src-gui/src/models/rpcModel.ts +++ b/src-gui/src/models/rpcModel.ts @@ -1,24 +1,24 @@ -import { piconerosToXmr, satsToBtc } from 'utils/conversionUtils'; -import { exhaustiveGuard } from 'utils/typescriptUtils'; +import { piconerosToXmr, satsToBtc } from "utils/conversionUtils"; +import { exhaustiveGuard } from "utils/typescriptUtils"; export enum RpcMethod { - GET_BTC_BALANCE = 'get_bitcoin_balance', - WITHDRAW_BTC = 'withdraw_btc', - BUY_XMR = 'buy_xmr', - RESUME_SWAP = 'resume_swap', - LIST_SELLERS = 'list_sellers', - CANCEL_REFUND_SWAP = 'cancel_refund_swap', - GET_SWAP_INFO = 'get_swap_info', - SUSPEND_CURRENT_SWAP = 'suspend_current_swap', - GET_HISTORY = 'get_history', - GET_MONERO_RECOVERY_KEYS = 'get_monero_recovery_info', + GET_BTC_BALANCE = "get_bitcoin_balance", + WITHDRAW_BTC = "withdraw_btc", + BUY_XMR = "buy_xmr", + RESUME_SWAP = "resume_swap", + LIST_SELLERS = "list_sellers", + CANCEL_REFUND_SWAP = "cancel_refund_swap", + GET_SWAP_INFO = "get_swap_info", + SUSPEND_CURRENT_SWAP = "suspend_current_swap", + GET_HISTORY = "get_history", + GET_MONERO_RECOVERY_KEYS = "get_monero_recovery_info", } export enum RpcProcessStateType { - STARTED = 'starting...', - LISTENING_FOR_CONNECTIONS = 'running', - EXITED = 'exited', - NOT_STARTED = 'not started', + STARTED = "starting...", + LISTENING_FOR_CONNECTIONS = "running", + EXITED = "exited", + NOT_STARTED = "not started", } export type RawRpcResponseSuccess = { @@ -38,13 +38,13 @@ export type RawRpcResponse = RawRpcResponseSuccess | RawRpcResponseError; export function isSuccessResponse( response: RawRpcResponse, ): response is RawRpcResponseSuccess { - return 'result' in response; + return "result" in response; } export function isErrorResponse( response: RawRpcResponse, ): response is RawRpcResponseError { - return 'error' in response; + return "error" in response; } export interface RpcSellerStatus { @@ -56,7 +56,7 @@ export interface RpcSellerStatus { max_quantity: number; }; } - | 'Unreachable'; + | "Unreachable"; multiaddr: string; } @@ -80,7 +80,7 @@ export type SwapTimelockInfoCancelled = { }; }; -export type SwapTimelockInfoPunished = 'Punish'; +export type SwapTimelockInfoPunished = "Punish"; export type SwapTimelockInfo = | SwapTimelockInfoNone @@ -90,19 +90,19 @@ export type SwapTimelockInfo = export function isSwapTimelockInfoNone( info: SwapTimelockInfo, ): info is SwapTimelockInfoNone { - return typeof info === 'object' && 'None' in info; + return typeof info === "object" && "None" in info; } export function isSwapTimelockInfoCancelled( info: SwapTimelockInfo, ): info is SwapTimelockInfoCancelled { - return typeof info === 'object' && 'Cancel' in info; + return typeof info === "object" && "Cancel" in info; } export function isSwapTimelockInfoPunished( info: SwapTimelockInfo, ): info is SwapTimelockInfoPunished { - return info === 'Punish'; + return info === "Punish"; } export type SwapSellerInfo = { @@ -111,21 +111,21 @@ export type SwapSellerInfo = { }; export interface GetSwapInfoResponse { - swapId: string; + swap_id: string; completed: boolean; seller: SwapSellerInfo; - startDate: string; - stateName: SwapStateName; + start_date: string; + state_name: SwapStateName; timelock: null | SwapTimelockInfo; - txLockId: string; - txCancelFee: number; - txRefundFee: number; - txLockFee: number; - btcAmount: number; - xmrAmount: number; - btcRefundAddress: string; - cancelTimelock: number; - punishTimelock: number; + 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 = { @@ -144,19 +144,19 @@ export interface GetHistoryResponse { } 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', + 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< @@ -275,7 +275,7 @@ export function isSwapStateNamePossiblyRefundableSwap( export function isGetSwapInfoResponseRunningSwap( response: GetSwapInfoResponse, ): response is GetSwapInfoResponseRunningSwap { - return isSwapStateNameRunningSwap(response.stateName); + return isSwapStateNameRunningSwap(response.state_name); } export function isSwapMoneroRecoverable(swapStateName: SwapStateName): boolean { @@ -286,46 +286,46 @@ export function isSwapMoneroRecoverable(swapStateName: SwapStateName): boolean { export function getHumanReadableDbStateType(type: SwapStateName): string { switch (type) { case SwapStateName.Started: - return 'Quote has been requested'; + return "Quote has been requested"; case SwapStateName.SwapSetupCompleted: - return 'Swap has been initiated'; + return "Swap has been initiated"; case SwapStateName.BtcLocked: - return 'Bitcoin has been locked'; + return "Bitcoin has been locked"; case SwapStateName.XmrLockProofReceived: - return 'Monero lock transaction transfer proof has been received'; + return "Monero lock transaction transfer proof has been received"; case SwapStateName.XmrLocked: - return 'Monero has been locked'; + return "Monero has been locked"; case SwapStateName.EncSigSent: - return 'Encrypted signature has been sent'; + return "Encrypted signature has been sent"; case SwapStateName.BtcRedeemed: - return 'Bitcoin has been redeemed'; + return "Bitcoin has been redeemed"; case SwapStateName.CancelTimelockExpired: - return 'Cancel timelock has expired'; + return "Cancel timelock has expired"; case SwapStateName.BtcCancelled: - return 'Swap has been cancelled'; + return "Swap has been cancelled"; case SwapStateName.BtcRefunded: - return 'Bitcoin has been refunded'; + return "Bitcoin has been refunded"; case SwapStateName.XmrRedeemed: - return 'Monero has been redeemed'; + return "Monero has been redeemed"; case SwapStateName.BtcPunished: - return 'Bitcoin has been punished'; + return "Bitcoin has been punished"; case SwapStateName.SafelyAborted: - return 'Swap has been safely aborted'; + return "Swap has been safely aborted"; default: return exhaustiveGuard(type); } } export function getSwapTxFees(swap: GetSwapInfoResponse): number { - return satsToBtc(swap.txLockFee); + return satsToBtc(swap.tx_lock_fee); } export function getSwapBtcAmount(swap: GetSwapInfoResponse): number { - return satsToBtc(swap.btcAmount); + return satsToBtc(swap.btc_amount); } export function getSwapXmrAmount(swap: GetSwapInfoResponse): number { - return piconerosToXmr(swap.xmrAmount); + return piconerosToXmr(swap.xmr_amount); } export function getSwapExchangeRate(swap: GetSwapInfoResponse): number { diff --git a/src-gui/src/renderer/components/alert/SwapMightBeCancelledAlert.tsx b/src-gui/src/renderer/components/alert/SwapMightBeCancelledAlert.tsx index 4bd2cd62..eadb78b1 100644 --- a/src-gui/src/renderer/components/alert/SwapMightBeCancelledAlert.tsx +++ b/src-gui/src/renderer/components/alert/SwapMightBeCancelledAlert.tsx @@ -1,97 +1,103 @@ -import { makeStyles } from '@material-ui/core'; -import { Alert, AlertTitle } from '@material-ui/lab'; -import { useActiveSwapInfo } from 'store/hooks'; +import { makeStyles } from "@material-ui/core"; +import { Alert, AlertTitle } from "@material-ui/lab"; +import { useActiveSwapInfo } from "store/hooks"; import { - isSwapTimelockInfoCancelled, - isSwapTimelockInfoNone, -} from 'models/rpcModel'; -import HumanizedBitcoinBlockDuration from '../other/HumanizedBitcoinBlockDuration'; + isSwapTimelockInfoCancelled, + isSwapTimelockInfoNone, +} from "models/rpcModel"; +import HumanizedBitcoinBlockDuration from "../other/HumanizedBitcoinBlockDuration"; const useStyles = makeStyles((theme) => ({ - outer: { - marginBottom: theme.spacing(1), - }, - list: { - margin: theme.spacing(0.25), - }, + outer: { + marginBottom: theme.spacing(1), + }, + list: { + margin: theme.spacing(0.25), + }, })); export default function SwapMightBeCancelledAlert({ - bobBtcLockTxConfirmations, + bobBtcLockTxConfirmations, }: { - bobBtcLockTxConfirmations: number; + bobBtcLockTxConfirmations: number; }) { - const classes = useStyles(); - const swap = useActiveSwapInfo(); + const classes = useStyles(); + const swap = useActiveSwapInfo(); - if ( - bobBtcLockTxConfirmations < 5 || - swap === null || - swap.timelock === null - ) { - return <>; - } + if ( + bobBtcLockTxConfirmations < 5 || + swap === null || + swap.timelock === null + ) { + return <>; + } - const { timelock } = swap; - const punishTimelockOffset = swap.punishTimelock; + const { timelock } = swap; + const punishTimelockOffset = swap.punish_timelock; - return ( - - Be careful! - The swap provider has taken a long time to lock their Monero. This might - mean that: -
    -
  • - There is a technical issue that prevents them from locking their funds -
  • -
  • They are a malicious actor (unlikely)
  • -
-
- There is still hope for the swap to be successful but you have to be extra - careful. Regardless of why it has taken them so long, it is important that - you refund the swap within the required time period if the swap is not - completed. If you fail to to do so, you will be punished and lose your - money. -
    - {isSwapTimelockInfoNone(timelock) && ( - <> -
  • - - You will be able to refund in about{' '} - - -
  • + return ( + + Be careful! + The swap provider has taken a long time to lock their Monero. This + might mean that: +
      +
    • + There is a technical issue that prevents them from locking + their funds +
    • +
    • They are a malicious actor (unlikely)
    • +
    +
    + There is still hope for the swap to be successful but you have to be + extra careful. Regardless of why it has taken them so long, it is + important that you refund the swap within the required time period + if the swap is not completed. If you fail to to do so, you will be + punished and lose your money. +
      + {isSwapTimelockInfoNone(timelock) && ( + <> +
    • + + You will be able to refund in about{" "} + + +
    • -
    • - - If you have not refunded or completed the swap in about{' '} - - , you will lose your funds. - -
    • - - )} - {isSwapTimelockInfoCancelled(timelock) && ( -
    • - - If you have not refunded or completed the swap in about{' '} - - , you will lose your funds. - -
    • - )} -
    • - As long as you see this screen, the swap will be refunded - automatically when the time comes. If this fails, you have to manually - refund by navigating to the History page. -
    • -
    -
    - ); +
  • + + If you have not refunded or completed the swap + in about{" "} + + , you will lose your funds. + +
  • + + )} + {isSwapTimelockInfoCancelled(timelock) && ( +
  • + + If you have not refunded or completed the swap in + about{" "} + + , you will lose your funds. + +
  • + )} +
  • + As long as you see this screen, the swap will be refunded + automatically when the time comes. If this fails, you have + to manually refund by navigating to the History page. +
  • +
+
+ ); } diff --git a/src-gui/src/renderer/components/alert/SwapStatusAlert.tsx b/src-gui/src/renderer/components/alert/SwapStatusAlert.tsx index 91fcb06b..8ee6a109 100644 --- a/src-gui/src/renderer/components/alert/SwapStatusAlert.tsx +++ b/src-gui/src/renderer/components/alert/SwapStatusAlert.tsx @@ -1,35 +1,35 @@ -import { Alert, AlertTitle } from '@material-ui/lab/'; -import { Box, makeStyles } from '@material-ui/core'; -import { ReactNode } from 'react'; -import { exhaustiveGuard } from 'utils/typescriptUtils'; +import { Alert, AlertTitle } from "@material-ui/lab/"; +import { Box, makeStyles } from "@material-ui/core"; +import { ReactNode } from "react"; +import { exhaustiveGuard } from "utils/typescriptUtils"; import { - SwapCancelRefundButton, - SwapResumeButton, -} from '../pages/history/table/HistoryRowActions'; -import HumanizedBitcoinBlockDuration from '../other/HumanizedBitcoinBlockDuration'; + SwapCancelRefundButton, + SwapResumeButton, +} from "../pages/history/table/HistoryRowActions"; +import HumanizedBitcoinBlockDuration from "../other/HumanizedBitcoinBlockDuration"; import { - GetSwapInfoResponse, - GetSwapInfoResponseRunningSwap, - isGetSwapInfoResponseRunningSwap, - isSwapTimelockInfoCancelled, - isSwapTimelockInfoNone, - isSwapTimelockInfoPunished, - SwapStateName, - SwapTimelockInfoCancelled, - SwapTimelockInfoNone, -} from '../../../models/rpcModel'; -import { SwapMoneroRecoveryButton } from '../pages/history/table/SwapMoneroRecoveryButton'; + GetSwapInfoResponse, + GetSwapInfoResponseRunningSwap, + isGetSwapInfoResponseRunningSwap, + isSwapTimelockInfoCancelled, + isSwapTimelockInfoNone, + isSwapTimelockInfoPunished, + SwapStateName, + SwapTimelockInfoCancelled, + SwapTimelockInfoNone, +} from "../../../models/rpcModel"; +import { SwapMoneroRecoveryButton } from "../pages/history/table/SwapMoneroRecoveryButton"; const useStyles = makeStyles({ - box: { - display: 'flex', - flexDirection: 'column', - gap: '0.5rem', - }, - list: { - padding: '0px', - margin: '0px', - }, + box: { + display: "flex", + flexDirection: "column", + gap: "0.5rem", + }, + list: { + padding: "0px", + margin: "0px", + }, }); /** @@ -38,15 +38,15 @@ const useStyles = makeStyles({ * @returns JSX.Element */ const MessageList = ({ messages }: { messages: ReactNode[] }) => { - const classes = useStyles(); - return ( -
    - {messages.map((msg, i) => ( - // eslint-disable-next-line react/no-array-index-key -
  • {msg}
  • - ))} -
- ); + const classes = useStyles(); + return ( +
    + {messages.map((msg, i) => ( + // eslint-disable-next-line react/no-array-index-key +
  • {msg}
  • + ))} +
+ ); }; /** @@ -55,20 +55,24 @@ const MessageList = ({ messages }: { messages: ReactNode[] }) => { * @returns JSX.Element */ const BitcoinRedeemedStateAlert = ({ swap }: { swap: GetSwapInfoResponse }) => { - const classes = useStyles(); - return ( - - - - - ); + const classes = useStyles(); + return ( + + + + + ); }; /** @@ -78,28 +82,31 @@ const BitcoinRedeemedStateAlert = ({ swap }: { swap: GetSwapInfoResponse }) => { * @returns JSX.Element */ const BitcoinLockedNoTimelockExpiredStateAlert = ({ - timelock, - punishTimelockOffset, + timelock, + punishTimelockOffset, }: { - timelock: SwapTimelockInfoNone; - punishTimelockOffset: number; + timelock: SwapTimelockInfoNone; + punishTimelockOffset: number; }) => ( - - Your Bitcoin is locked. If the swap is not completed in approximately{' '} - , - you need to refund - , - <> - You will lose your funds if you do not refund or complete the swap - within{' '} - - , - ]} - /> + + Your Bitcoin is locked. If the swap is not completed in + approximately{" "} + + , you need to refund + , + <> + You will lose your funds if you do not refund or complete the + swap within{" "} + + , + ]} + /> ); /** @@ -110,30 +117,34 @@ const BitcoinLockedNoTimelockExpiredStateAlert = ({ * @returns JSX.Element */ const BitcoinPossiblyCancelledAlert = ({ - swap, - timelock, + swap, + timelock, }: { - swap: GetSwapInfoResponse; - timelock: SwapTimelockInfoCancelled; + swap: GetSwapInfoResponse; + timelock: SwapTimelockInfoCancelled; }) => { - const classes = useStyles(); - return ( - - - You will lose your funds if you do not refund within{' '} - + + You will lose your funds if you do not refund within{" "} + + , + ]} /> - , - ]} - /> - - - ); + + + ); }; /** @@ -141,7 +152,7 @@ const BitcoinPossiblyCancelledAlert = ({ * @returns JSX.Element */ const ImmediateActionAlert = () => ( - <>Resume the swap immediately to avoid losing your funds + <>Resume the swap immediately to avoid losing your funds ); /** @@ -150,55 +161,55 @@ const ImmediateActionAlert = () => ( * @returns JSX.Element | null */ function SwapAlertStatusText({ - swap, + swap, }: { - swap: GetSwapInfoResponseRunningSwap; + swap: GetSwapInfoResponseRunningSwap; }) { - switch (swap.stateName) { - // This is the state where the swap is safe because the other party has redeemed the Bitcoin - // It cannot be punished anymore - case SwapStateName.BtcRedeemed: - return ; + switch (swap.state_name) { + // This is the state where the swap is safe because the other party has redeemed the Bitcoin + // It cannot be punished anymore + case SwapStateName.BtcRedeemed: + return ; - // These are states that are at risk of punishment because the Bitcoin have been locked - // but has not been redeemed yet by the other party - case SwapStateName.BtcLocked: - case SwapStateName.XmrLockProofReceived: - case SwapStateName.XmrLocked: - case SwapStateName.EncSigSent: - case SwapStateName.CancelTimelockExpired: - case SwapStateName.BtcCancelled: - if (swap.timelock !== null) { - if (isSwapTimelockInfoNone(swap.timelock)) { - return ( - - ); - } + // These are states that are at risk of punishment because the Bitcoin have been locked + // but has not been redeemed yet by the other party + case SwapStateName.BtcLocked: + case SwapStateName.XmrLockProofReceived: + case SwapStateName.XmrLocked: + case SwapStateName.EncSigSent: + case SwapStateName.CancelTimelockExpired: + case SwapStateName.BtcCancelled: + if (swap.timelock !== null) { + if (isSwapTimelockInfoNone(swap.timelock)) { + return ( + + ); + } - if (isSwapTimelockInfoCancelled(swap.timelock)) { - return ( - - ); - } + if (isSwapTimelockInfoCancelled(swap.timelock)) { + return ( + + ); + } - if (isSwapTimelockInfoPunished(swap.timelock)) { - return ; - } + if (isSwapTimelockInfoPunished(swap.timelock)) { + return ; + } - // We have covered all possible timelock states above - // If we reach this point, it means we have missed a case - return exhaustiveGuard(swap.timelock); - } - return ; - default: - return exhaustiveGuard(swap.stateName); - } + // We have covered all possible timelock states above + // If we reach this point, it means we have missed a case + return exhaustiveGuard(swap.timelock); + } + return ; + default: + return exhaustiveGuard(swap.state_name); + } } /** @@ -207,27 +218,27 @@ function SwapAlertStatusText({ * @returns JSX.Element | null */ export default function SwapStatusAlert({ - swap, + swap, }: { - swap: GetSwapInfoResponse; + swap: GetSwapInfoResponse; }): JSX.Element | null { - // If the swap is not running, there is no need to display the alert - // This is either because the swap is finished or has not started yet (e.g. in the setup phase, no Bitcoin locked) - if (!isGetSwapInfoResponseRunningSwap(swap)) { - return null; - } + // If the swap is not running, there is no need to display the alert + // This is either because the swap is finished or has not started yet (e.g. in the setup phase, no Bitcoin locked) + if (!isGetSwapInfoResponseRunningSwap(swap)) { + return null; + } - return ( - } - variant="filled" - > - - Swap {swap.swapId.substring(0, 5)}... is unfinished - - - - ); + return ( + } + variant="filled" + > + + Swap {swap.swap_id.substring(0, 5)}... is unfinished + + + + ); } diff --git a/src-gui/src/renderer/components/alert/SwapTxLockAlertsBox.tsx b/src-gui/src/renderer/components/alert/SwapTxLockAlertsBox.tsx index 055573e7..323db6ee 100644 --- a/src-gui/src/renderer/components/alert/SwapTxLockAlertsBox.tsx +++ b/src-gui/src/renderer/components/alert/SwapTxLockAlertsBox.tsx @@ -1,28 +1,28 @@ -import { Box, makeStyles } from '@material-ui/core'; -import { useSwapInfosSortedByDate } from 'store/hooks'; -import SwapStatusAlert from './SwapStatusAlert'; +import { Box, makeStyles } from "@material-ui/core"; +import { useSwapInfosSortedByDate } from "store/hooks"; +import SwapStatusAlert from "./SwapStatusAlert"; const useStyles = makeStyles((theme) => ({ - outer: { - display: 'flex', - flexDirection: 'column', - gap: theme.spacing(1), - }, + outer: { + display: "flex", + flexDirection: "column", + gap: theme.spacing(1), + }, })); export default function SwapTxLockAlertsBox() { - const classes = useStyles(); + const classes = useStyles(); - // We specifically choose ALL swaps here - // If a swap is in a state where an Alert is not needed (becaue no Bitcoin have been locked or because the swap has been completed) - // the SwapStatusAlert component will not render an Alert - const swaps = useSwapInfosSortedByDate(); + // We specifically choose ALL swaps here + // If a swap is in a state where an Alert is not needed (becaue no Bitcoin have been locked or because the swap has been completed) + // the SwapStatusAlert component will not render an Alert + const swaps = useSwapInfosSortedByDate(); - return ( - - {swaps.map((swap) => ( - - ))} - - ); + return ( + + {swaps.map((swap) => ( + + ))} + + ); } diff --git a/src-gui/src/renderer/components/modal/feedback/FeedbackDialog.tsx b/src-gui/src/renderer/components/modal/feedback/FeedbackDialog.tsx index b8ed07d5..aa3df8ba 100644 --- a/src-gui/src/renderer/components/modal/feedback/FeedbackDialog.tsx +++ b/src-gui/src/renderer/components/modal/feedback/FeedbackDialog.tsx @@ -1,47 +1,44 @@ import { - Box, - Button, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, - MenuItem, - Select, - TextField, -} from '@material-ui/core'; -import { useState } from 'react'; -import { useSnackbar } from 'notistack'; -import { - useActiveSwapInfo, - useAppSelector, -} from 'store/hooks'; -import { parseDateString } from 'utils/parseUtils'; -import { store } from 'renderer/store/storeRenderer'; -import { CliLog } from 'models/cliModel'; -import { submitFeedbackViaHttp } from '../../../api'; -import { PiconeroAmount } from '../../other/Units'; -import LoadingButton from '../../other/LoadingButton'; + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + DialogTitle, + MenuItem, + Select, + TextField, +} from "@material-ui/core"; +import { useState } from "react"; +import { useSnackbar } from "notistack"; +import { useActiveSwapInfo, useAppSelector } from "store/hooks"; +import { parseDateString } from "utils/parseUtils"; +import { store } from "renderer/store/storeRenderer"; +import { CliLog } from "models/cliModel"; +import { submitFeedbackViaHttp } from "../../../api"; +import { PiconeroAmount } from "../../other/Units"; +import LoadingButton from "../../other/LoadingButton"; async function submitFeedback(body: string, swapId: string | number) { - let attachedBody = ''; + let attachedBody = ""; - if (swapId !== 0 && typeof swapId === 'string') { - const swapInfo = store.getState().rpc.state.swapInfos[swapId]; - const logs = [] as CliLog[]; + if (swapId !== 0 && typeof swapId === "string") { + const swapInfo = store.getState().rpc.state.swapInfos[swapId]; + const logs = [] as CliLog[]; - throw new Error('Not implemented'); + throw new Error("Not implemented"); - if (swapInfo === undefined) { - throw new Error(`Swap with id ${swapId} not found`); + if (swapInfo === undefined) { + throw new Error(`Swap with id ${swapId} not found`); + } + + attachedBody = `${JSON.stringify(swapInfo, null, 4)} \n\nLogs: ${logs + .map((l) => JSON.stringify(l)) + .join("\n====\n")}`; } - attachedBody = `${JSON.stringify(swapInfo, null, 4)} \n\nLogs: ${logs - .map((l) => JSON.stringify(l)) - .join('\n====\n')}`; - } - - await submitFeedbackViaHttp(body, attachedBody); + await submitFeedbackViaHttp(body, attachedBody); } /* @@ -51,120 +48,136 @@ async function submitFeedback(body: string, swapId: string | number) { * selectedSwap = 0 means no swap is attached */ function SwapSelectDropDown({ - selectedSwap, - setSelectedSwap, + selectedSwap, + setSelectedSwap, }: { - selectedSwap: string | number; - setSelectedSwap: (swapId: string | number) => void; + selectedSwap: string | number; + setSelectedSwap: (swapId: string | number) => void; }) { - const swaps = useAppSelector((state) => - Object.values(state.rpc.state.swapInfos), - ); + const swaps = useAppSelector((state) => + Object.values(state.rpc.state.swapInfos), + ); - return ( - - ); + return ( + + ); } const MAX_FEEDBACK_LENGTH = 4000; export default function FeedbackDialog({ - open, - onClose, + open, + onClose, }: { - open: boolean; - onClose: () => void; + open: boolean; + onClose: () => void; }) { - const [pending, setPending] = useState(false); - const [bodyText, setBodyText] = useState(''); - const currentSwapId = useActiveSwapInfo(); + const [pending, setPending] = useState(false); + const [bodyText, setBodyText] = useState(""); + const currentSwapId = useActiveSwapInfo(); - const { enqueueSnackbar } = useSnackbar(); + const { enqueueSnackbar } = useSnackbar(); - const [selectedAttachedSwap, setSelectedAttachedSwap] = useState< - string | number - >(currentSwapId?.swapId || 0); + const [selectedAttachedSwap, setSelectedAttachedSwap] = useState< + string | number + >(currentSwapId?.swap_id || 0); - const bodyTooLong = bodyText.length > MAX_FEEDBACK_LENGTH; + const bodyTooLong = bodyText.length > MAX_FEEDBACK_LENGTH; - return ( - - Submit Feedback - - - Got something to say? Drop us a message below. If you had an issue - with a specific swap, select it from the dropdown to attach the logs. - It will help us figure out what went wrong. Hit that submit button - when you are ready. We appreciate you taking the time to share your - thoughts! - - - setBodyText(e.target.value)} - label={ - bodyTooLong - ? `Text is too long (${bodyText.length}/${MAX_FEEDBACK_LENGTH})` - : 'Feedback' - } - multiline - minRows={4} - maxRows={4} - fullWidth - error={bodyTooLong} - /> - - - - - - { - if (pending) { - return; - } + return ( + + Submit Feedback + + + Got something to say? Drop us a message below. If you had an + issue with a specific swap, select it from the dropdown to + attach the logs. It will help us figure out what went wrong. + Hit that submit button when you are ready. We appreciate you + taking the time to share your thoughts! + + + setBodyText(e.target.value)} + label={ + bodyTooLong + ? `Text is too long (${bodyText.length}/${MAX_FEEDBACK_LENGTH})` + : "Feedback" + } + multiline + minRows={4} + maxRows={4} + fullWidth + error={bodyTooLong} + /> + + + + + + { + if (pending) { + return; + } - try { - setPending(true); - await submitFeedback(bodyText, selectedAttachedSwap); - enqueueSnackbar('Feedback submitted successfully!', { - variant: 'success', - }); - } catch (e) { - console.error(`Failed to submit feedback: ${e}`); - enqueueSnackbar(`Failed to submit feedback (${e})`, { - variant: 'error', - }); - } finally { - setPending(false); - } - onClose(); - }} - loading={pending} - > - Submit - - - - ); + try { + setPending(true); + await submitFeedback( + bodyText, + selectedAttachedSwap, + ); + enqueueSnackbar( + "Feedback submitted successfully!", + { + variant: "success", + }, + ); + } catch (e) { + console.error(`Failed to submit feedback: ${e}`); + enqueueSnackbar( + `Failed to submit feedback (${e})`, + { + variant: "error", + }, + ); + } finally { + setPending(false); + } + onClose(); + }} + loading={pending} + > + Submit + + + + ); } diff --git a/src-gui/src/renderer/components/modal/swap/SwapStateStepper.tsx b/src-gui/src/renderer/components/modal/swap/SwapStateStepper.tsx index 10d0491b..21e47ee5 100644 --- a/src-gui/src/renderer/components/modal/swap/SwapStateStepper.tsx +++ b/src-gui/src/renderer/components/modal/swap/SwapStateStepper.tsx @@ -1,166 +1,169 @@ -import { Step, StepLabel, Stepper, Typography } from '@material-ui/core'; -import { SwapSpawnType } from 'models/cliModel'; -import { SwapStateName } from 'models/rpcModel'; -import { useActiveSwapInfo, useAppSelector } from 'store/hooks'; -import { exhaustiveGuard } from 'utils/typescriptUtils'; +import { Step, StepLabel, Stepper, Typography } from "@material-ui/core"; +import { SwapSpawnType } from "models/cliModel"; +import { SwapStateName } from "models/rpcModel"; +import { useActiveSwapInfo, useAppSelector } from "store/hooks"; +import { exhaustiveGuard } from "utils/typescriptUtils"; export enum PathType { - HAPPY_PATH = 'happy path', - UNHAPPY_PATH = 'unhappy path', + HAPPY_PATH = "happy path", + UNHAPPY_PATH = "unhappy path", } function getActiveStep( - stateName: SwapStateName | null, - processExited: boolean, + stateName: SwapStateName | null, + processExited: boolean, ): [PathType, number, boolean] { - switch (stateName) { - /// // Happy Path - // Step: 0 (Waiting for Bitcoin lock tx to be published) - case null: - return [PathType.HAPPY_PATH, 0, false]; - case SwapStateName.Started: - case SwapStateName.SwapSetupCompleted: - return [PathType.HAPPY_PATH, 0, processExited]; + switch (stateName) { + /// // Happy Path + // Step: 0 (Waiting for Bitcoin lock tx to be published) + case null: + return [PathType.HAPPY_PATH, 0, false]; + case SwapStateName.Started: + case SwapStateName.SwapSetupCompleted: + return [PathType.HAPPY_PATH, 0, processExited]; - // Step: 1 (Waiting for Bitcoin Lock confirmation and XMR Lock Publication) - // We have locked the Bitcoin and are waiting for the other party to lock their XMR - case SwapStateName.BtcLocked: - return [PathType.HAPPY_PATH, 1, processExited]; + // Step: 1 (Waiting for Bitcoin Lock confirmation and XMR Lock Publication) + // We have locked the Bitcoin and are waiting for the other party to lock their XMR + case SwapStateName.BtcLocked: + return [PathType.HAPPY_PATH, 1, processExited]; - // Step: 2 (Waiting for XMR Lock confirmation) - // We have locked the Bitcoin and the other party has locked their XMR - case SwapStateName.XmrLockProofReceived: - return [PathType.HAPPY_PATH, 1, processExited]; + // Step: 2 (Waiting for XMR Lock confirmation) + // We have locked the Bitcoin and the other party has locked their XMR + case SwapStateName.XmrLockProofReceived: + return [PathType.HAPPY_PATH, 1, processExited]; - // Step: 3 (Sending Encrypted Signature and waiting for Bitcoin Redemption) - // The XMR lock transaction has been confirmed - // We now need to send the encrypted signature to the other party and wait for them to redeem the Bitcoin - case SwapStateName.XmrLocked: - case SwapStateName.EncSigSent: - return [PathType.HAPPY_PATH, 2, processExited]; + // Step: 3 (Sending Encrypted Signature and waiting for Bitcoin Redemption) + // The XMR lock transaction has been confirmed + // We now need to send the encrypted signature to the other party and wait for them to redeem the Bitcoin + case SwapStateName.XmrLocked: + case SwapStateName.EncSigSent: + return [PathType.HAPPY_PATH, 2, processExited]; - // Step: 4 (Waiting for XMR Redemption) - case SwapStateName.BtcRedeemed: - return [PathType.HAPPY_PATH, 3, processExited]; + // Step: 4 (Waiting for XMR Redemption) + case SwapStateName.BtcRedeemed: + return [PathType.HAPPY_PATH, 3, processExited]; - // Step: 4 (Completed) (Swap completed, XMR redeemed) - case SwapStateName.XmrRedeemed: - return [PathType.HAPPY_PATH, 4, false]; + // Step: 4 (Completed) (Swap completed, XMR redeemed) + case SwapStateName.XmrRedeemed: + return [PathType.HAPPY_PATH, 4, false]; - // Edge Case of Happy Path where the swap is safely aborted. We "fail" at the first step. - case SwapStateName.SafelyAborted: - return [PathType.HAPPY_PATH, 0, true]; + // Edge Case of Happy Path where the swap is safely aborted. We "fail" at the first step. + case SwapStateName.SafelyAborted: + return [PathType.HAPPY_PATH, 0, true]; - // // Unhappy Path - // Step: 1 (Cancelling swap, checking if cancel transaction has been published already by the other party) - case SwapStateName.CancelTimelockExpired: - return [PathType.UNHAPPY_PATH, 0, processExited]; + // // Unhappy Path + // Step: 1 (Cancelling swap, checking if cancel transaction has been published already by the other party) + case SwapStateName.CancelTimelockExpired: + return [PathType.UNHAPPY_PATH, 0, processExited]; - // Step: 2 (Attempt to publish the Bitcoin refund transaction) - case SwapStateName.BtcCancelled: - return [PathType.UNHAPPY_PATH, 1, processExited]; + // Step: 2 (Attempt to publish the Bitcoin refund transaction) + case SwapStateName.BtcCancelled: + return [PathType.UNHAPPY_PATH, 1, processExited]; - // Step: 2 (Completed) (Bitcoin refunded) - case SwapStateName.BtcRefunded: - return [PathType.UNHAPPY_PATH, 2, false]; + // Step: 2 (Completed) (Bitcoin refunded) + case SwapStateName.BtcRefunded: + return [PathType.UNHAPPY_PATH, 2, false]; - // Step: 2 (We failed to publish the Bitcoin refund transaction) - // We failed to publish the Bitcoin refund transaction because the timelock has expired. - // We will be punished. Nothing we can do about it now. - case SwapStateName.BtcPunished: - return [PathType.UNHAPPY_PATH, 1, true]; - default: - return exhaustiveGuard(stateName); - } + // Step: 2 (We failed to publish the Bitcoin refund transaction) + // We failed to publish the Bitcoin refund transaction because the timelock has expired. + // We will be punished. Nothing we can do about it now. + case SwapStateName.BtcPunished: + return [PathType.UNHAPPY_PATH, 1, true]; + default: + return exhaustiveGuard(stateName); + } } function HappyPathStepper({ - activeStep, - error, + activeStep, + error, }: { - activeStep: number; - error: boolean; + activeStep: number; + error: boolean; }) { - return ( - - - ~12min} - error={error && activeStep === 0} - > - Locking your BTC - - - - ~18min} - error={error && activeStep === 1} - > - They lock their XMR - - - - ~2min} - error={error && activeStep === 2} - > - They redeem the BTC - - - - ~2min} - error={error && activeStep === 3} - > - Redeeming your XMR - - - - ); + return ( + + + ~12min} + error={error && activeStep === 0} + > + Locking your BTC + + + + ~18min} + error={error && activeStep === 1} + > + They lock their XMR + + + + ~2min} + error={error && activeStep === 2} + > + They redeem the BTC + + + + ~2min} + error={error && activeStep === 3} + > + Redeeming your XMR + + + + ); } function UnhappyPathStepper({ - activeStep, - error, + activeStep, + error, }: { - activeStep: number; - error: boolean; + activeStep: number; + error: boolean; }) { - return ( - - - ~20min} - error={error && activeStep === 0} - > - Cancelling swap - - - - ~20min} - error={error && activeStep === 1} - > - Refunding your BTC - - - - ); + return ( + + + ~20min} + error={error && activeStep === 0} + > + Cancelling swap + + + + ~20min} + error={error && activeStep === 1} + > + Refunding your BTC + + + + ); } export default function SwapStateStepper() { - const currentSwapSpawnType = useAppSelector((s) => s.swap.spawnType); - const stateName = useActiveSwapInfo()?.stateName ?? null; - const processExited = useAppSelector((s) => !s.swap.processRunning); - const [pathType, activeStep, error] = getActiveStep(stateName, processExited); + const currentSwapSpawnType = useAppSelector((s) => s.swap.spawnType); + const stateName = useActiveSwapInfo()?.state_name ?? null; + const processExited = useAppSelector((s) => !s.swap.processRunning); + const [pathType, activeStep, error] = getActiveStep( + stateName, + processExited, + ); - // If the current swap is being manually cancelled and refund, we want to show the unhappy path even though the current state is not a "unhappy" state - if (currentSwapSpawnType === SwapSpawnType.CANCEL_REFUND) { - return ; - } + // If the current swap is being manually cancelled and refund, we want to show the unhappy path even though the current state is not a "unhappy" state + if (currentSwapSpawnType === SwapSpawnType.CANCEL_REFUND) { + return ; + } - if (pathType === PathType.HAPPY_PATH) { - return ; - } - return ; + if (pathType === PathType.HAPPY_PATH) { + return ; + } + return ; } diff --git a/src-gui/src/renderer/components/modal/swap/pages/done/BitcoinRefundedPage.tsx b/src-gui/src/renderer/components/modal/swap/pages/done/BitcoinRefundedPage.tsx index 91f3a4cd..cda57383 100644 --- a/src-gui/src/renderer/components/modal/swap/pages/done/BitcoinRefundedPage.tsx +++ b/src-gui/src/renderer/components/modal/swap/pages/done/BitcoinRefundedPage.tsx @@ -1,43 +1,44 @@ -import { Box, DialogContentText } from '@material-ui/core'; -import { SwapStateBtcRefunded } from 'models/storeModel'; -import { useActiveSwapInfo } from 'store/hooks'; -import BitcoinTransactionInfoBox from '../../BitcoinTransactionInfoBox'; -import FeedbackInfoBox from '../../../../pages/help/FeedbackInfoBox'; +import { Box, DialogContentText } from "@material-ui/core"; +import { SwapStateBtcRefunded } from "models/storeModel"; +import { useActiveSwapInfo } from "store/hooks"; +import BitcoinTransactionInfoBox from "../../BitcoinTransactionInfoBox"; +import FeedbackInfoBox from "../../../../pages/help/FeedbackInfoBox"; export default function BitcoinRefundedPage({ - state, + state, }: { - state: SwapStateBtcRefunded | null; + state: SwapStateBtcRefunded | null; }) { - const swap = useActiveSwapInfo(); - const additionalContent = swap - ? `Refund address: ${swap.btcRefundAddress}` - : null; + const swap = useActiveSwapInfo(); + const additionalContent = swap + ? `Refund address: ${swap.btc_refund_address}` + : null; - return ( - - - Unfortunately, the swap was not successful. However, rest assured that - all your Bitcoin has been refunded to the specified address. The swap - process is now complete, and you are free to exit the application. - - - {state && ( - - )} - - - - ); + return ( + + + Unfortunately, the swap was not successful. However, rest + assured that all your Bitcoin has been refunded to the specified + address. The swap process is now complete, and you are free to + exit the application. + + + {state && ( + + )} + + + + ); } diff --git a/src-gui/src/renderer/components/modal/swap/pages/exited/ProcessExitedAndNotDonePage.tsx b/src-gui/src/renderer/components/modal/swap/pages/exited/ProcessExitedAndNotDonePage.tsx index beea3154..db8fcf48 100644 --- a/src-gui/src/renderer/components/modal/swap/pages/exited/ProcessExitedAndNotDonePage.tsx +++ b/src-gui/src/renderer/components/modal/swap/pages/exited/ProcessExitedAndNotDonePage.tsx @@ -1,71 +1,71 @@ -import { Box, DialogContentText } from '@material-ui/core'; -import { useActiveSwapInfo, useAppSelector } from 'store/hooks'; -import { SwapStateProcessExited } from 'models/storeModel'; -import CliLogsBox from '../../../../other/RenderedCliLog'; -import { SwapSpawnType } from 'models/cliModel'; +import { Box, DialogContentText } from "@material-ui/core"; +import { useActiveSwapInfo, useAppSelector } from "store/hooks"; +import { SwapStateProcessExited } from "models/storeModel"; +import CliLogsBox from "../../../../other/RenderedCliLog"; +import { SwapSpawnType } from "models/cliModel"; export default function ProcessExitedAndNotDonePage({ - state, + state, }: { - state: SwapStateProcessExited; + state: SwapStateProcessExited; }) { - const swap = useActiveSwapInfo(); - const logs = useAppSelector((s) => s.swap.logs); - const spawnType = useAppSelector((s) => s.swap.spawnType); + const swap = useActiveSwapInfo(); + const logs = useAppSelector((s) => s.swap.logs); + const spawnType = useAppSelector((s) => s.swap.spawnType); - function getText() { - const isCancelRefund = spawnType === SwapSpawnType.CANCEL_REFUND; - const hasRpcError = state.rpcError != null; - const hasSwap = swap != null; + function getText() { + const isCancelRefund = spawnType === SwapSpawnType.CANCEL_REFUND; + const hasRpcError = state.rpcError != null; + const hasSwap = swap != null; - let messages = []; + let messages = []; - messages.push( - isCancelRefund - ? 'The manual cancel and refund was unsuccessful.' - : 'The swap exited unexpectedly without completing.', - ); - - if (!hasSwap && !isCancelRefund) { - messages.push('No funds were locked.'); - } - - messages.push( - hasRpcError - ? 'Check the error and the logs below for more information.' - : 'Check the logs below for more information.', - ); - - if (hasSwap) { - messages.push(`The swap is in the "${swap.stateName}" state.`); - if (!isCancelRefund) { messages.push( - 'Try resuming the swap or attempt to initiate a manual cancel and refund.', + isCancelRefund + ? "The manual cancel and refund was unsuccessful." + : "The swap exited unexpectedly without completing.", ); - } + + if (!hasSwap && !isCancelRefund) { + messages.push("No funds were locked."); + } + + messages.push( + hasRpcError + ? "Check the error and the logs below for more information." + : "Check the logs below for more information.", + ); + + if (hasSwap) { + messages.push(`The swap is in the "${swap.state_name}" state.`); + if (!isCancelRefund) { + messages.push( + "Try resuming the swap or attempt to initiate a manual cancel and refund.", + ); + } + } + + return messages.join(" "); } - return messages.join(' '); - } - - return ( - - {getText()} - - {state.rpcError && ( - - )} - - - - ); + return ( + + {getText()} + + {state.rpcError && ( + + )} + + + + ); } diff --git a/src-gui/src/renderer/components/modal/swap/pages/exited/ProcessExitedPage.tsx b/src-gui/src/renderer/components/modal/swap/pages/exited/ProcessExitedPage.tsx index 2e78ff86..6d11f1a1 100644 --- a/src-gui/src/renderer/components/modal/swap/pages/exited/ProcessExitedPage.tsx +++ b/src-gui/src/renderer/components/modal/swap/pages/exited/ProcessExitedPage.tsx @@ -1,47 +1,47 @@ -import { useActiveSwapInfo } from 'store/hooks'; -import { SwapStateName } from 'models/rpcModel'; +import { useActiveSwapInfo } from "store/hooks"; +import { SwapStateName } from "models/rpcModel"; import { - isSwapStateBtcPunished, - isSwapStateBtcRefunded, - isSwapStateXmrRedeemInMempool, - SwapStateProcessExited, -} from '../../../../../../models/storeModel'; -import XmrRedeemInMempoolPage from '../done/XmrRedeemInMempoolPage'; -import BitcoinPunishedPage from '../done/BitcoinPunishedPage'; + isSwapStateBtcPunished, + isSwapStateBtcRefunded, + isSwapStateXmrRedeemInMempool, + SwapStateProcessExited, +} from "../../../../../../models/storeModel"; +import XmrRedeemInMempoolPage from "../done/XmrRedeemInMempoolPage"; +import BitcoinPunishedPage from "../done/BitcoinPunishedPage"; // eslint-disable-next-line import/no-cycle -import SwapStatePage from '../SwapStatePage'; -import BitcoinRefundedPage from '../done/BitcoinRefundedPage'; -import ProcessExitedAndNotDonePage from './ProcessExitedAndNotDonePage'; +import SwapStatePage from "../SwapStatePage"; +import BitcoinRefundedPage from "../done/BitcoinRefundedPage"; +import ProcessExitedAndNotDonePage from "./ProcessExitedAndNotDonePage"; type ProcessExitedPageProps = { - state: SwapStateProcessExited; + state: SwapStateProcessExited; }; export default function ProcessExitedPage({ state }: ProcessExitedPageProps) { - const swap = useActiveSwapInfo(); + const swap = useActiveSwapInfo(); - // If we have a swap state, for a "done" state we should use it to display additional information that can't be extracted from the database - if ( - isSwapStateXmrRedeemInMempool(state.prevState) || - isSwapStateBtcRefunded(state.prevState) || - isSwapStateBtcPunished(state.prevState) - ) { - return ; - } + // If we have a swap state, for a "done" state we should use it to display additional information that can't be extracted from the database + if ( + isSwapStateXmrRedeemInMempool(state.prevState) || + isSwapStateBtcRefunded(state.prevState) || + isSwapStateBtcPunished(state.prevState) + ) { + return ; + } - // If we don't have a swap state for a "done" state, we should fall back to using the database to display as much information as we can - if (swap) { - if (swap.stateName === SwapStateName.XmrRedeemed) { - return ; + // If we don't have a swap state for a "done" state, we should fall back to using the database to display as much information as we can + if (swap) { + if (swap.state_name === SwapStateName.XmrRedeemed) { + return ; + } + if (swap.state_name === SwapStateName.BtcRefunded) { + return ; + } + if (swap.state_name === SwapStateName.BtcPunished) { + return ; + } } - if (swap.stateName === SwapStateName.BtcRefunded) { - return ; - } - if (swap.stateName === SwapStateName.BtcPunished) { - return ; - } - } - // If the swap is not a "done" state (or we don't have a db state because the swap did complete the SwapSetup yet) we should tell the user and show logs - return ; + // If the swap is not a "done" state (or we don't have a db state because the swap did complete the SwapSetup yet) we should tell the user and show logs + return ; } diff --git a/src-gui/src/renderer/components/pages/history/table/HistoryRow.tsx b/src-gui/src/renderer/components/pages/history/table/HistoryRow.tsx index bbd0bf3c..27d4160f 100644 --- a/src-gui/src/renderer/components/pages/history/table/HistoryRow.tsx +++ b/src-gui/src/renderer/components/pages/history/table/HistoryRow.tsx @@ -1,86 +1,98 @@ import { - Box, - Collapse, - IconButton, - makeStyles, - TableCell, - TableRow, -} from '@material-ui/core'; -import { useState } from 'react'; -import ArrowForwardIcon from '@material-ui/icons/ArrowForward'; -import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; -import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp'; + Box, + Collapse, + IconButton, + makeStyles, + TableCell, + TableRow, +} from "@material-ui/core"; +import { useState } from "react"; +import ArrowForwardIcon from "@material-ui/icons/ArrowForward"; +import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown"; +import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp"; import { - getHumanReadableDbStateType, - getSwapBtcAmount, - getSwapXmrAmount, - GetSwapInfoResponse, -} from '../../../../../models/rpcModel'; -import HistoryRowActions from './HistoryRowActions'; -import HistoryRowExpanded from './HistoryRowExpanded'; -import { BitcoinAmount, MoneroAmount } from '../../../other/Units'; + getHumanReadableDbStateType, + getSwapBtcAmount, + getSwapXmrAmount, + GetSwapInfoResponse, +} from "../../../../../models/rpcModel"; +import HistoryRowActions from "./HistoryRowActions"; +import HistoryRowExpanded from "./HistoryRowExpanded"; +import { BitcoinAmount, MoneroAmount } from "../../../other/Units"; type HistoryRowProps = { - swap: GetSwapInfoResponse; + swap: GetSwapInfoResponse; }; const useStyles = makeStyles((theme) => ({ - amountTransferContainer: { - display: 'flex', - alignItems: 'center', - gap: theme.spacing(1), - }, + amountTransferContainer: { + display: "flex", + alignItems: "center", + gap: theme.spacing(1), + }, })); function AmountTransfer({ - btcAmount, - xmrAmount, + btcAmount, + xmrAmount, }: { - xmrAmount: number; - btcAmount: number; + xmrAmount: number; + btcAmount: number; }) { - const classes = useStyles(); + const classes = useStyles(); - return ( - - - - - - ); + return ( + + + + + + ); } export default function HistoryRow({ swap }: HistoryRowProps) { - const btcAmount = getSwapBtcAmount(swap); - const xmrAmount = getSwapXmrAmount(swap); + const btcAmount = getSwapBtcAmount(swap); + const xmrAmount = getSwapXmrAmount(swap); - const [expanded, setExpanded] = useState(false); + const [expanded, setExpanded] = useState(false); - return ( - <> - - - setExpanded(!expanded)}> - {expanded ? : } - - - {swap.swapId.substring(0, 5)}... - - - - {getHumanReadableDbStateType(swap.stateName)} - - - - + return ( + <> + + + setExpanded(!expanded)} + > + {expanded ? ( + + ) : ( + + )} + + + {swap.swap_id.substring(0, 5)}... + + + + + {getHumanReadableDbStateType(swap.state_name)} + + + + + - - - - {expanded && } - - - - - ); + + + + {expanded && } + + + + + ); } diff --git a/src-gui/src/renderer/components/pages/history/table/HistoryRowActions.tsx b/src-gui/src/renderer/components/pages/history/table/HistoryRowActions.tsx index 0caf8543..bc867253 100644 --- a/src-gui/src/renderer/components/pages/history/table/HistoryRowActions.tsx +++ b/src-gui/src/renderer/components/pages/history/table/HistoryRowActions.tsx @@ -1,90 +1,90 @@ -import { Tooltip } from '@material-ui/core'; -import Button, { ButtonProps } from '@material-ui/core/Button/Button'; -import DoneIcon from '@material-ui/icons/Done'; -import ErrorIcon from '@material-ui/icons/Error'; -import { green, red } from '@material-ui/core/colors'; -import PlayArrowIcon from '@material-ui/icons/PlayArrow'; -import IpcInvokeButton from '../../../IpcInvokeButton'; +import { Tooltip } from "@material-ui/core"; +import Button, { ButtonProps } from "@material-ui/core/Button/Button"; +import DoneIcon from "@material-ui/icons/Done"; +import ErrorIcon from "@material-ui/icons/Error"; +import { green, red } from "@material-ui/core/colors"; +import PlayArrowIcon from "@material-ui/icons/PlayArrow"; +import IpcInvokeButton from "../../../IpcInvokeButton"; import { - GetSwapInfoResponse, - SwapStateName, - isSwapStateNamePossiblyCancellableSwap, - isSwapStateNamePossiblyRefundableSwap, -} from '../../../../../models/rpcModel'; + GetSwapInfoResponse, + SwapStateName, + isSwapStateNamePossiblyCancellableSwap, + isSwapStateNamePossiblyRefundableSwap, +} from "../../../../../models/rpcModel"; export function SwapResumeButton({ - swap, - ...props + swap, + ...props }: { swap: GetSwapInfoResponse } & ButtonProps) { - return ( - } - requiresRpc - {...props} - > - Resume - - ); + return ( + } + requiresRpc + {...props} + > + Resume + + ); } export function SwapCancelRefundButton({ - swap, - ...props + swap, + ...props }: { swap: GetSwapInfoResponse } & ButtonProps) { - const cancelOrRefundable = - isSwapStateNamePossiblyCancellableSwap(swap.stateName) || - isSwapStateNamePossiblyRefundableSwap(swap.stateName); + const cancelOrRefundable = + isSwapStateNamePossiblyCancellableSwap(swap.state_name) || + isSwapStateNamePossiblyRefundableSwap(swap.state_name); - if (!cancelOrRefundable) { - return <>; - } + if (!cancelOrRefundable) { + return <>; + } - return ( - - Attempt manual Cancel & Refund - - ); + return ( + + Attempt manual Cancel & Refund + + ); } export default function HistoryRowActions({ - swap, + swap, }: { - swap: GetSwapInfoResponse; + swap: GetSwapInfoResponse; }) { - if (swap.stateName === SwapStateName.XmrRedeemed) { - return ( - - - - ); - } + if (swap.state_name === SwapStateName.XmrRedeemed) { + return ( + + + + ); + } - if (swap.stateName === SwapStateName.BtcRefunded) { - return ( - - - - ); - } + if (swap.state_name === SwapStateName.BtcRefunded) { + return ( + + + + ); + } - if (swap.stateName === SwapStateName.BtcPunished) { - return ( - - - - ); - } + if (swap.state_name === SwapStateName.BtcPunished) { + return ( + + + + ); + } - return ; + return ; } diff --git a/src-gui/src/renderer/components/pages/history/table/HistoryRowExpanded.tsx b/src-gui/src/renderer/components/pages/history/table/HistoryRowExpanded.tsx index cc0deb98..cb9f1297 100644 --- a/src-gui/src/renderer/components/pages/history/table/HistoryRowExpanded.tsx +++ b/src-gui/src/renderer/components/pages/history/table/HistoryRowExpanded.tsx @@ -1,134 +1,143 @@ import { - Box, - Link, - makeStyles, - Table, - TableBody, - TableCell, - TableContainer, - TableRow, -} from '@material-ui/core'; -import { getBitcoinTxExplorerUrl } from 'utils/conversionUtils'; -import { isTestnet } from 'store/config'; + Box, + Link, + makeStyles, + Table, + TableBody, + TableCell, + TableContainer, + TableRow, +} from "@material-ui/core"; +import { getBitcoinTxExplorerUrl } from "utils/conversionUtils"; +import { isTestnet } from "store/config"; import { - getHumanReadableDbStateType, - getSwapBtcAmount, - getSwapExchangeRate, - getSwapTxFees, - getSwapXmrAmount, - GetSwapInfoResponse, -} from '../../../../../models/rpcModel'; -import SwapLogFileOpenButton from './SwapLogFileOpenButton'; -import { SwapCancelRefundButton } from './HistoryRowActions'; -import { SwapMoneroRecoveryButton } from './SwapMoneroRecoveryButton'; + getHumanReadableDbStateType, + getSwapBtcAmount, + getSwapExchangeRate, + getSwapTxFees, + getSwapXmrAmount, + GetSwapInfoResponse, +} from "../../../../../models/rpcModel"; +import SwapLogFileOpenButton from "./SwapLogFileOpenButton"; +import { SwapCancelRefundButton } from "./HistoryRowActions"; +import { SwapMoneroRecoveryButton } from "./SwapMoneroRecoveryButton"; import { - BitcoinAmount, - MoneroAmount, - MoneroBitcoinExchangeRate, -} from 'renderer/components/other/Units'; + BitcoinAmount, + MoneroAmount, + MoneroBitcoinExchangeRate, +} from "renderer/components/other/Units"; const useStyles = makeStyles((theme) => ({ - outer: { - display: 'grid', - padding: theme.spacing(1), - gap: theme.spacing(1), - }, - actionsOuter: { - display: 'flex', - flexDirection: 'row', - gap: theme.spacing(1), - }, + outer: { + display: "grid", + padding: theme.spacing(1), + gap: theme.spacing(1), + }, + actionsOuter: { + display: "flex", + flexDirection: "row", + gap: theme.spacing(1), + }, })); export default function HistoryRowExpanded({ - swap, + swap, }: { - swap: GetSwapInfoResponse; + swap: GetSwapInfoResponse; }) { - const classes = useStyles(); + const classes = useStyles(); - const { seller, startDate } = swap; - const btcAmount = getSwapBtcAmount(swap); - const xmrAmount = getSwapXmrAmount(swap); - const txFees = getSwapTxFees(swap); - const exchangeRate = getSwapExchangeRate(swap); + const { seller, start_date: startDate } = swap; + const btcAmount = getSwapBtcAmount(swap); + const xmrAmount = getSwapXmrAmount(swap); + const txFees = getSwapTxFees(swap); + const exchangeRate = getSwapExchangeRate(swap); - return ( - - - - - - Started on - {startDate} - - - Swap ID - {swap.swapId} - - - State Name - - {getHumanReadableDbStateType(swap.stateName)} - - - - Monero Amount - - - - - - Bitcoin Amount - - - - - - Exchange Rate - - - - - - Bitcoin Network Fees - - - - - - Provider Address - - {seller.addresses.join(', ')} - - - - Bitcoin lock transaction - - - {swap.txLockId} - - - - -
-
- - - - - -
- ); + return ( + + + + + + Started on + {startDate} + + + Swap ID + {swap.swap_id} + + + State Name + + {getHumanReadableDbStateType(swap.state_name)} + + + + Monero Amount + + + + + + Bitcoin Amount + + + + + + Exchange Rate + + + + + + Bitcoin Network Fees + + + + + + Provider Address + + {seller.addresses.join(", ")} + + + + Bitcoin lock transaction + + + {swap.tx_lock_id} + + + + +
+
+ + + + + +
+ ); } diff --git a/src-gui/src/renderer/components/pages/history/table/HistoryTable.tsx b/src-gui/src/renderer/components/pages/history/table/HistoryTable.tsx index 163456cf..5e602a22 100644 --- a/src-gui/src/renderer/components/pages/history/table/HistoryTable.tsx +++ b/src-gui/src/renderer/components/pages/history/table/HistoryTable.tsx @@ -1,53 +1,53 @@ import { - Box, - makeStyles, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, -} from '@material-ui/core'; -import { sortBy } from 'lodash'; -import { parseDateString } from 'utils/parseUtils'; + Box, + makeStyles, + Paper, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, +} from "@material-ui/core"; +import { sortBy } from "lodash"; +import { parseDateString } from "utils/parseUtils"; import { - useAppSelector, - useSwapInfosSortedByDate, -} from '../../../../../store/hooks'; -import HistoryRow from './HistoryRow'; + useAppSelector, + useSwapInfosSortedByDate, +} from "../../../../../store/hooks"; +import HistoryRow from "./HistoryRow"; const useStyles = makeStyles((theme) => ({ - outer: { - paddingTop: theme.spacing(1), - paddingBottom: theme.spacing(1), - }, + outer: { + paddingTop: theme.spacing(1), + paddingBottom: theme.spacing(1), + }, })); export default function HistoryTable() { - const classes = useStyles(); - const swapSortedByDate = useSwapInfosSortedByDate(); + const classes = useStyles(); + const swapSortedByDate = useSwapInfosSortedByDate(); - return ( - - - - - - - ID - Amount - State - - - - - {swapSortedByDate.map((swap) => ( - - ))} - -
-
-
- ); + return ( + + + + + + + ID + Amount + State + + + + + {swapSortedByDate.map((swap) => ( + + ))} + +
+
+
+ ); } diff --git a/src-gui/src/renderer/components/pages/history/table/SwapMoneroRecoveryButton.tsx b/src-gui/src/renderer/components/pages/history/table/SwapMoneroRecoveryButton.tsx index e20e056e..b4ecdb20 100644 --- a/src-gui/src/renderer/components/pages/history/table/SwapMoneroRecoveryButton.tsx +++ b/src-gui/src/renderer/components/pages/history/table/SwapMoneroRecoveryButton.tsx @@ -1,119 +1,120 @@ -import { ButtonProps } from '@material-ui/core/Button/Button'; +import { ButtonProps } from "@material-ui/core/Button/Button"; import { - Box, - Button, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - Link, -} from '@material-ui/core'; -import { useAppDispatch, useAppSelector } from 'store/hooks'; -import { rpcResetMoneroRecoveryKeys } from 'store/features/rpcSlice'; + Box, + Button, + Dialog, + DialogActions, + DialogContent, + DialogContentText, + Link, +} from "@material-ui/core"; +import { useAppDispatch, useAppSelector } from "store/hooks"; +import { rpcResetMoneroRecoveryKeys } from "store/features/rpcSlice"; import { - GetSwapInfoResponse, - isSwapMoneroRecoverable, -} from '../../../../../models/rpcModel'; -import IpcInvokeButton from '../../../IpcInvokeButton'; -import DialogHeader from '../../../modal/DialogHeader'; -import ScrollablePaperTextBox from '../../../other/ScrollablePaperTextBox'; + GetSwapInfoResponse, + isSwapMoneroRecoverable, +} from "../../../../../models/rpcModel"; +import IpcInvokeButton from "../../../IpcInvokeButton"; +import DialogHeader from "../../../modal/DialogHeader"; +import ScrollablePaperTextBox from "../../../other/ScrollablePaperTextBox"; function MoneroRecoveryKeysDialog({ swap }: { swap: GetSwapInfoResponse }) { - const dispatch = useAppDispatch(); - const keys = useAppSelector((s) => s.rpc.state.moneroRecovery); + const dispatch = useAppDispatch(); + const keys = useAppSelector((s) => s.rpc.state.moneroRecovery); - function onClose() { - dispatch(rpcResetMoneroRecoveryKeys()); - } + function onClose() { + dispatch(rpcResetMoneroRecoveryKeys()); + } - if (keys === null || keys.swapId !== swap.swapId) { - return <>; - } + if (keys === null || keys.swapId !== swap.swap_id) { + return <>; + } - return ( - - - - - You can use the keys below to manually redeem the Monero funds from - the multi-signature wallet. -
    -
  • - This is useful if the swap daemon fails to redeem the funds itself -
  • -
  • - If you have come this far, there is no risk of losing funds. You - are the only one with access to these keys and can use them to - access your funds -
  • -
  • - View{' '} - - this guide - {' '} - for a detailed description on how to import the keys and spend the - funds. -
  • -
-
- - {[ - ['Primary Address', keys.keys.address], - ['View Key', keys.keys.view_key], - ['Spend Key', keys.keys.spend_key], - ['Restore Height', keys.keys.restore_height.toString()], - ].map(([title, value]) => ( - + - ))} - -
- - - -
- ); + + + You can use the keys below to manually redeem the Monero + funds from the multi-signature wallet. +
    +
  • + This is useful if the swap daemon fails to redeem + the funds itself +
  • +
  • + If you have come this far, there is no risk of + losing funds. You are the only one with access to + these keys and can use them to access your funds +
  • +
  • + View{" "} + + this guide + {" "} + for a detailed description on how to import the keys + and spend the funds. +
  • +
+
+ + {[ + ["Primary Address", keys.keys.address], + ["View Key", keys.keys.view_key], + ["Spend Key", keys.keys.spend_key], + ["Restore Height", keys.keys.restore_height.toString()], + ].map(([title, value]) => ( + + ))} + +
+ + + + + ); } export function SwapMoneroRecoveryButton({ - swap, - ...props + swap, + ...props }: { swap: GetSwapInfoResponse } & ButtonProps) { - const isRecoverable = isSwapMoneroRecoverable(swap.stateName); + const isRecoverable = isSwapMoneroRecoverable(swap.state_name); - if (!isRecoverable) { - return <>; - } + if (!isRecoverable) { + return <>; + } - return ( - <> - - Display Monero Recovery Keys - - - - ); + return ( + <> + + Display Monero Recovery Keys + + + + ); } diff --git a/src-gui/src/renderer/rpc.ts b/src-gui/src/renderer/rpc.ts index 126ca21c..da691a16 100644 --- a/src-gui/src/renderer/rpc.ts +++ b/src-gui/src/renderer/rpc.ts @@ -1,7 +1,7 @@ import { invoke } from "@tauri-apps/api/core"; import { BalanceBitcoinResponse } from "models/rpcModel"; import { store } from "./store/storeRenderer"; -import { rpcSetBalance } from "store/features/rpcSlice"; +import { rpcSetBalance, rpcSetSwapInfo } from "store/features/rpcSlice"; export async function checkBitcoinBalance() { // TODO: use tauri-bindgen here @@ -13,6 +13,7 @@ export async function checkBitcoinBalance() { } export async function getRawSwapInfos() { - const response = await invoke("swap_infos"); + const response = await invoke("swap_infos_all"); console.log(response); + (response as any[]).forEach((info) => store.dispatch(rpcSetSwapInfo(info))); } diff --git a/src-gui/src/store/features/rpcSlice.ts b/src-gui/src/store/features/rpcSlice.ts index 19f5865d..599a04b5 100644 --- a/src-gui/src/store/features/rpcSlice.ts +++ b/src-gui/src/store/features/rpcSlice.ts @@ -1,11 +1,11 @@ -import { createSlice, PayloadAction } from '@reduxjs/toolkit'; -import { ExtendedProviderStatus, ProviderStatus } from 'models/apiModel'; -import { MoneroWalletRpcUpdateState } from 'models/storeModel'; +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { ExtendedProviderStatus, ProviderStatus } from "models/apiModel"; +import { MoneroWalletRpcUpdateState } from "models/storeModel"; import { GetSwapInfoResponse, MoneroRecoveryResponse, RpcProcessStateType, -} from '../../models/rpcModel'; +} from "../../models/rpcModel"; import { CliLog, isCliLog, @@ -14,8 +14,8 @@ import { isCliLogFinishedSyncingMoneroWallet, isCliLogStartedRpcServer, isCliLogStartedSyncingMoneroWallet, -} from '../../models/cliModel'; -import { getLogsAndStringsFromRawFileString } from 'utils/parseUtils'; +} from "../../models/cliModel"; +import { getLogsAndStringsFromRawFileString } from "utils/parseUtils"; type Process = | { @@ -82,7 +82,7 @@ const initialState: RPCSlice = { }; export const rpcSlice = createSlice({ - name: 'rpc', + name: "rpc", initialState, reducers: { rpcAddLogs(slice, action: PayloadAction<(CliLog | string)[]>) { @@ -110,7 +110,7 @@ export const rpcSlice = createSlice({ downloadUrl: log.fields.download_url, }; - if (log.fields.progress === '100%') { + if (log.fields.progress === "100%") { slice.state.moneroWalletRpc.updateState = false; } } else if (isCliLogStartedSyncingMoneroWallet(log)) { @@ -169,7 +169,7 @@ export const rpcSlice = createSlice({ slice.state.withdrawTxId = null; }, rpcSetSwapInfo(slice, action: PayloadAction) { - slice.state.swapInfos[action.payload.swapId] = action.payload; + slice.state.swapInfos[action.payload.swap_id] = action.payload; }, rpcSetEndpointBusy(slice, action: PayloadAction) { if (!slice.busyEndpoints.includes(action.payload)) { diff --git a/src-gui/src/store/hooks.ts b/src-gui/src/store/hooks.ts index df19f6e5..d2f58135 100644 --- a/src-gui/src/store/hooks.ts +++ b/src-gui/src/store/hooks.ts @@ -1,7 +1,7 @@ -import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; -import type { AppDispatch, RootState } from 'renderer/store/storeRenderer'; -import { sortBy } from 'lodash'; -import { parseDateString } from 'utils/parseUtils'; +import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; +import type { AppDispatch, RootState } from "renderer/store/storeRenderer"; +import { sortBy } from "lodash"; +import { parseDateString } from "utils/parseUtils"; // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch = () => useDispatch(); @@ -22,7 +22,7 @@ export function useIsSwapRunning() { export function useSwapInfo(swapId: string | null) { return useAppSelector((state) => - swapId ? state.rpc.state.swapInfos[swapId] ?? null : null, + swapId ? (state.rpc.state.swapInfos[swapId] ?? null) : null, ); } @@ -51,6 +51,6 @@ export function useSwapInfosSortedByDate() { const swapInfos = useAppSelector((state) => state.rpc.state.swapInfos); return sortBy( Object.values(swapInfos), - (swap) => -parseDateString(swap.startDate), + (swap) => -parseDateString(swap.start_date), ); } diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 98077ef9..37776609 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -21,4 +21,4 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" swap = { path = "../swap" } tauri = { version = "2.0.0-rc.1", features = ["config-json5"] } -tauri-plugin-shell = "2.0.0-rc.0" +uuid = "1.10.0" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 650321c1..d6686571 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -3,5 +3,5 @@ "identifier": "default", "description": "Capability for the main window", "windows": ["main"], - "permissions": ["shell:allow-open"] + "permissions": [] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c4102031..b9189c81 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -3,11 +3,15 @@ use std::sync::Arc; use once_cell::sync::OnceCell; use swap::{ api::{ - request::{get_balance, BalanceArgs, BalanceResponse}, + request::{ + get_balance, get_swap_info, get_swap_infos_all, BalanceArgs, BalanceResponse, + GetSwapInfoResponse, + }, Context, }, cli::command::{Bitcoin, Monero}, }; +use uuid::Uuid; // Lazy load the Context static CONTEXT: OnceCell> = OnceCell::new(); @@ -26,6 +30,15 @@ async fn balance() -> Result { .map_err(|e| e.to_string()) } +#[tauri::command] +async fn swap_infos_all() -> Result, String> { + let context = CONTEXT.get().unwrap(); + + get_swap_infos_all(context.clone()) + .await + .map_err(|e| e.to_string()) +} + fn setup<'a>(app: &'a mut tauri::App) -> Result<(), Box> { tauri::async_runtime::block_on(async { let context = Context::build( @@ -56,8 +69,7 @@ fn setup<'a>(app: &'a mut tauri::App) -> Result<(), Box> #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() - .plugin(tauri_plugin_shell::init()) - .invoke_handler(tauri::generate_handler![balance]) + .invoke_handler(tauri::generate_handler![balance, swap_infos_all]) .setup(setup) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/swap/src/api/request.rs b/swap/src/api/request.rs index 2a140765..03a491ea 100644 --- a/swap/src/api/request.rs +++ b/swap/src/api/request.rs @@ -7,6 +7,7 @@ use crate::network::swarm; use crate::protocol::bob::{BobState, Swap}; use crate::protocol::{bob, State}; use crate::{bitcoin, cli, monero, rpc}; +use ::bitcoin::Txid; use anyhow::{bail, Context as AnyContext, Result}; use libp2p::core::Multiaddr; use qrcode::render::unicode; @@ -52,22 +53,6 @@ pub struct MoneroRecoveryArgs { pub swap_id: Uuid, } -#[derive(Serialize, Deserialize, Debug)] -pub struct ResumeSwapResponse { - pub result: String, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct BalanceResponse { - pub balance: u64, // in satoshis -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct BuyXmrResponse { - pub swap_id: String, - pub quote: BidQuote, // You'll need to import or define BidQuote -} - #[derive(Debug, Eq, PartialEq)] pub struct WithdrawBtcArgs { pub amount: Option, @@ -94,6 +79,52 @@ pub struct GetSwapInfoArgs { pub swap_id: Uuid, } +#[derive(Serialize, Deserialize, Debug)] +pub struct ResumeSwapResponse { + pub result: String, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct BalanceResponse { + pub balance: u64, // in satoshis +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct BuyXmrResponse { + pub swap_id: String, + pub quote: BidQuote, // You'll need to import or define BidQuote +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetHistoryResponse { + swaps: Vec<(Uuid, String)>, +} + +#[derive(Serialize)] +pub struct GetSwapInfoResponse { + pub swap_id: Uuid, + pub seller: Seller, + pub completed: bool, + pub start_date: String, + pub state_name: String, + pub xmr_amount: u64, + pub btc_amount: u64, + pub tx_lock_id: Txid, + pub tx_cancel_fee: u64, + pub tx_refund_fee: u64, + pub tx_lock_fee: u64, + pub btc_refund_address: String, + pub cancel_timelock: u32, + pub punish_timelock: u32, + pub timelock: Option, +} + +#[derive(Serialize, Deserialize)] +pub struct Seller { + pub peer_id: String, + pub addresses: Vec, +} + // TODO: We probably dont even need this. // We can just call the method directly from the RPC server, the CLI and the Tauri connector #[derive(Debug, PartialEq)] @@ -231,8 +262,23 @@ async fn suspend_current_swap(context: Arc) -> Result) -> Result> { + let swap_ids = context.db.all().await?; + let mut swap_infos = Vec::new(); + + for (swap_id, _) in swap_ids { + let swap_info = get_swap_info(GetSwapInfoArgs { swap_id }, context.clone()).await?; + swap_infos.push(swap_info); + } + + Ok(swap_infos) +} + // #[tracing::instrument(fields(method="get_swap_info", swap_id = args.swap_id), skip(context))] -async fn get_swap_info(args: GetSwapInfoArgs, context: Arc) -> Result { +pub async fn get_swap_info( + args: GetSwapInfoArgs, + context: Arc, +) -> Result { let bitcoin_wallet = context .bitcoin_wallet .as_ref() @@ -311,38 +357,38 @@ async fn get_swap_info(args: GetSwapInfoArgs, context: Arc) -> Result { - Some(state.expired_timelock(bitcoin_wallet).await) + Some(state.expired_timelock(bitcoin_wallet).await?) } BobState::XmrLocked(state) | BobState::EncSigSent(state) => { - Some(state.expired_timelock(bitcoin_wallet).await) + Some(state.expired_timelock(bitcoin_wallet).await?) } BobState::CancelTimelockExpired(state) | BobState::BtcCancelled(state) => { - Some(state.expired_timelock(bitcoin_wallet).await) + Some(state.expired_timelock(bitcoin_wallet).await?) } - BobState::BtcPunished { .. } => Some(Ok(ExpiredTimelocks::Punish)), + BobState::BtcPunished { .. } => Some(ExpiredTimelocks::Punish), BobState::BtcRefunded(_) | BobState::BtcRedeemed(_) | BobState::XmrRedeemed { .. } => None, }; - Ok(json!({ - "swapId": args.swap_id, - "seller": { - "peerId": peerId.to_string(), - "addresses": addresses + Ok(GetSwapInfoResponse { + swap_id: args.swap_id, + seller: Seller { + peer_id: peerId.to_string(), + addresses, }, - "completed": is_completed, - "startDate": start_date, - "stateName": state_name, - "xmrAmount": xmr_amount, - "btcAmount": btc_amount, - "txLockId": tx_lock_id, - "txCancelFee": tx_cancel_fee, - "txRefundFee": tx_refund_fee, - "txLockFee": tx_lock_fee, - "btcRefundAddress": btc_refund_address.to_string(), - "cancelTimelock": cancel_timelock, - "punishTimelock": punish_timelock, - "timelock": timelock.map(|tl| tl.map(|tl| json!(tl)).unwrap_or(json!(null))).unwrap_or(json!(null)), - })) + completed: is_completed, + start_date, + state_name, + xmr_amount: xmr_amount.as_piconero(), + btc_amount, + tx_lock_id, + tx_cancel_fee, + tx_refund_fee, + tx_lock_fee, + btc_refund_address: btc_refund_address.to_string(), + cancel_timelock: cancel_timelock.into(), + punish_timelock: punish_timelock.into(), + timelock, + }) } async fn buy_xmr(buy_xmr: BuyXmrArgs, context: Arc) -> Result { @@ -653,7 +699,7 @@ async fn cancel_and_refund( }) } -async fn get_history(context: Arc) -> Result { +async fn get_history(context: Arc) -> Result { let swaps = context.db.all().await?; let mut vec: Vec<(Uuid, String)> = Vec::new(); for (swap_id, state) in swaps { @@ -661,7 +707,7 @@ async fn get_history(context: Arc) -> Result { vec.push((swap_id, state.to_string())); } - Ok(json!({ "swaps": vec })) + Ok(GetHistoryResponse { swaps: vec }) } async fn get_raw_states(context: Arc) -> Result {