From 063f9dbf9b28cc9390eb3183e7a5feef5a0945ba Mon Sep 17 00:00:00 2001 From: binarybaron <86064887+binarybaron@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:47:15 +0200 Subject: [PATCH] feat(gui, swap): allow change-address to be omitted and default to internal wallet (#68) This PR: - allows --change-address to be omitted and default to internal wallet address (https://github.com/comit-network/xmr-btc-swap/pull/1709). This is a change that is merged from upstream into our fork - adds the necessary components for the tauri integration and the ui components to allow toggling between internal vs external refund address Co-authored-by: binarybaron <86064887+binarybaron@users.noreply.github.com> Co-authored-by: Einliterflasche <81313171+Einliterflasche@users.noreply.github.com> Co-authored-by: Byron Hambly --- CHANGELOG.md | 1 + docs/cli/README.md | 2 +- .../modal/swap/pages/init/InitPage.tsx | 100 ++++++++++++------ src-gui/src/renderer/rpc.ts | 20 ++-- swap/src/cli/api.rs | 4 + swap/src/cli/api/request.rs | 19 +++- swap/src/cli/command.rs | 10 +- swap/src/rpc/methods.rs | 12 ++- 8 files changed, 118 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e110b501b..bd4cec221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ASB + CLI: You can now use the `logs` command to retrieve logs stored in the past, redacting addresses and id's using `logs --redact`. - ASB: The `--disable-timestamp` flag has been removed - Introduced a cooperative Monero redeem feature for Bob to request from Alice if Bob is punished for not refunding in time. Alice can choose to cooperate but is not obligated to do so. This change is backwards compatible. To attempt recovery, resume a swap in the "Bitcoin punished" state. Success depends on Alice being active and still having a record of the swap. Note that Alice's cooperation is voluntary and recovery is not guaranteed +- CLI: `--change-address` can now be omitted. In that case, any change is refunded to the internal bitcoin wallet. ## [0.13.2] - 2024-07-02 diff --git a/docs/cli/README.md b/docs/cli/README.md index d95d90ef1..eff9d0722 100644 --- a/docs/cli/README.md +++ b/docs/cli/README.md @@ -75,7 +75,7 @@ OPTIONS: This command has three core options: -- `--change-address`: A Bitcoin address you control. Will be used for refunds of any kind. +- `--change-address`: A Bitcoin address you control. Will be used for refunds of any kind. You can also omit this flag which will refund any change to the internal wallet. - `--receive-address`: A Monero address you control. This is where you will receive the Monero after the swap. - `--seller`: The multiaddress of the seller you want to swap with. diff --git a/src-gui/src/renderer/components/modal/swap/pages/init/InitPage.tsx b/src-gui/src/renderer/components/modal/swap/pages/init/InitPage.tsx index fbd80111c..0836ea73b 100644 --- a/src-gui/src/renderer/components/modal/swap/pages/init/InitPage.tsx +++ b/src-gui/src/renderer/components/modal/swap/pages/init/InitPage.tsx @@ -1,4 +1,11 @@ -import { Box, DialogContentText, makeStyles } from "@material-ui/core"; +import { + Box, + makeStyles, + Paper, + Tab, + Tabs, + Typography, +} from "@material-ui/core"; import PlayArrowIcon from "@material-ui/icons/PlayArrow"; import { useState } from "react"; import BitcoinAddressTextField from "renderer/components/inputs/BitcoinAddressTextField"; @@ -21,61 +28,90 @@ const useStyles = makeStyles((theme) => ({ export default function InitPage() { const classes = useStyles(); + const [redeemAddress, setRedeemAddress] = useState(""); const [refundAddress, setRefundAddress] = useState(""); + const [useExternalRefundAddress, setUseExternalRefundAddress] = + useState(false); + const [redeemAddressValid, setRedeemAddressValid] = useState(false); const [refundAddressValid, setRefundAddressValid] = useState(false); + const selectedProvider = useAppSelector( (state) => state.providers.selectedProvider, ); async function init() { - await buyXmr(selectedProvider, refundAddress, redeemAddress); + await buyXmr( + selectedProvider, + useExternalRefundAddress ? refundAddress : null, + redeemAddress, + ); } return ( - - Please specify the address to which the Monero should be sent upon - completion of the swap and the address for receiving a Bitcoin refund if - the swap fails. - - - + + + setUseExternalRefundAddress(newValue === 1) + } + > + + + + + {useExternalRefundAddress ? ( + + ) : ( + + In case something goes wrong, the Bitcoin will be refunded to + the internal Bitcoin wallet of the GUI. You can then withdraw + them from there or use them for another swap directly. + + )} + + + + + } + onInvoke={init} + displayErrorSnackbar + > + Request quote and start swap + - - } - onInvoke={init} - displayErrorSnackbar - > - Start swap - ); } diff --git a/src-gui/src/renderer/rpc.ts b/src-gui/src/renderer/rpc.ts index 241d87741..ee8785219 100644 --- a/src-gui/src/renderer/rpc.ts +++ b/src-gui/src/renderer/rpc.ts @@ -89,14 +89,22 @@ export async function withdrawBtc(address: string): Promise { export async function buyXmr( seller: Provider, - bitcoin_change_address: string, + bitcoin_change_address: string | null, monero_receive_address: string, ) { - await invoke("buy_xmr", { - seller: providerToConcatenatedMultiAddr(seller), - bitcoin_change_address, - monero_receive_address, - }); + await invoke( + "buy_xmr", + bitcoin_change_address == null + ? { + seller: providerToConcatenatedMultiAddr(seller), + monero_receive_address, + } + : { + seller: providerToConcatenatedMultiAddr(seller), + monero_receive_address, + bitcoin_change_address, + }, + ); } export async function resumeSwap(swapId: string) { diff --git a/swap/src/cli/api.rs b/swap/src/cli/api.rs index df1be01af..3a2da19b5 100644 --- a/swap/src/cli/api.rs +++ b/swap/src/cli/api.rs @@ -415,6 +415,10 @@ impl Context { Ok(()) } + + pub fn bitcoin_wallet(&self) -> Option> { + self.bitcoin_wallet.clone() + } } impl fmt::Debug for Context { diff --git a/swap/src/cli/api/request.rs b/swap/src/cli/api/request.rs index 34c6f5ae1..85b8cf5cf 100644 --- a/swap/src/cli/api/request.rs +++ b/swap/src/cli/api/request.rs @@ -44,8 +44,8 @@ pub trait Request { pub struct BuyXmrArgs { #[typeshare(serialized_as = "string")] pub seller: Multiaddr, - #[typeshare(serialized_as = "string")] - pub bitcoin_change_address: bitcoin::Address, + #[typeshare(serialized_as = "Option")] + pub bitcoin_change_address: Option, #[typeshare(serialized_as = "string")] pub monero_receive_address: monero::Address, } @@ -545,6 +545,21 @@ pub async fn buy_xmr( .as_ref() .expect("Could not find Bitcoin wallet"), ); + + let bitcoin_change_address = match bitcoin_change_address { + Some(addr) => addr, + None => { + let internal_wallet_address = bitcoin_wallet.new_address().await?; + + tracing::info!( + internal_wallet_address=%internal_wallet_address, + "No --change-address supplied. Any change will be received to the internal wallet." + ); + + internal_wallet_address + } + }; + let monero_wallet = Arc::clone( context .monero_wallet diff --git a/swap/src/cli/command.rs b/swap/src/cli/command.rs index 9bec6db3e..190ec5bab 100644 --- a/swap/src/cli/command.rs +++ b/swap/src/cli/command.rs @@ -78,8 +78,10 @@ where } => { let monero_receive_address = monero_address::validate_is_testnet(monero_receive_address, is_testnet)?; - let bitcoin_change_address = - bitcoin_address::validate_is_testnet(bitcoin_change_address, is_testnet)?; + + let bitcoin_change_address = bitcoin_change_address + .map(|address| bitcoin_address::validate_is_testnet(address, is_testnet)) + .transpose()?; let context = Arc::new( ContextBuilder::new(is_testnet) @@ -372,10 +374,10 @@ enum CliCommand { #[structopt( long = "change-address", - help = "The bitcoin address where any form of change or excess funds should be sent to", + help = "The bitcoin address where any form of change or excess funds should be sent to. If omitted they will be sent to the internal wallet.", parse(try_from_str = bitcoin_address::parse) )] - bitcoin_change_address: bitcoin::Address, + bitcoin_change_address: Option, #[structopt(flatten)] monero: Monero, diff --git a/swap/src/rpc/methods.rs b/swap/src/rpc/methods.rs index f871d0d62..d31b35f70 100644 --- a/swap/src/rpc/methods.rs +++ b/swap/src/rpc/methods.rs @@ -90,11 +90,13 @@ pub fn register_modules(outer_context: Context) -> Result> { module.register_async_method("buy_xmr", |params_raw, context| async move { let mut params: BuyXmrArgs = params_raw.parse()?; - params.bitcoin_change_address = bitcoin_address::validate( - params.bitcoin_change_address, - context.config.env_config.bitcoin_network, - ) - .to_jsonrpsee_result()?; + params.bitcoin_change_address = params + .bitcoin_change_address + .map(|address| { + bitcoin_address::validate(address, context.config.env_config.bitcoin_network) + }) + .transpose() + .to_jsonrpsee_result()?; params.monero_receive_address = monero_address::validate( params.monero_receive_address,