diff --git a/src-gui/src/renderer/background.ts b/src-gui/src/renderer/background.ts index ecde6939..f5e606a6 100644 --- a/src-gui/src/renderer/background.ts +++ b/src-gui/src/renderer/background.ts @@ -3,11 +3,11 @@ import { TauriEvent } from "models/tauriModel"; import { contextStatusEventReceived, contextInitializationFailed, - rpcSetBalance, timelockChangeEventReceived, approvalEventReceived, backgroundProgressEventReceived, } from "store/features/rpcSlice"; +import { setBitcoinBalance } from "store/features/bitcoinWalletSlice"; import { receivedCliLog } from "store/features/logsSlice"; import { poolStatusReceived } from "store/features/poolSlice"; import { swapProgressEventReceived } from "store/features/swapSlice"; @@ -118,7 +118,7 @@ listen(TAURI_UNIFIED_EVENT_CHANNEL_NAME, (event) => { break; case "BalanceChange": - store.dispatch(rpcSetBalance(eventData.balance)); + store.dispatch(setBitcoinBalance(eventData.balance)); break; case "SwapDatabaseStateUpdate": diff --git a/src-gui/src/renderer/components/alert/FundsLeftInWalletAlert.tsx b/src-gui/src/renderer/components/alert/FundsLeftInWalletAlert.tsx deleted file mode 100644 index c1a571e3..00000000 --- a/src-gui/src/renderer/components/alert/FundsLeftInWalletAlert.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Button } from "@mui/material"; -import Alert from "@mui/material/Alert"; -import { useNavigate } from "react-router-dom"; -import { useAppSelector } from "store/hooks"; - -export default function FundsLeftInWalletAlert() { - const fundsLeft = useAppSelector((state) => state.rpc.state.balance); - const navigate = useNavigate(); - - if (fundsLeft != null && fundsLeft > 0) { - return ( - navigate("/bitcoin-wallet")} - > - View - - } - > - There are some Bitcoin left in your wallet - - ); - } - return null; -} diff --git a/src-gui/src/renderer/components/modal/wallet/pages/AddressInputPage.tsx b/src-gui/src/renderer/components/modal/wallet/pages/AddressInputPage.tsx index cdf5d952..cf5909de 100644 --- a/src-gui/src/renderer/components/modal/wallet/pages/AddressInputPage.tsx +++ b/src-gui/src/renderer/components/modal/wallet/pages/AddressInputPage.tsx @@ -14,7 +14,7 @@ export default function AddressInputPage({ <> To withdraw the Bitcoin inside the internal wallet, please enter an - address. All funds will be sent to that address. + address. All funds (the entire balance) will be sent to that address. diff --git a/src-gui/src/renderer/components/navigation/NavigationFooter.tsx b/src-gui/src/renderer/components/navigation/NavigationFooter.tsx index 2c31925a..e598e68d 100644 --- a/src-gui/src/renderer/components/navigation/NavigationFooter.tsx +++ b/src-gui/src/renderer/components/navigation/NavigationFooter.tsx @@ -1,6 +1,5 @@ import { Box, Tooltip } from "@mui/material"; import { BackgroundProgressAlerts } from "../alert/DaemonStatusAlert"; -import FundsLeftInWalletAlert from "../alert/FundsLeftInWalletAlert"; import UnfinishedSwapsAlert from "../alert/UnfinishedSwapsAlert"; import BackgroundRefundAlert from "../alert/BackgroundRefundAlert"; import ContactInfoBox from "../other/ContactInfoBox"; @@ -15,7 +14,6 @@ export default function NavigationFooter() { gap: 1, }} > - diff --git a/src-gui/src/renderer/components/other/ActionableMonospaceTextBox.tsx b/src-gui/src/renderer/components/other/ActionableMonospaceTextBox.tsx index c321ba9b..0fa14df7 100644 --- a/src-gui/src/renderer/components/other/ActionableMonospaceTextBox.tsx +++ b/src-gui/src/renderer/components/other/ActionableMonospaceTextBox.tsx @@ -92,40 +92,43 @@ export default function ActionableMonospaceTextBox({ > - - - {content} - {displayCopyIcon && ( - - - - )} - {enableQrCode && ( - - setQrCodeOpen(true)} - onMouseEnter={() => setIsQrCodeButtonHovered(true)} - onMouseLeave={() => setIsQrCodeButtonHovered(false)} - size="small" - sx={{ marginLeft: 1 }} - > - - - - )} - - + + {displayCopyIcon && ( + + + + + + )} + {enableQrCode && ( + + { + e.stopPropagation(); + setQrCodeOpen(true); + }} + onMouseEnter={() => setIsQrCodeButtonHovered(true)} + onMouseLeave={() => setIsQrCodeButtonHovered(false)} + size="small" + > + + + + )} + + } + > + {content} + diff --git a/src-gui/src/renderer/components/other/MonospaceTextBox.tsx b/src-gui/src/renderer/components/other/MonospaceTextBox.tsx index 6a95c499..bce732a0 100644 --- a/src-gui/src/renderer/components/other/MonospaceTextBox.tsx +++ b/src-gui/src/renderer/components/other/MonospaceTextBox.tsx @@ -3,18 +3,25 @@ import { Box, Typography } from "@mui/material"; type Props = { children: React.ReactNode; light?: boolean; + actions?: React.ReactNode; }; -export default function MonospaceTextBox({ children, light = false }: Props) { +export default function MonospaceTextBox({ + children, + light = false, + actions, +}: Props) { return ( ({ display: "flex", alignItems: "center", + justifyContent: "space-between", backgroundColor: light ? "transparent" : theme.palette.grey[900], borderRadius: 2, border: light ? `1px solid ${theme.palette.grey[800]}` : "none", padding: theme.spacing(1), + gap: 1, })} > {children} + {actions && ( + {actions} + )} ); } diff --git a/src-gui/src/renderer/components/pages/help/ExportDataBox.tsx b/src-gui/src/renderer/components/pages/help/ExportDataBox.tsx deleted file mode 100644 index 23eb2326..00000000 --- a/src-gui/src/renderer/components/pages/help/ExportDataBox.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { - Box, - Typography, - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Button, - Link, - DialogContentText, -} from "@mui/material"; -import InfoBox from "renderer/components/pages/swap/swap/components/InfoBox"; -import { useState } from "react"; -import { getWalletDescriptor } from "renderer/rpc"; -import { ExportBitcoinWalletResponse } from "models/tauriModel"; -import PromiseInvokeButton from "renderer/components/PromiseInvokeButton"; -import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox"; -import { isContextWithBitcoinWallet } from "models/tauriModelExt"; - -export default function ExportDataBox() { - const [walletDescriptor, setWalletDescriptor] = - useState(null); - - const handleCloseDialog = () => { - setWalletDescriptor(null); - }; - - return ( - - - You can export the wallet descriptor of the interal Bitcoin wallet - for backup or recovery purposes. Please make sure to store it - securely. - - - } - additionalContent={ - <> - - Reveal Bitcoin Wallet Private Key - - {walletDescriptor !== null && ( - - )} - - } - /> - ); -} - -function WalletDescriptorModal({ - open, - onClose, - walletDescriptor, -}: { - open: boolean; - onClose: () => void; - walletDescriptor: ExportBitcoinWalletResponse; -}) { - const parsedDescriptor = JSON.parse( - walletDescriptor.wallet_descriptor["descriptor"], - ); - const stringifiedDescriptor = JSON.stringify(parsedDescriptor, null, 4); - - return ( - - Bitcoin Wallet Descriptor - - -
    -
  • - The text below contains the wallet descriptor of the internal - Bitcoin wallet. It contains your private key and can be used to - derive your wallet. It should thus be stored securely. -
  • -
  • - It can be imported into other Bitcoin wallets or services that - support the descriptor format. -
  • -
  • - For more information on what to do with the descriptor, see our{" "} - - documentation - -
  • -
-
- -
- - - -
- ); -} diff --git a/src-gui/src/renderer/components/pages/help/SettingsPage.tsx b/src-gui/src/renderer/components/pages/help/SettingsPage.tsx index 5c684d94..dd3ed571 100644 --- a/src-gui/src/renderer/components/pages/help/SettingsPage.tsx +++ b/src-gui/src/renderer/components/pages/help/SettingsPage.tsx @@ -32,7 +32,6 @@ export default function SettingsPage() { - ); diff --git a/src-gui/src/renderer/components/pages/monero/components/ConfirmationsBadge.tsx b/src-gui/src/renderer/components/pages/monero/components/ConfirmationsBadge.tsx index cc9f27c2..271071dc 100644 --- a/src-gui/src/renderer/components/pages/monero/components/ConfirmationsBadge.tsx +++ b/src-gui/src/renderer/components/pages/monero/components/ConfirmationsBadge.tsx @@ -13,7 +13,7 @@ export default function ConfirmationsBadge({ return ( } - label="Published" + label="Unconfirmed" color="secondary" size="small" /> diff --git a/src-gui/src/renderer/components/pages/wallet/WalletPage.tsx b/src-gui/src/renderer/components/pages/wallet/WalletPage.tsx index d3b4d0b6..3b22e8d1 100644 --- a/src-gui/src/renderer/components/pages/wallet/WalletPage.tsx +++ b/src-gui/src/renderer/components/pages/wallet/WalletPage.tsx @@ -1,21 +1,32 @@ -import { Box, Typography } from "@mui/material"; -import { Alert } from "@mui/material"; -import WithdrawWidget from "./WithdrawWidget"; +import { Box } from "@mui/material"; +import { useAppSelector } from "store/hooks"; +import WalletOverview from "./components/WalletOverview"; +import WalletActionButtons from "./components/WalletActionButtons"; +import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox"; export default function WalletPage() { + const walletBalance = useAppSelector((state) => state.bitcoinWallet.balance); + const bitcoinAddress = useAppSelector((state) => state.bitcoinWallet.address); + return ( - - You do not have to deposit money before starting a swap. Instead, you - will be greeted with a deposit address after you initiate one. - - + + {bitcoinAddress && ( + + )} + ); } diff --git a/src-gui/src/renderer/components/pages/wallet/WithdrawWidget.tsx b/src-gui/src/renderer/components/pages/wallet/WithdrawWidget.tsx index 047c5e03..cf18b882 100644 --- a/src-gui/src/renderer/components/pages/wallet/WithdrawWidget.tsx +++ b/src-gui/src/renderer/components/pages/wallet/WithdrawWidget.tsx @@ -9,7 +9,7 @@ import WithdrawDialog from "../../modal/wallet/WithdrawDialog"; import WalletRefreshButton from "./WalletRefreshButton"; export default function WithdrawWidget() { - const walletBalance = useAppSelector((state) => state.rpc.state.balance); + const walletBalance = useAppSelector((state) => state.bitcoinWallet.balance); const [showDialog, setShowDialog] = useState(false); function onShowDialog() { diff --git a/src-gui/src/renderer/components/pages/wallet/components/WalletActionButtons.tsx b/src-gui/src/renderer/components/pages/wallet/components/WalletActionButtons.tsx new file mode 100644 index 00000000..c6ddb551 --- /dev/null +++ b/src-gui/src/renderer/components/pages/wallet/components/WalletActionButtons.tsx @@ -0,0 +1,37 @@ +import { Box, Chip } from "@mui/material"; +import { Send as SendIcon } from "@mui/icons-material"; +import { useState } from "react"; +import { useAppSelector } from "store/hooks"; +import WithdrawDialog from "../../../modal/wallet/WithdrawDialog"; +import WalletDescriptorButton from "./WalletDescriptorButton"; + +export default function WalletActionButtons() { + const [showDialog, setShowDialog] = useState(false); + const balance = useAppSelector((state) => state.bitcoinWallet.balance); + + return ( + <> + setShowDialog(false)} /> + + + } + label="Sweep" + variant="button" + clickable + onClick={() => setShowDialog(true)} + disabled={balance === null || balance <= 0} + /> + + + + + ); +} diff --git a/src-gui/src/renderer/components/pages/wallet/components/WalletDescriptorButton.tsx b/src-gui/src/renderer/components/pages/wallet/components/WalletDescriptorButton.tsx new file mode 100644 index 00000000..acd24743 --- /dev/null +++ b/src-gui/src/renderer/components/pages/wallet/components/WalletDescriptorButton.tsx @@ -0,0 +1,101 @@ +import { + Button, + Dialog, + DialogTitle, + DialogContent, + DialogActions, + DialogContentText, + Link, +} from "@mui/material"; +import { Key as KeyIcon } from "@mui/icons-material"; +import { useState } from "react"; +import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox"; +import { getWalletDescriptor } from "renderer/rpc"; +import { ExportBitcoinWalletResponse } from "models/tauriModel"; +import PromiseInvokeButton from "renderer/components/PromiseInvokeButton"; +import { isContextWithBitcoinWallet } from "models/tauriModelExt"; + +const WALLET_DESCRIPTOR_DOCS_URL = + "https://github.com/eigenwallet/core/blob/master/dev-docs/asb/README.md#exporting-the-bitcoin-wallet-descriptor"; + +export default function WalletDescriptorButton() { + const [walletDescriptor, setWalletDescriptor] = + useState(null); + + const handleCloseDialog = () => { + setWalletDescriptor(null); + }; + + return ( + <> + } + onInvoke={getWalletDescriptor} + onSuccess={setWalletDescriptor} + displayErrorSnackbar={true} + contextRequirement={isContextWithBitcoinWallet} + > + Reveal Private Key + + {walletDescriptor !== null && ( + + )} + + ); +} + +function WalletDescriptorModal({ + open, + onClose, + walletDescriptor, +}: { + open: boolean; + onClose: () => void; + walletDescriptor: ExportBitcoinWalletResponse; +}) { + const parsedDescriptor = JSON.parse( + walletDescriptor.wallet_descriptor["descriptor"], + ); + const stringifiedDescriptor = JSON.stringify(parsedDescriptor, null, 4); + + return ( + + Bitcoin Wallet Descriptor + + + The Bitcoin wallet is derived from your Monero wallet. Opening the + same Monero wallet in another Eigenwallet will yield the same Bitcoin + wallet. +
+
+ It contains your private key. Anyone who has it can spend your funds. + It should thus be stored securely. +
+
+ It can be imported into other Bitcoin wallets or services that support + the descriptor format. For more information on what to do with the + descriptor, see our{" "} + + documentation + +
+ +
+ + + +
+ ); +} diff --git a/src-gui/src/renderer/components/pages/wallet/components/WalletOverview.tsx b/src-gui/src/renderer/components/pages/wallet/components/WalletOverview.tsx new file mode 100644 index 00000000..2f25cebb --- /dev/null +++ b/src-gui/src/renderer/components/pages/wallet/components/WalletOverview.tsx @@ -0,0 +1,80 @@ +import { Box, Typography, Card } from "@mui/material"; +import { BitcoinAmount } from "renderer/components/other/Units"; +import { useAppSelector, useSettings } from "store/hooks"; +import { satsToBtc } from "utils/conversionUtils"; +import WalletRefreshButton from "../WalletRefreshButton"; + +interface WalletOverviewProps { + balance: number | null; +} + +function FiatBitcoinAmount({ amount }: { amount: number | null }) { + const btcPrice = useAppSelector((state) => state.rates.btcPrice); + const [fetchFiatPrices, fiatCurrency] = useSettings((settings) => [ + settings.fetchFiatPrices, + settings.fiatCurrency, + ]); + + if ( + !fetchFiatPrices || + fiatCurrency == null || + amount == null || + btcPrice == null + ) { + return ; + } + + return ( + + {(amount * btcPrice).toFixed(2)} {fiatCurrency} + + ); +} + +export default function WalletOverview({ balance }: WalletOverviewProps) { + const btcBalance = balance == null ? null : satsToBtc(balance); + + return ( + + + {/* Left side content */} + + + Available Funds + + + + + + + + + + {/* Right side - Refresh button */} + + + + + + ); +} diff --git a/src-gui/src/renderer/rpc.ts b/src-gui/src/renderer/rpc.ts index c481ef35..75871fb5 100644 --- a/src-gui/src/renderer/rpc.ts +++ b/src-gui/src/renderer/rpc.ts @@ -16,6 +16,7 @@ import { WithdrawBtcResponse, GetSwapInfoArgs, ExportBitcoinWalletResponse, + GetBitcoinAddressResponse, CheckMoneroNodeArgs, CheckSeedArgs, CheckSeedResponse, @@ -49,11 +50,11 @@ import { ContextStatus, } from "models/tauriModel"; import { - rpcSetBalance, rpcSetSwapInfo, approvalRequestsReplaced, contextInitializationFailed, } from "store/features/rpcSlice"; +import { setBitcoinBalance } from "store/features/bitcoinWalletSlice"; import { setMainAddress, setBalance, @@ -166,7 +167,7 @@ export async function checkBitcoinBalance() { force_refresh: true, }); - store.dispatch(rpcSetBalance(response.balance)); + store.dispatch(setBitcoinBalance(response.balance)); } export async function cheapCheckBitcoinBalance() { @@ -174,7 +175,15 @@ export async function cheapCheckBitcoinBalance() { force_refresh: false, }); - store.dispatch(rpcSetBalance(response.balance)); + store.dispatch(setBitcoinBalance(response.balance)); +} + +export async function getBitcoinAddress() { + const response = await invokeNoArgs( + "get_bitcoin_address", + ); + + return response.address; } export async function getAllSwapInfos() { diff --git a/src-gui/src/store/combinedReducer.ts b/src-gui/src/store/combinedReducer.ts index ca868b19..3e151465 100644 --- a/src-gui/src/store/combinedReducer.ts +++ b/src-gui/src/store/combinedReducer.ts @@ -8,6 +8,7 @@ import nodesSlice from "./features/nodesSlice"; import conversationsSlice from "./features/conversationsSlice"; import poolSlice from "./features/poolSlice"; import walletSlice from "./features/walletSlice"; +import bitcoinWalletSlice from "./features/bitcoinWalletSlice"; import logsSlice from "./features/logsSlice"; export const reducers = { @@ -21,5 +22,6 @@ export const reducers = { conversations: conversationsSlice, pool: poolSlice, wallet: walletSlice, + bitcoinWallet: bitcoinWalletSlice, logs: logsSlice, }; diff --git a/src-gui/src/store/features/bitcoinWalletSlice.ts b/src-gui/src/store/features/bitcoinWalletSlice.ts new file mode 100644 index 00000000..1f14804f --- /dev/null +++ b/src-gui/src/store/features/bitcoinWalletSlice.ts @@ -0,0 +1,32 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +interface BitcoinWalletState { + address: string | null; + balance: number | null; +} + +const initialState: BitcoinWalletState = { + address: null, + balance: null, +}; + +export const bitcoinWalletSlice = createSlice({ + name: "bitcoinWallet", + initialState, + reducers: { + setBitcoinAddress(state, action: PayloadAction) { + state.address = action.payload; + }, + setBitcoinBalance(state, action: PayloadAction) { + state.balance = action.payload; + }, + resetBitcoinWalletState(state) { + return initialState; + }, + }, +}); + +export const { setBitcoinAddress, setBitcoinBalance, resetBitcoinWalletState } = + bitcoinWalletSlice.actions; + +export default bitcoinWalletSlice.reducer; diff --git a/src-gui/src/store/features/rpcSlice.ts b/src-gui/src/store/features/rpcSlice.ts index 74a30e61..1e2d080f 100644 --- a/src-gui/src/store/features/rpcSlice.ts +++ b/src-gui/src/store/features/rpcSlice.ts @@ -14,7 +14,6 @@ import { GetSwapInfoResponseExt } from "models/tauriModelExt"; import logger from "utils/logger"; interface State { - balance: number | null; withdrawTxId: string | null; rendezvousDiscoveredSellers: (ExtendedMakerStatus | MakerStatus)[]; swapInfos: { @@ -54,7 +53,6 @@ export interface RPCSlice { const initialState: RPCSlice = { status: null, state: { - balance: null, withdrawTxId: null, rendezvousDiscoveredSellers: [], swapInfos: {}, @@ -95,9 +93,6 @@ export const rpcSlice = createSlice({ ); } }, - rpcSetBalance(slice, action: PayloadAction) { - slice.state.balance = action.payload; - }, rpcSetWithdrawTxId(slice, action: PayloadAction) { slice.state.withdrawTxId = action.payload; }, @@ -177,7 +172,6 @@ export const rpcSlice = createSlice({ export const { contextStatusEventReceived, contextInitializationFailed, - rpcSetBalance, rpcSetWithdrawTxId, rpcResetWithdrawTxId, rpcSetRendezvousDiscoveredMakers, diff --git a/src-gui/src/store/middleware/storeListener.ts b/src-gui/src/store/middleware/storeListener.ts index 5a961a24..5e3e77aa 100644 --- a/src-gui/src/store/middleware/storeListener.ts +++ b/src-gui/src/store/middleware/storeListener.ts @@ -3,6 +3,7 @@ import { throttle, debounce } from "lodash"; import { getAllSwapInfos, checkBitcoinBalance, + getBitcoinAddress, updateAllNodeStatuses, fetchSellersAtPresetRendezvousPoints, getSwapInfo, @@ -30,6 +31,7 @@ import { addFeedbackId, setConversation, } from "store/features/conversationsSlice"; +import { setBitcoinAddress } from "store/features/bitcoinWalletSlice"; // Create a Map to store throttled functions per swap_id const throttledGetSwapInfoFunctions = new Map< @@ -86,15 +88,21 @@ export function createMainListeners() { if (!status) return; - // If the Bitcoin wallet just came available, check the Bitcoin balance + // If the Bitcoin wallet just came available, check the Bitcoin balance and get address if ( status.bitcoin_wallet_available && !previousContextStatus?.bitcoin_wallet_available ) { logger.info( - "Bitcoin wallet just became available, checking balance...", + "Bitcoin wallet just became available, checking balance and getting address...", ); await checkBitcoinBalance(); + try { + const address = await getBitcoinAddress(); + store.dispatch(setBitcoinAddress(address)); + } catch (error) { + logger.error("Failed to fetch Bitcoin address", error); + } } // If the Monero wallet just came available, initialize the Monero wallet diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index ffc12e15..311373e2 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -8,11 +8,11 @@ use swap::cli::{ BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, ChangeMoneroNodeArgs, CheckElectrumNodeArgs, CheckElectrumNodeResponse, CheckMoneroNodeArgs, CheckMoneroNodeResponse, CheckSeedArgs, CheckSeedResponse, DfxAuthenticateResponse, - ExportBitcoinWalletArgs, GetCurrentSwapArgs, GetDataDirArgs, GetHistoryArgs, - GetLogsArgs, GetMoneroAddressesArgs, GetMoneroBalanceArgs, GetMoneroHistoryArgs, - GetMoneroMainAddressArgs, GetMoneroSeedArgs, GetMoneroSyncProgressArgs, - GetPendingApprovalsResponse, GetRestoreHeightArgs, GetSwapInfoArgs, - GetSwapInfosAllArgs, ListSellersArgs, MoneroRecoveryArgs, RedactArgs, + ExportBitcoinWalletArgs, GetBitcoinAddressArgs, GetCurrentSwapArgs, GetDataDirArgs, + GetHistoryArgs, GetLogsArgs, GetMoneroAddressesArgs, GetMoneroBalanceArgs, + GetMoneroHistoryArgs, GetMoneroMainAddressArgs, GetMoneroSeedArgs, + GetMoneroSyncProgressArgs, GetPendingApprovalsResponse, GetRestoreHeightArgs, + GetSwapInfoArgs, GetSwapInfosAllArgs, ListSellersArgs, MoneroRecoveryArgs, RedactArgs, RejectApprovalArgs, RejectApprovalResponse, ResolveApprovalArgs, ResumeSwapArgs, SendMoneroArgs, SetRestoreHeightArgs, SuspendCurrentSwapArgs, WithdrawBtcArgs, }, @@ -35,6 +35,7 @@ macro_rules! generate_command_handlers { () => { tauri::generate_handler![ get_balance, + get_bitcoin_address, get_monero_addresses, get_swap_info, get_swap_infos_all, @@ -435,6 +436,7 @@ tauri_command!(send_monero, SendMoneroArgs); tauri_command!(change_monero_node, ChangeMoneroNodeArgs); // These commands require no arguments +tauri_command!(get_bitcoin_address, GetBitcoinAddressArgs, no_args); tauri_command!(get_wallet_descriptor, ExportBitcoinWalletArgs, no_args); tauri_command!(suspend_current_swap, SuspendCurrentSwapArgs, no_args); tauri_command!(get_swap_info, GetSwapInfoArgs); diff --git a/swap/src/cli/api/request.rs b/swap/src/cli/api/request.rs index 7cb55875..8db84941 100644 --- a/swap/src/cli/api/request.rs +++ b/swap/src/cli/api/request.rs @@ -279,6 +279,30 @@ impl Request for BalanceArgs { } } +// GetBitcoinAddress +#[typeshare] +#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)] +pub struct GetBitcoinAddressArgs; + +#[typeshare] +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct GetBitcoinAddressResponse { + #[typeshare(serialized_as = "string")] + #[serde(with = "swap_serde::bitcoin::address_serde")] + pub address: bitcoin::Address, +} + +impl Request for GetBitcoinAddressArgs { + type Response = GetBitcoinAddressResponse; + + async fn request(self, ctx: Arc) -> Result { + let bitcoin_wallet = ctx.try_get_bitcoin_wallet().await?; + let address = bitcoin_wallet.new_address().await?; + + Ok(GetBitcoinAddressResponse { address }) + } +} + // GetHistory #[typeshare] #[derive(Serialize, Deserialize, Debug)]