mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-06-20 04:44:10 -04:00
wip: WithdrawDialog migrated to Tauri IPC
This commit is contained in:
parent
92034a5be8
commit
47821cbe79
14 changed files with 185 additions and 166 deletions
|
@ -6,6 +6,7 @@ import { ReactNode, useEffect, useState } from "react";
|
||||||
interface IpcInvokeButtonProps<T> {
|
interface IpcInvokeButtonProps<T> {
|
||||||
onSuccess?: (data: T) => void;
|
onSuccess?: (data: T) => void;
|
||||||
onClick: () => Promise<T>;
|
onClick: () => Promise<T>;
|
||||||
|
onPendingChange?: (bool) => void;
|
||||||
isLoadingOverride?: boolean;
|
isLoadingOverride?: boolean;
|
||||||
isIconButton?: boolean;
|
isIconButton?: boolean;
|
||||||
loadIcon?: ReactNode;
|
loadIcon?: ReactNode;
|
||||||
|
@ -24,26 +25,22 @@ export default function PromiseInvokeButton<T>({
|
||||||
isIconButton,
|
isIconButton,
|
||||||
displayErrorSnackbar,
|
displayErrorSnackbar,
|
||||||
tooltipTitle,
|
tooltipTitle,
|
||||||
|
onPendingChange,
|
||||||
...rest
|
...rest
|
||||||
}: IpcInvokeButtonProps<T> & ButtonProps) {
|
}: IpcInvokeButtonProps<T> & ButtonProps) {
|
||||||
const { enqueueSnackbar } = useSnackbar();
|
const { enqueueSnackbar } = useSnackbar();
|
||||||
|
|
||||||
const [isPending, setIsPending] = useState(false);
|
const [isPending, setIsPending] = useState(false);
|
||||||
const [hasMinLoadingTimePassed, setHasMinLoadingTimePassed] = useState(false);
|
|
||||||
|
|
||||||
const isLoading = (isPending && hasMinLoadingTimePassed) || isLoadingOverride;
|
const isLoading = isPending || isLoadingOverride;
|
||||||
const actualEndIcon = isLoading
|
const actualEndIcon = isLoading
|
||||||
? loadIcon || <CircularProgress size="1em" />
|
? loadIcon || <CircularProgress size="1em" />
|
||||||
: endIcon;
|
: endIcon;
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setHasMinLoadingTimePassed(false);
|
|
||||||
setTimeout(() => setHasMinLoadingTimePassed(true), 100);
|
|
||||||
}, [isPending]);
|
|
||||||
|
|
||||||
async function handleClick(event: React.MouseEvent<HTMLButtonElement>) {
|
async function handleClick(event: React.MouseEvent<HTMLButtonElement>) {
|
||||||
if (!isPending) {
|
if (!isPending) {
|
||||||
try {
|
try {
|
||||||
|
onPendingChange?.(true);
|
||||||
setIsPending(true);
|
setIsPending(true);
|
||||||
let result = await onClick();
|
let result = await onClick();
|
||||||
onSuccess?.(result);
|
onSuccess?.(result);
|
||||||
|
@ -56,21 +53,15 @@ export default function PromiseInvokeButton<T>({
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsPending(false);
|
setIsPending(false);
|
||||||
|
onPendingChange?.(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDisabled = disabled || isLoading;
|
const isDisabled = disabled || isLoading;
|
||||||
|
|
||||||
return (
|
return isIconButton ? (
|
||||||
<Tooltip title={tooltipTitle}>
|
<IconButton onClick={handleClick} disabled={isDisabled} {...(rest as any)}>
|
||||||
<span>
|
|
||||||
{isIconButton ? (
|
|
||||||
<IconButton
|
|
||||||
onClick={handleClick}
|
|
||||||
disabled={isDisabled}
|
|
||||||
{...(rest as any)}
|
|
||||||
>
|
|
||||||
{actualEndIcon}
|
{actualEndIcon}
|
||||||
</IconButton>
|
</IconButton>
|
||||||
) : (
|
) : (
|
||||||
|
@ -80,8 +71,5 @@ export default function PromiseInvokeButton<T>({
|
||||||
endIcon={actualEndIcon}
|
endIcon={actualEndIcon}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import { Dialog } from '@material-ui/core';
|
import { Button, Dialog, DialogActions } from "@material-ui/core";
|
||||||
import { useAppDispatch, useIsRpcEndpointBusy } from 'store/hooks';
|
import { useAppDispatch, useIsRpcEndpointBusy } from "store/hooks";
|
||||||
import { RpcMethod } from 'models/rpcModel';
|
import { RpcMethod } from "models/rpcModel";
|
||||||
import { rpcResetWithdrawTxId } from 'store/features/rpcSlice';
|
import { rpcResetWithdrawTxId } from "store/features/rpcSlice";
|
||||||
import WithdrawStatePage from './WithdrawStatePage';
|
import WithdrawStatePage from "./WithdrawStatePage";
|
||||||
import DialogHeader from '../DialogHeader';
|
import DialogHeader from "../DialogHeader";
|
||||||
|
import PromiseInvokeButton from "renderer/components/PromiseInvokeButton";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { withdrawBtc } from "renderer/rpc";
|
||||||
|
import BtcTxInMempoolPageContent from "./pages/BitcoinWithdrawTxInMempoolPage";
|
||||||
|
import AddressInputPage from "./pages/AddressInputPage";
|
||||||
|
|
||||||
export default function WithdrawDialog({
|
export default function WithdrawDialog({
|
||||||
open,
|
open,
|
||||||
|
@ -12,23 +17,60 @@ export default function WithdrawDialog({
|
||||||
open: boolean;
|
open: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) {
|
}) {
|
||||||
const isRpcEndpointBusy = useIsRpcEndpointBusy(RpcMethod.WITHDRAW_BTC);
|
const [pending, setPending] = useState(false);
|
||||||
const dispatch = useAppDispatch();
|
const [withdrawTxId, setWithdrawTxId] = useState<string | null>(null);
|
||||||
|
const [withdrawAddressValid, setWithdrawAddressValid] = useState(false);
|
||||||
|
const [withdrawAddress, setWithdrawAddress] = useState<string>("");
|
||||||
|
|
||||||
function onCancel() {
|
function onCancel() {
|
||||||
if (!isRpcEndpointBusy) {
|
if (!pending) {
|
||||||
|
setWithdrawTxId(null);
|
||||||
|
setWithdrawAddress("");
|
||||||
onClose();
|
onClose();
|
||||||
dispatch(rpcResetWithdrawTxId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This prevents an issue where the Dialog is shown for a split second without a present withdraw state
|
// This prevents an issue where the Dialog is shown for a split second without a present withdraw state
|
||||||
if (!open && !isRpcEndpointBusy) return null;
|
if (!open) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open onClose={onCancel} maxWidth="sm" fullWidth>
|
<Dialog open onClose={onCancel} maxWidth="sm" fullWidth>
|
||||||
<DialogHeader title="Withdraw Bitcoin" />
|
<DialogHeader title="Withdraw Bitcoin" />
|
||||||
<WithdrawStatePage onCancel={onCancel} />
|
{withdrawTxId === null ? (
|
||||||
|
<AddressInputPage
|
||||||
|
setWithdrawAddress={setWithdrawAddress}
|
||||||
|
withdrawAddress={withdrawAddress}
|
||||||
|
setWithdrawAddressValid={setWithdrawAddressValid}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<BtcTxInMempoolPageContent
|
||||||
|
withdrawTxId={withdrawTxId}
|
||||||
|
onCancel={onCancel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<DialogActions>
|
||||||
|
{withdrawTxId === null ? (
|
||||||
|
<PromiseInvokeButton
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
disabled={!withdrawAddressValid}
|
||||||
|
onClick={() => withdrawBtc(withdrawAddress)}
|
||||||
|
onPendingChange={(pending) => {
|
||||||
|
console.log("pending", pending);
|
||||||
|
setPending(pending);
|
||||||
|
}}
|
||||||
|
onSuccess={(txId) => {
|
||||||
|
setWithdrawTxId(txId);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Withdraw
|
||||||
|
</PromiseInvokeButton>
|
||||||
|
) : (
|
||||||
|
<Button onClick={onCancel} color="primary" disabled={pending}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</DialogActions>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
import { useState } from 'react';
|
import { useState } from "react";
|
||||||
import { Button, DialogActions, DialogContentText } from '@material-ui/core';
|
import { Button, DialogActions, DialogContentText } from "@material-ui/core";
|
||||||
import BitcoinAddressTextField from '../../../inputs/BitcoinAddressTextField';
|
import BitcoinAddressTextField from "../../../inputs/BitcoinAddressTextField";
|
||||||
import WithdrawDialogContent from '../WithdrawDialogContent';
|
import WithdrawDialogContent from "../WithdrawDialogContent";
|
||||||
import IpcInvokeButton from '../../../IpcInvokeButton';
|
import IpcInvokeButton from "../../../IpcInvokeButton";
|
||||||
|
|
||||||
export default function AddressInputPage({
|
export default function AddressInputPage({
|
||||||
onCancel,
|
withdrawAddress,
|
||||||
|
setWithdrawAddress,
|
||||||
|
setWithdrawAddressValid,
|
||||||
}: {
|
}: {
|
||||||
onCancel: () => void;
|
withdrawAddress: string;
|
||||||
|
setWithdrawAddress: (address: string) => void;
|
||||||
|
setWithdrawAddressValid: (valid: boolean) => void;
|
||||||
}) {
|
}) {
|
||||||
const [withdrawAddressValid, setWithdrawAddressValid] = useState(false);
|
|
||||||
const [withdrawAddress, setWithdrawAddress] = useState('');
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<WithdrawDialogContent>
|
<WithdrawDialogContent>
|
||||||
|
@ -28,22 +29,6 @@ export default function AddressInputPage({
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
</WithdrawDialogContent>
|
</WithdrawDialogContent>
|
||||||
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={onCancel} variant="text">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<IpcInvokeButton
|
|
||||||
disabled={!withdrawAddressValid}
|
|
||||||
ipcChannel="spawn-withdraw-btc"
|
|
||||||
ipcArgs={[withdrawAddress]}
|
|
||||||
color="primary"
|
|
||||||
variant="contained"
|
|
||||||
requiresRpc
|
|
||||||
>
|
|
||||||
Withdraw
|
|
||||||
</IpcInvokeButton>
|
|
||||||
</DialogActions>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Button, DialogActions, DialogContentText } from '@material-ui/core';
|
import { Button, DialogActions, DialogContentText } from "@material-ui/core";
|
||||||
import BitcoinTransactionInfoBox from '../../swap/BitcoinTransactionInfoBox';
|
import BitcoinTransactionInfoBox from "../../swap/BitcoinTransactionInfoBox";
|
||||||
import WithdrawDialogContent from '../WithdrawDialogContent';
|
import WithdrawDialogContent from "../WithdrawDialogContent";
|
||||||
|
|
||||||
export default function BtcTxInMempoolPageContent({
|
export default function BtcTxInMempoolPageContent({
|
||||||
withdrawTxId,
|
withdrawTxId,
|
||||||
|
@ -23,14 +23,6 @@ export default function BtcTxInMempoolPageContent({
|
||||||
additionalContent={null}
|
additionalContent={null}
|
||||||
/>
|
/>
|
||||||
</WithdrawDialogContent>
|
</WithdrawDialogContent>
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={onCancel} variant="text">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button onClick={onCancel} color="primary" variant="contained">
|
|
||||||
Done
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import { Button, DialogActions } from '@material-ui/core';
|
|
||||||
import CircularProgressWithSubtitle from '../../swap/CircularProgressWithSubtitle';
|
|
||||||
import WithdrawDialogContent from '../WithdrawDialogContent';
|
|
||||||
|
|
||||||
export default function InitiatedPage({ onCancel }: { onCancel: () => void }) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<WithdrawDialogContent>
|
|
||||||
<CircularProgressWithSubtitle description="Withdrawing Bitcoin" />
|
|
||||||
</WithdrawDialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={onCancel} variant="text">
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button disabled color="primary" variant="contained">
|
|
||||||
Done
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ import { store } from "./store/storeRenderer";
|
||||||
import { rpcSetBalance, rpcSetSwapInfo } from "store/features/rpcSlice";
|
import { rpcSetBalance, rpcSetSwapInfo } from "store/features/rpcSlice";
|
||||||
|
|
||||||
export async function checkBitcoinBalance() {
|
export async function checkBitcoinBalance() {
|
||||||
// TODO: use tauri-bindgen here
|
|
||||||
const response = (await invoke("get_balance")) as {
|
const response = (await invoke("get_balance")) as {
|
||||||
balance: number;
|
balance: number;
|
||||||
};
|
};
|
||||||
|
@ -16,3 +15,17 @@ export async function getRawSwapInfos() {
|
||||||
|
|
||||||
(response as any[]).forEach((info) => store.dispatch(rpcSetSwapInfo(info)));
|
(response as any[]).forEach((info) => store.dispatch(rpcSetSwapInfo(info)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function withdrawBtc(address: string): Promise<string> {
|
||||||
|
const response = (await invoke("withdraw_btc", {
|
||||||
|
args: {
|
||||||
|
address,
|
||||||
|
amount: null,
|
||||||
|
},
|
||||||
|
})) as {
|
||||||
|
txid: string;
|
||||||
|
amount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
return response.txid;
|
||||||
|
}
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
import { ExtendedProviderStatus } from 'models/apiModel';
|
import { ExtendedProviderStatus } from "models/apiModel";
|
||||||
|
|
||||||
export const isTestnet = () =>
|
export const isTestnet = () => true;
|
||||||
false
|
|
||||||
|
|
||||||
export const isExternalRpc = () =>
|
export const isExternalRpc = () => true;
|
||||||
true
|
|
||||||
|
|
||||||
export const isDevelopment =
|
export const isDevelopment = true;
|
||||||
true
|
|
||||||
|
|
||||||
export function getStubTestnetProvider(): ExtendedProviderStatus | null {
|
export function getStubTestnetProvider(): ExtendedProviderStatus | null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getPlatform = () => {
|
export const getPlatform = () => {
|
||||||
return 'mac';
|
return "mac";
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use once_cell::sync::OnceCell;
|
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
use std::sync::Arc;
|
||||||
use swap::{
|
use swap::{
|
||||||
api::{
|
api::{
|
||||||
request::{
|
request::{
|
||||||
get_balance as get_balance_impl, get_swap_infos_all as get_swap_infos_all_impl,
|
get_balance as get_balance_impl, get_swap_infos_all as get_swap_infos_all_impl,
|
||||||
BalanceArgs, BalanceResponse, GetSwapInfoResponse,
|
withdraw_btc as withdraw_btc_impl, BalanceArgs, BalanceResponse, GetSwapInfoResponse,
|
||||||
|
WithdrawBtcArgs, WithdrawBtcResponse,
|
||||||
},
|
},
|
||||||
Context,
|
Context,
|
||||||
},
|
},
|
||||||
cli::command::{Bitcoin, Monero},
|
cli::command::{Bitcoin, Monero},
|
||||||
};
|
};
|
||||||
|
use tauri::{Manager, State};
|
||||||
// Lazy load the Context
|
|
||||||
static CONTEXT: OnceCell<Arc<Context>> = OnceCell::new();
|
|
||||||
|
|
||||||
trait ToStringResult<T> {
|
trait ToStringResult<T> {
|
||||||
fn to_string_result(self) -> Result<T, String>;
|
fn to_string_result(self) -> Result<T, String>;
|
||||||
|
@ -31,24 +28,46 @@ impl<T, E: ToString> ToStringResult<T> for Result<T, E> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn get_balance() -> Result<BalanceResponse, String> {
|
async fn get_balance(context: State<'_, Arc<Context>>) -> Result<BalanceResponse, String> {
|
||||||
let context = CONTEXT.get().unwrap();
|
|
||||||
|
|
||||||
get_balance_impl(
|
get_balance_impl(
|
||||||
BalanceArgs {
|
BalanceArgs {
|
||||||
force_refresh: true,
|
force_refresh: true,
|
||||||
},
|
},
|
||||||
context.clone(),
|
context.inner().clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.to_string_result()
|
.to_string_result()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn get_swap_infos_all() -> Result<Vec<GetSwapInfoResponse>, String> {
|
async fn get_swap_infos_all(
|
||||||
let context = CONTEXT.get().unwrap();
|
context: State<'_, Arc<Context>>,
|
||||||
|
) -> Result<Vec<GetSwapInfoResponse>, String> {
|
||||||
|
get_swap_infos_all_impl(context.inner().clone())
|
||||||
|
.await
|
||||||
|
.to_string_result()
|
||||||
|
}
|
||||||
|
|
||||||
get_swap_infos_all_impl(context.clone())
|
/*macro_rules! tauri_command {
|
||||||
|
($command_name:ident, $command_args:ident, $command_response:ident) => {
|
||||||
|
#[tauri::command]
|
||||||
|
async fn $command_name(
|
||||||
|
context: State<'_, Context>,
|
||||||
|
args: $command_args,
|
||||||
|
) -> Result<$command_response, String> {
|
||||||
|
swap::api::request::$command_name(args, context)
|
||||||
|
.await
|
||||||
|
.to_string_result()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}*/
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn withdraw_btc(
|
||||||
|
context: State<'_, Arc<Context>>,
|
||||||
|
args: WithdrawBtcArgs,
|
||||||
|
) -> Result<WithdrawBtcResponse, String> {
|
||||||
|
withdraw_btc_impl(args, context.inner().clone())
|
||||||
.await
|
.await
|
||||||
.to_string_result()
|
.to_string_result()
|
||||||
}
|
}
|
||||||
|
@ -73,9 +92,7 @@ fn setup<'a>(app: &'a mut tauri::App) -> Result<(), Box<dyn std::error::Error>>
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
CONTEXT
|
app.manage(Arc::new(context));
|
||||||
.set(Arc::new(context))
|
|
||||||
.expect("Failed to initialize cli context");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -84,7 +101,11 @@ fn setup<'a>(app: &'a mut tauri::App) -> Result<(), Box<dyn std::error::Error>>
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.invoke_handler(tauri::generate_handler![get_balance, get_swap_infos_all])
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
get_balance,
|
||||||
|
get_swap_infos_all,
|
||||||
|
withdraw_btc
|
||||||
|
])
|
||||||
.setup(setup)
|
.setup(setup)
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|
|
@ -21,7 +21,7 @@ use std::future::Future;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tracing::{debug_span, field, Instrument, Span};
|
use tracing::Instrument;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
|
@ -53,9 +53,9 @@ pub struct MoneroRecoveryArgs {
|
||||||
pub swap_id: Uuid,
|
pub swap_id: Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct WithdrawBtcArgs {
|
pub struct WithdrawBtcArgs {
|
||||||
pub amount: Option<Amount>,
|
pub amount: Option<u64>,
|
||||||
pub address: bitcoin::Address,
|
pub address: bitcoin::Address,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,6 +119,12 @@ pub struct GetSwapInfoResponse {
|
||||||
pub timelock: Option<ExpiredTimelocks>,
|
pub timelock: Option<ExpiredTimelocks>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct WithdrawBtcResponse {
|
||||||
|
amount: u64,
|
||||||
|
txid: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Seller {
|
pub struct Seller {
|
||||||
pub peer_id: String,
|
pub peer_id: String,
|
||||||
|
@ -645,7 +651,7 @@ pub async fn get_config(context: Arc<Context>) -> Result<serde_json::Value> {
|
||||||
pub async fn withdraw_btc(
|
pub async fn withdraw_btc(
|
||||||
withdraw_btc: WithdrawBtcArgs,
|
withdraw_btc: WithdrawBtcArgs,
|
||||||
context: Arc<Context>,
|
context: Arc<Context>,
|
||||||
) -> Result<serde_json::Value> {
|
) -> Result<WithdrawBtcResponse> {
|
||||||
let WithdrawBtcArgs { address, amount } = withdraw_btc;
|
let WithdrawBtcArgs { address, amount } = withdraw_btc;
|
||||||
let bitcoin_wallet = context
|
let bitcoin_wallet = context
|
||||||
.bitcoin_wallet
|
.bitcoin_wallet
|
||||||
|
@ -653,7 +659,7 @@ pub async fn withdraw_btc(
|
||||||
.context("Could not get Bitcoin wallet")?;
|
.context("Could not get Bitcoin wallet")?;
|
||||||
|
|
||||||
let amount = match amount {
|
let amount = match amount {
|
||||||
Some(amount) => amount,
|
Some(amount) => Amount::from_sat(amount),
|
||||||
None => {
|
None => {
|
||||||
bitcoin_wallet
|
bitcoin_wallet
|
||||||
.max_giveable(address.script_pubkey().len())
|
.max_giveable(address.script_pubkey().len())
|
||||||
|
@ -669,11 +675,10 @@ pub async fn withdraw_btc(
|
||||||
.broadcast(signed_tx.clone(), "withdraw")
|
.broadcast(signed_tx.clone(), "withdraw")
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(json!({
|
Ok(WithdrawBtcResponse {
|
||||||
"signed_tx": signed_tx,
|
txid: signed_tx.txid().to_string(),
|
||||||
"amount": amount.to_sat(),
|
amount: amount.to_sat(),
|
||||||
"txid": signed_tx.txid(),
|
})
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(fields(method = "start_daemon"), skip(context))]
|
#[tracing::instrument(fields(method = "start_daemon"), skip(context))]
|
||||||
|
@ -848,17 +853,7 @@ impl Request {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_cmd(self, context: Arc<Context>) -> Result<Box<dyn erased_serde::Serialize>> {
|
pub async fn call(self, _: Arc<Context>) -> Result<JsonValue> {
|
||||||
match self.cmd {
|
|
||||||
Method::Balance(args) => {
|
|
||||||
let response = get_balance(args, context).await?;
|
|
||||||
Ok(Box::new(response) as Box<dyn erased_serde::Serialize>)
|
|
||||||
}
|
|
||||||
_ => todo!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn call(self, context: Arc<Context>) -> Result<JsonValue> {
|
|
||||||
unreachable!("This function should never be called")
|
unreachable!("This function should never be called")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#![warn(
|
#![warn(
|
||||||
unused_extern_crates,
|
unused_extern_crates,
|
||||||
missing_copy_implementations,
|
missing_copy_implementations,
|
||||||
rust_2018_idioms,
|
|
||||||
clippy::cast_possible_truncation,
|
clippy::cast_possible_truncation,
|
||||||
clippy::cast_sign_loss,
|
clippy::cast_sign_loss,
|
||||||
clippy::fallible_impl_from,
|
clippy::fallible_impl_from,
|
||||||
|
@ -12,8 +11,10 @@
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use crate::cli::command::{parse_args_and_apply_defaults, ParseResult};
|
use crate::{
|
||||||
use crate::common::check_latest_version;
|
cli::command::{parse_args_and_apply_defaults, ParseResult},
|
||||||
|
common::check_latest_version,
|
||||||
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::api::request::{
|
use crate::api::request::{
|
||||||
buy_xmr, cancel_and_refund, export_bitcoin_wallet, get_balance, get_config, get_history,
|
buy_xmr, cancel_and_refund, export_bitcoin_wallet, get_balance, get_config, get_history,
|
||||||
list_sellers, monero_recovery, resume_swap, start_daemon, withdraw_btc, BalanceArgs,
|
list_sellers, monero_recovery, resume_swap, start_daemon, withdraw_btc, BalanceArgs,
|
||||||
BuyXmrArgs, CancelAndRefundArgs, ListSellersArgs, Method, MoneroRecoveryArgs, Request,
|
BuyXmrArgs, CancelAndRefundArgs, ListSellersArgs, MoneroRecoveryArgs, ResumeArgs,
|
||||||
ResumeArgs, StartDaemonArgs, WithdrawBtcArgs,
|
StartDaemonArgs, WithdrawBtcArgs,
|
||||||
};
|
};
|
||||||
use crate::api::Context;
|
use crate::api::Context;
|
||||||
use crate::bitcoin::{bitcoin_address, Amount};
|
use crate::bitcoin::{bitcoin_address, Amount};
|
||||||
|
@ -10,7 +10,6 @@ use crate::monero;
|
||||||
use crate::monero::monero_address;
|
use crate::monero::monero_address;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use libp2p::core::Multiaddr;
|
use libp2p::core::Multiaddr;
|
||||||
use serde_json::Value;
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
@ -193,7 +192,14 @@ where
|
||||||
.await?,
|
.await?,
|
||||||
);
|
);
|
||||||
|
|
||||||
withdraw_btc(WithdrawBtcArgs { amount, address }, context).await?;
|
withdraw_btc(
|
||||||
|
WithdrawBtcArgs {
|
||||||
|
amount: amount.map(Amount::to_sat),
|
||||||
|
address,
|
||||||
|
},
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use crate::api::Context;
|
use crate::api::Context;
|
||||||
use std::{net::SocketAddr, sync::Arc};
|
use std::net::SocketAddr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tower_http::cors::CorsLayer;
|
use tower_http::cors::CorsLayer;
|
||||||
|
|
||||||
use jsonrpsee::{
|
use jsonrpsee::{
|
||||||
core::server::host_filtering::AllowHosts,
|
core::server::host_filtering::AllowHosts,
|
||||||
server::{RpcModule, ServerBuilder, ServerHandle},
|
server::{ServerBuilder, ServerHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod methods;
|
pub mod methods;
|
||||||
|
|
|
@ -13,7 +13,6 @@ use jsonrpsee::server::RpcModule;
|
||||||
use libp2p::core::Multiaddr;
|
use libp2p::core::Multiaddr;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
trait ConvertToJsonRpseeError<T> {
|
trait ConvertToJsonRpseeError<T> {
|
||||||
|
@ -29,7 +28,7 @@ impl<T> ConvertToJsonRpseeError<T> for Result<T, anyhow::Error> {
|
||||||
pub fn register_modules(outer_context: Context) -> Result<RpcModule<Context>> {
|
pub fn register_modules(outer_context: Context) -> Result<RpcModule<Context>> {
|
||||||
let mut module = RpcModule::new(outer_context);
|
let mut module = RpcModule::new(outer_context);
|
||||||
|
|
||||||
module.register_async_method("suspend_current_swap", |params, context| async move {
|
module.register_async_method("suspend_current_swap", |_, context| async move {
|
||||||
suspend_current_swap(context).await.to_jsonrpsee_result()
|
suspend_current_swap(context).await.to_jsonrpsee_result()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -66,11 +65,11 @@ pub fn register_modules(outer_context: Context) -> Result<RpcModule<Context>> {
|
||||||
.to_jsonrpsee_result()
|
.to_jsonrpsee_result()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
module.register_async_method("get_history", |params, context| async move {
|
module.register_async_method("get_history", |_, context| async move {
|
||||||
get_history(context).await.to_jsonrpsee_result()
|
get_history(context).await.to_jsonrpsee_result()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
module.register_async_method("get_raw_states", |params, context| async move {
|
module.register_async_method("get_raw_states", |_, context| async move {
|
||||||
get_raw_states(context).await.to_jsonrpsee_result()
|
get_raw_states(context).await.to_jsonrpsee_result()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
@ -131,7 +130,8 @@ pub fn register_modules(outer_context: Context) -> Result<RpcModule<Context>> {
|
||||||
::bitcoin::Amount::from_str_in(amount_str, ::bitcoin::Denomination::Bitcoin)
|
::bitcoin::Amount::from_str_in(amount_str, ::bitcoin::Denomination::Bitcoin)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
jsonrpsee_core::Error::Custom("Unable to parse amount".to_string())
|
jsonrpsee_core::Error::Custom("Unable to parse amount".to_string())
|
||||||
})?,
|
})?
|
||||||
|
.to_sat(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -224,7 +224,7 @@ pub fn register_modules(outer_context: Context) -> Result<RpcModule<Context>> {
|
||||||
.to_jsonrpsee_result()
|
.to_jsonrpsee_result()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
module.register_async_method("get_current_swap", |params, context| async move {
|
module.register_async_method("get_current_swap", |_, context| async move {
|
||||||
get_current_swap(context).await.to_jsonrpsee_result()
|
get_current_swap(context).await.to_jsonrpsee_result()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue