mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-05-02 14:56:10 -04:00
refactor(cli): Builder pattern for constructing Context
This commit is contained in:
parent
015c956273
commit
c562e352f2
3 changed files with 202 additions and 130 deletions
|
@ -1,14 +1,14 @@
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use swap::{
|
use swap::cli::{
|
||||||
cli::api::{
|
api::{
|
||||||
request::{
|
request::{
|
||||||
BalanceArgs, BuyXmrArgs, GetHistoryArgs, ResumeSwapArgs, SuspendCurrentSwapArgs,
|
BalanceArgs, BuyXmrArgs, GetHistoryArgs, ResumeSwapArgs, SuspendCurrentSwapArgs,
|
||||||
WithdrawBtcArgs,
|
WithdrawBtcArgs,
|
||||||
},
|
},
|
||||||
Context,
|
Context, ContextBuilder,
|
||||||
},
|
},
|
||||||
cli::command::{Bitcoin, Monero},
|
command::{Bitcoin, Monero},
|
||||||
};
|
};
|
||||||
use tauri::{Manager, RunEvent};
|
use tauri::{Manager, RunEvent};
|
||||||
|
|
||||||
|
@ -63,25 +63,20 @@ tauri_command!(suspend_current_swap, SuspendCurrentSwapArgs);
|
||||||
|
|
||||||
fn setup<'a>(app: &'a mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
fn setup<'a>(app: &'a mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
tauri::async_runtime::block_on(async {
|
tauri::async_runtime::block_on(async {
|
||||||
let context = Context::build(
|
let context = ContextBuilder::new(true)
|
||||||
Some(Bitcoin {
|
.with_bitcoin(Bitcoin {
|
||||||
bitcoin_electrum_rpc_url: None,
|
bitcoin_electrum_rpc_url: None,
|
||||||
bitcoin_target_block: None,
|
bitcoin_target_block: None,
|
||||||
}),
|
})
|
||||||
Some(Monero {
|
.with_monero(Monero {
|
||||||
monero_daemon_address: None,
|
monero_daemon_address: None,
|
||||||
}),
|
})
|
||||||
None,
|
.with_json(true)
|
||||||
None,
|
.with_debug(true)
|
||||||
true,
|
.with_tauri(app.app_handle().to_owned())
|
||||||
true,
|
.build()
|
||||||
true,
|
.await
|
||||||
None,
|
.expect("failed to create context");
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.with_tauri_handle(app.app_handle().to_owned());
|
|
||||||
|
|
||||||
app.manage(Arc::new(context));
|
app.manage(Arc::new(context));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,6 @@ use anyhow::{anyhow, bail, Context as AnyContext, Error, Result};
|
||||||
use futures::future::try_join_all;
|
use futures::future::try_join_all;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::net::SocketAddr;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, Mutex as SyncMutex, Once};
|
use std::sync::{Arc, Mutex as SyncMutex, Once};
|
||||||
use tauri::AppHandle;
|
use tauri::AppHandle;
|
||||||
|
@ -28,7 +27,6 @@ static START: Once = Once::new();
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
tor_socks5_port: u16,
|
tor_socks5_port: u16,
|
||||||
namespace: XmrBtcNamespace,
|
namespace: XmrBtcNamespace,
|
||||||
server_address: Option<SocketAddr>,
|
|
||||||
pub env_config: EnvConfig,
|
pub env_config: EnvConfig,
|
||||||
seed: Option<Seed>,
|
seed: Option<Seed>,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
|
@ -184,32 +182,109 @@ pub struct Context {
|
||||||
monero_rpc_process: Option<Arc<SyncMutex<monero::WalletRpcProcess>>>,
|
monero_rpc_process: Option<Arc<SyncMutex<monero::WalletRpcProcess>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
/// A conveniant builder struct for [`Context`].
|
||||||
impl Context {
|
#[derive(Debug)]
|
||||||
pub async fn build(
|
#[must_use = "ContextBuilder must be built to be useful"]
|
||||||
bitcoin: Option<Bitcoin>,
|
pub struct ContextBuilder {
|
||||||
monero: Option<Monero>,
|
monero: Option<Monero>,
|
||||||
tor: Option<Tor>,
|
bitcoin: Option<Bitcoin>,
|
||||||
data: Option<PathBuf>,
|
tor: Option<Tor>,
|
||||||
is_testnet: bool,
|
data: Option<PathBuf>,
|
||||||
debug: bool,
|
is_testnet: bool,
|
||||||
json: bool,
|
debug: bool,
|
||||||
server_address: Option<SocketAddr>,
|
json: bool,
|
||||||
) -> Result<Context> {
|
tauri_handle: Option<AppHandle>,
|
||||||
let data_dir = data::data_dir_from(data, is_testnet)?;
|
}
|
||||||
let env_config = env_config_from(is_testnet);
|
|
||||||
|
impl ContextBuilder {
|
||||||
|
/// Start building a context
|
||||||
|
pub fn new(is_testnet: bool) -> Self {
|
||||||
|
if is_testnet {
|
||||||
|
Self::testnet()
|
||||||
|
} else {
|
||||||
|
Self::mainnet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Basic builder with default options for mainnet
|
||||||
|
pub fn mainnet() -> Self {
|
||||||
|
ContextBuilder {
|
||||||
|
monero: None,
|
||||||
|
bitcoin: None,
|
||||||
|
tor: None,
|
||||||
|
data: None,
|
||||||
|
is_testnet: false,
|
||||||
|
debug: false,
|
||||||
|
json: false,
|
||||||
|
tauri_handle: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Basic builder with default options for testnet
|
||||||
|
pub fn testnet() -> Self {
|
||||||
|
let mut builder = Self::mainnet();
|
||||||
|
builder.is_testnet = true;
|
||||||
|
builder
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures the Context to initialize a Monero wallet with the given configuration.
|
||||||
|
pub fn with_monero(mut self, monero: impl Into<Option<Monero>>) -> Self {
|
||||||
|
self.monero = monero.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures the Context to initialize a Bitcoin wallet with the given configuration.
|
||||||
|
pub fn with_bitcoin(mut self, bitcoin: impl Into<Option<Bitcoin>>) -> Self {
|
||||||
|
self.bitcoin = bitcoin.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures the Context to use Tor with the given configuration.
|
||||||
|
pub fn with_tor(mut self, tor: impl Into<Option<Tor>>) -> Self {
|
||||||
|
self.tor = tor.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attach a handle to Tauri to the Context for emitting events etc.
|
||||||
|
pub fn with_tauri(mut self, tauri: impl Into<Option<AppHandle>>) -> Self {
|
||||||
|
self.tauri_handle = tauri.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configures where the data and logs are saved in the filesystem
|
||||||
|
pub fn with_data_dir(mut self, data: impl Into<Option<PathBuf>>) -> Self {
|
||||||
|
self.data = data.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether to include debug level logging messages (default false)
|
||||||
|
pub fn with_debug(mut self, debug: bool) -> Self {
|
||||||
|
self.debug = debug;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set logging format to json (default false)
|
||||||
|
pub fn with_json(mut self, json: bool) -> Self {
|
||||||
|
self.json = json;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the builder, initializes the context by initializing the wallets and other components and returns the Context.
|
||||||
|
pub async fn build(self) -> Result<Context> {
|
||||||
|
let data_dir = data::data_dir_from(self.data, self.is_testnet)?;
|
||||||
|
let env_config = env_config_from(self.is_testnet);
|
||||||
|
|
||||||
START.call_once(|| {
|
START.call_once(|| {
|
||||||
let _ = cli::tracing::init(debug, json, data_dir.join("logs"));
|
let _ = cli::tracing::init(self.debug, self.json, data_dir.join("logs"));
|
||||||
});
|
});
|
||||||
|
|
||||||
let seed = Seed::from_file_or_generate(data_dir.as_path())
|
let seed = Seed::from_file_or_generate(data_dir.as_path())
|
||||||
.context("Failed to read seed in file")?;
|
.context("Failed to read seed in file")?;
|
||||||
|
|
||||||
let bitcoin_wallet = {
|
let bitcoin_wallet = {
|
||||||
if let Some(bitcoin) = bitcoin {
|
if let Some(bitcoin) = self.bitcoin {
|
||||||
let (bitcoin_electrum_rpc_url, bitcoin_target_block) =
|
let (bitcoin_electrum_rpc_url, bitcoin_target_block) =
|
||||||
bitcoin.apply_defaults(is_testnet)?;
|
bitcoin.apply_defaults(self.is_testnet)?;
|
||||||
Some(Arc::new(
|
Some(Arc::new(
|
||||||
init_bitcoin_wallet(
|
init_bitcoin_wallet(
|
||||||
bitcoin_electrum_rpc_url,
|
bitcoin_electrum_rpc_url,
|
||||||
|
@ -226,8 +301,8 @@ impl Context {
|
||||||
};
|
};
|
||||||
|
|
||||||
let (monero_wallet, monero_rpc_process) = {
|
let (monero_wallet, monero_rpc_process) = {
|
||||||
if let Some(monero) = monero {
|
if let Some(monero) = self.monero {
|
||||||
let monero_daemon_address = monero.apply_defaults(is_testnet);
|
let monero_daemon_address = monero.apply_defaults(self.is_testnet);
|
||||||
let (wlt, prc) =
|
let (wlt, prc) =
|
||||||
init_monero_wallet(data_dir.clone(), monero_daemon_address, env_config).await?;
|
init_monero_wallet(data_dir.clone(), monero_daemon_address, env_config).await?;
|
||||||
(Some(Arc::new(wlt)), Some(Arc::new(SyncMutex::new(prc))))
|
(Some(Arc::new(wlt)), Some(Arc::new(SyncMutex::new(prc))))
|
||||||
|
@ -236,7 +311,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let tor_socks5_port = tor.map_or(9050, |tor| tor.tor_socks5_port);
|
let tor_socks5_port = self.tor.map_or(9050, |tor| tor.tor_socks5_port);
|
||||||
|
|
||||||
let context = Context {
|
let context = Context {
|
||||||
db: open_db(data_dir.join("sqlite")).await?,
|
db: open_db(data_dir.join("sqlite")).await?,
|
||||||
|
@ -245,23 +320,24 @@ impl Context {
|
||||||
monero_rpc_process,
|
monero_rpc_process,
|
||||||
config: Config {
|
config: Config {
|
||||||
tor_socks5_port,
|
tor_socks5_port,
|
||||||
namespace: XmrBtcNamespace::from_is_testnet(is_testnet),
|
namespace: XmrBtcNamespace::from_is_testnet(self.is_testnet),
|
||||||
env_config,
|
env_config,
|
||||||
seed: Some(seed),
|
seed: Some(seed),
|
||||||
server_address,
|
debug: self.debug,
|
||||||
debug,
|
json: self.json,
|
||||||
json,
|
is_testnet: self.is_testnet,
|
||||||
is_testnet,
|
|
||||||
data_dir,
|
data_dir,
|
||||||
},
|
},
|
||||||
swap_lock: Arc::new(SwapLock::new()),
|
swap_lock: Arc::new(SwapLock::new()),
|
||||||
tasks: Arc::new(PendingTaskList::default()),
|
tasks: Arc::new(PendingTaskList::default()),
|
||||||
tauri_handle: None,
|
tauri_handle: self.tauri_handle.map(TauriHandle::new),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(context)
|
Ok(context)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Context {
|
||||||
pub fn with_tauri_handle(mut self, tauri_handle: AppHandle) -> Self {
|
pub fn with_tauri_handle(mut self, tauri_handle: AppHandle) -> Self {
|
||||||
self.tauri_handle = Some(TauriHandle::new(tauri_handle));
|
self.tauri_handle = Some(TauriHandle::new(tauri_handle));
|
||||||
|
|
||||||
|
@ -394,7 +470,6 @@ impl Config {
|
||||||
Self {
|
Self {
|
||||||
tor_socks5_port: 9050,
|
tor_socks5_port: 9050,
|
||||||
namespace: XmrBtcNamespace::from_is_testnet(false),
|
namespace: XmrBtcNamespace::from_is_testnet(false),
|
||||||
server_address: None,
|
|
||||||
env_config,
|
env_config,
|
||||||
seed: Some(seed),
|
seed: Some(seed),
|
||||||
debug: false,
|
debug: false,
|
||||||
|
|
|
@ -18,6 +18,8 @@ use structopt::{clap, StructOpt};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::api::ContextBuilder;
|
||||||
|
|
||||||
// See: https://moneroworld.com/
|
// See: https://moneroworld.com/
|
||||||
pub const DEFAULT_MONERO_DAEMON_ADDRESS: &str = "node.community.rino.io:18081";
|
pub const DEFAULT_MONERO_DAEMON_ADDRESS: &str = "node.community.rino.io:18081";
|
||||||
pub const DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET: &str = "stagenet.community.rino.io:38081";
|
pub const DEFAULT_MONERO_DAEMON_ADDRESS_STAGENET: &str = "stagenet.community.rino.io:38081";
|
||||||
|
@ -79,17 +81,15 @@ where
|
||||||
bitcoin_address::validate_is_testnet(bitcoin_change_address, is_testnet)?;
|
bitcoin_address::validate_is_testnet(bitcoin_change_address, is_testnet)?;
|
||||||
|
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(
|
ContextBuilder::new(is_testnet)
|
||||||
Some(bitcoin),
|
.with_bitcoin(bitcoin)
|
||||||
Some(monero),
|
.with_monero(monero)
|
||||||
Some(tor),
|
.with_tor(tor)
|
||||||
data,
|
.with_data_dir(data)
|
||||||
is_testnet,
|
.with_debug(debug)
|
||||||
debug,
|
.with_json(json)
|
||||||
json,
|
.build()
|
||||||
None,
|
.await?,
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
BuyXmrArgs {
|
BuyXmrArgs {
|
||||||
|
@ -104,7 +104,12 @@ where
|
||||||
}
|
}
|
||||||
CliCommand::History => {
|
CliCommand::History => {
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(None, None, None, data, is_testnet, debug, json, None).await?,
|
ContextBuilder::new(is_testnet)
|
||||||
|
.with_data_dir(data)
|
||||||
|
.with_debug(debug)
|
||||||
|
.with_json(json)
|
||||||
|
.build()
|
||||||
|
.await?,
|
||||||
);
|
);
|
||||||
|
|
||||||
GetHistoryArgs {}.request(context.clone()).await?;
|
GetHistoryArgs {}.request(context.clone()).await?;
|
||||||
|
@ -113,7 +118,12 @@ where
|
||||||
}
|
}
|
||||||
CliCommand::Config => {
|
CliCommand::Config => {
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(None, None, None, data, is_testnet, debug, json, None).await?,
|
ContextBuilder::new(is_testnet)
|
||||||
|
.with_data_dir(data)
|
||||||
|
.with_debug(debug)
|
||||||
|
.with_json(json)
|
||||||
|
.build()
|
||||||
|
.await?,
|
||||||
);
|
);
|
||||||
|
|
||||||
GetConfigArgs {}.request(context.clone()).await?;
|
GetConfigArgs {}.request(context.clone()).await?;
|
||||||
|
@ -122,17 +132,13 @@ where
|
||||||
}
|
}
|
||||||
CliCommand::Balance { bitcoin } => {
|
CliCommand::Balance { bitcoin } => {
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(
|
ContextBuilder::new(is_testnet)
|
||||||
Some(bitcoin),
|
.with_bitcoin(bitcoin)
|
||||||
None,
|
.with_data_dir(data)
|
||||||
None,
|
.with_debug(debug)
|
||||||
data,
|
.with_json(json)
|
||||||
is_testnet,
|
.build()
|
||||||
debug,
|
.await?,
|
||||||
json,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
BalanceArgs {
|
BalanceArgs {
|
||||||
|
@ -150,17 +156,15 @@ where
|
||||||
tor,
|
tor,
|
||||||
} => {
|
} => {
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(
|
ContextBuilder::new(is_testnet)
|
||||||
Some(bitcoin),
|
.with_bitcoin(bitcoin)
|
||||||
Some(monero),
|
.with_monero(monero)
|
||||||
Some(tor),
|
.with_tor(tor)
|
||||||
data,
|
.with_data_dir(data)
|
||||||
is_testnet,
|
.with_debug(debug)
|
||||||
debug,
|
.with_json(json)
|
||||||
json,
|
.build()
|
||||||
server_address,
|
.await?,
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
StartDaemonArgs { server_address }
|
StartDaemonArgs { server_address }
|
||||||
|
@ -177,17 +181,13 @@ where
|
||||||
let address = bitcoin_address::validate_is_testnet(address, is_testnet)?;
|
let address = bitcoin_address::validate_is_testnet(address, is_testnet)?;
|
||||||
|
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(
|
ContextBuilder::new(is_testnet)
|
||||||
Some(bitcoin),
|
.with_bitcoin(bitcoin)
|
||||||
None,
|
.with_data_dir(data)
|
||||||
None,
|
.with_debug(debug)
|
||||||
data,
|
.with_json(json)
|
||||||
is_testnet,
|
.build()
|
||||||
debug,
|
.await?,
|
||||||
json,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
WithdrawBtcArgs { amount, address }
|
WithdrawBtcArgs { amount, address }
|
||||||
|
@ -203,17 +203,15 @@ where
|
||||||
tor,
|
tor,
|
||||||
} => {
|
} => {
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(
|
ContextBuilder::new(is_testnet)
|
||||||
Some(bitcoin),
|
.with_bitcoin(bitcoin)
|
||||||
Some(monero),
|
.with_monero(monero)
|
||||||
Some(tor),
|
.with_tor(tor)
|
||||||
data,
|
.with_data_dir(data)
|
||||||
is_testnet,
|
.with_debug(debug)
|
||||||
debug,
|
.with_json(json)
|
||||||
json,
|
.build()
|
||||||
None,
|
.await?,
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ResumeSwapArgs { swap_id }.request(context.clone()).await?;
|
ResumeSwapArgs { swap_id }.request(context.clone()).await?;
|
||||||
|
@ -226,17 +224,14 @@ where
|
||||||
tor,
|
tor,
|
||||||
} => {
|
} => {
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(
|
ContextBuilder::new(is_testnet)
|
||||||
Some(bitcoin),
|
.with_bitcoin(bitcoin)
|
||||||
None,
|
.with_tor(tor)
|
||||||
Some(tor),
|
.with_data_dir(data)
|
||||||
data,
|
.with_debug(debug)
|
||||||
is_testnet,
|
.with_json(json)
|
||||||
debug,
|
.build()
|
||||||
json,
|
.await?,
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
CancelAndRefundArgs { swap_id }
|
CancelAndRefundArgs { swap_id }
|
||||||
|
@ -250,7 +245,13 @@ where
|
||||||
tor,
|
tor,
|
||||||
} => {
|
} => {
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(None, None, Some(tor), data, is_testnet, debug, json, None).await?,
|
ContextBuilder::new(is_testnet)
|
||||||
|
.with_tor(tor)
|
||||||
|
.with_data_dir(data)
|
||||||
|
.with_debug(debug)
|
||||||
|
.with_json(json)
|
||||||
|
.build()
|
||||||
|
.await?,
|
||||||
);
|
);
|
||||||
|
|
||||||
ListSellersArgs { rendezvous_point }
|
ListSellersArgs { rendezvous_point }
|
||||||
|
@ -261,17 +262,13 @@ where
|
||||||
}
|
}
|
||||||
CliCommand::ExportBitcoinWallet { bitcoin } => {
|
CliCommand::ExportBitcoinWallet { bitcoin } => {
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(
|
ContextBuilder::new(is_testnet)
|
||||||
Some(bitcoin),
|
.with_bitcoin(bitcoin)
|
||||||
None,
|
.with_data_dir(data)
|
||||||
None,
|
.with_debug(debug)
|
||||||
data,
|
.with_json(json)
|
||||||
is_testnet,
|
.build()
|
||||||
debug,
|
.await?,
|
||||||
json,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ExportBitcoinWalletArgs {}.request(context.clone()).await?;
|
ExportBitcoinWalletArgs {}.request(context.clone()).await?;
|
||||||
|
@ -282,7 +279,12 @@ where
|
||||||
swap_id: SwapId { swap_id },
|
swap_id: SwapId { swap_id },
|
||||||
} => {
|
} => {
|
||||||
let context = Arc::new(
|
let context = Arc::new(
|
||||||
Context::build(None, None, None, data, is_testnet, debug, json, None).await?,
|
ContextBuilder::new(is_testnet)
|
||||||
|
.with_data_dir(data)
|
||||||
|
.with_debug(debug)
|
||||||
|
.with_json(json)
|
||||||
|
.build()
|
||||||
|
.await?,
|
||||||
);
|
);
|
||||||
|
|
||||||
MoneroRecoveryArgs { swap_id }
|
MoneroRecoveryArgs { swap_id }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue