Skip to content

Commit

Permalink
feat(gui, swap): allow change-address to be omitted and default to in…
Browse files Browse the repository at this point in the history
…ternal wallet (#68)

This PR:
- allows --change-address to be omitted and default to internal wallet address (comit-network#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 <[email protected]>
Co-authored-by: Einliterflasche <[email protected]>
Co-authored-by: Byron Hambly <[email protected]>
  • Loading branch information
3 people authored Sep 9, 2024
1 parent 91482f1 commit 063f9db
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 50 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion docs/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
100 changes: 68 additions & 32 deletions src-gui/src/renderer/components/modal/swap/pages/init/InitPage.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 (
<Box>
<RemainingFundsWillBeUsedAlert />
<DialogContentText>
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.
</DialogContentText>

<Box className={classes.fieldsOuter}>
<MoneroAddressTextField
label="Monero redeem address"
address={redeemAddress}
onAddressChange={setRedeemAddress}
onAddressValidityChange={setRedeemAddressValid}
helperText="The monero will be sent to this address"
helperText="The monero will be sent to this address if the swap is successful."
fullWidth
/>

<BitcoinAddressTextField
label="Bitcoin refund address"
address={refundAddress}
onAddressChange={setRefundAddress}
onAddressValidityChange={setRefundAddressValid}
helperText="In case something goes terribly wrong, all Bitcoin will be refunded to this address"
fullWidth
/>
<Paper variant="outlined" style={{}}>
<Tabs
value={useExternalRefundAddress ? 1 : 0}
indicatorColor="primary"
variant="fullWidth"
onChange={(_, newValue) =>
setUseExternalRefundAddress(newValue === 1)
}
>
<Tab label="Refund to internal Bitcoin wallet" value={0} />
<Tab label="Refund to external Bitcoin address" value={1} />
</Tabs>
<Box style={{ padding: "16px" }}>
{useExternalRefundAddress ? (
<BitcoinAddressTextField
label="External Bitcoin refund address"
address={refundAddress}
onAddressChange={setRefundAddress}
onAddressValidityChange={setRefundAddressValid}
helperText="In case something goes wrong, the Bitcoin will be refunded to this address."
fullWidth
/>
) : (
<Typography variant="caption">
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.
</Typography>
)}
</Box>
</Paper>
</Box>
<Box style={{ display: "flex", justifyContent: "center" }}>
<PromiseInvokeButton
disabled={
(!refundAddressValid && useExternalRefundAddress) ||
!redeemAddressValid ||
!selectedProvider
}
variant="contained"
color="primary"
size="large"
className={classes.initButton}
endIcon={<PlayArrowIcon />}
onInvoke={init}
displayErrorSnackbar
>
Request quote and start swap
</PromiseInvokeButton>
</Box>

<PromiseInvokeButton
disabled={
!refundAddressValid || !redeemAddressValid || !selectedProvider
}
variant="contained"
color="primary"
size="large"
className={classes.initButton}
endIcon={<PlayArrowIcon />}
onInvoke={init}
displayErrorSnackbar
>
Start swap
</PromiseInvokeButton>
</Box>
);
}
20 changes: 14 additions & 6 deletions src-gui/src/renderer/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,22 @@ export async function withdrawBtc(address: string): Promise<string> {

export async function buyXmr(
seller: Provider,
bitcoin_change_address: string,
bitcoin_change_address: string | null,
monero_receive_address: string,
) {
await invoke<BuyXmrArgs, BuyXmrResponse>("buy_xmr", {
seller: providerToConcatenatedMultiAddr(seller),
bitcoin_change_address,
monero_receive_address,
});
await invoke<BuyXmrArgs, BuyXmrResponse>(
"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) {
Expand Down
4 changes: 4 additions & 0 deletions swap/src/cli/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ impl Context {

Ok(())
}

pub fn bitcoin_wallet(&self) -> Option<Arc<bitcoin::Wallet>> {
self.bitcoin_wallet.clone()
}
}

impl fmt::Debug for Context {
Expand Down
19 changes: 17 additions & 2 deletions swap/src/cli/api/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>")]
pub bitcoin_change_address: Option<bitcoin::Address>,
#[typeshare(serialized_as = "string")]
pub monero_receive_address: monero::Address,
}
Expand Down Expand Up @@ -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
Expand Down
10 changes: 6 additions & 4 deletions swap/src/cli/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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<bitcoin::Address>,

#[structopt(flatten)]
monero: Monero,
Expand Down
12 changes: 7 additions & 5 deletions swap/src/rpc/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,13 @@ pub fn register_modules(outer_context: Context) -> Result<RpcModule<Context>> {
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,
Expand Down

0 comments on commit 063f9db

Please sign in to comment.