1use std::net::SocketAddr;
4use std::path::PathBuf;
5
6use clap::{ArgAction, Args, Parser, Subcommand};
7use paralock::EvmChain;
8
9const ABOUT: &str = "Trustless swaps between shielded ZEC and stablecoins on EVM chains, and a Paralock market maker";
10
11const AFTER_HELP: &str = "\
12Configuration:
13 paralock.toml, from --config <file>, $PARALOCK_CONFIG, ./paralock.toml or
14 <state>/paralock.toml. Keys come from environment variables or key files
15 (key_env, key_file), never from the file itself unless allow_inline_keys.
16
17Devnet:
18 --devnet reads devnet.json from <checkout>/.paralock inside a Paralock
19 checkout, else $PARALOCK_HOME, else ~/.paralock. Starting and stopping the
20 devnet (Zcash regtest, anvil, contracts) stays in the npm CLI:
21 `npx paralock dev`, `npx paralock devnet up|status|down`.
22
23Exit codes:
24 0 success, 1 failure, 2 usage or configuration error, 3 a swap failed after
25 locking ZEC (refunded, or refundable with `paralock refund <id>`).
26
27Experimental and not audited. The Zcash leg is transparent on chain.";
28
29#[derive(Debug, Parser)]
31#[command(name = "paralock", version, about = ABOUT, after_help = AFTER_HELP, propagate_version = true)]
32pub struct Cli {
33 #[command(flatten)]
35 pub global: GlobalArgs,
36 #[command(subcommand)]
38 pub command: Command,
39}
40
41#[derive(Debug, Args)]
43pub struct GlobalArgs {
44 #[arg(long, global = true)]
46 pub devnet: bool,
47 #[arg(long, short = 'c', global = true, value_name = "FILE")]
49 pub config: Option<PathBuf>,
50 #[arg(long, global = true)]
52 pub json: bool,
53 #[arg(long, global = true, value_name = "URL")]
55 pub maker_url: Option<String>,
56 #[arg(long, global = true, value_name = "N")]
58 pub evm_account: Option<usize>,
59 #[arg(long, global = true)]
61 pub lightwalletd: bool,
62 #[arg(long, short = 'v', global = true, action = ArgAction::Count)]
64 pub verbose: u8,
65}
66
67#[derive(Debug, Subcommand)]
69pub enum Command {
70 Doctor(DoctorArgs),
72 Quote(QuoteArgs),
74 Swap(SwapArgs),
76 Pay(PayArgs),
78 Swaps(SwapsArgs),
80 Resume(ResumeArgs),
82 Refund(RefundArgs),
84 Maker(MakerArgs),
86 Version,
88}
89
90fn chain(value: &str) -> Result<EvmChain, String> {
91 EvmChain::parse(&value.to_ascii_lowercase())
92 .ok_or_else(|| "expected tempo, base, ethereum, arbitrum, robinhood or hyperevm".to_owned())
93}
94
95#[derive(Debug, Args)]
97pub struct DoctorArgs {
98 #[arg(long)]
100 pub maker: bool,
101}
102
103#[derive(Debug, Args)]
105#[command(group = clap::ArgGroup::new("amount").required(true).args(["zec", "usd"]))]
106pub struct QuoteArgs {
107 #[arg(long, value_name = "ZEC")]
109 pub zec: Option<String>,
110 #[arg(long, value_name = "USD")]
112 pub usd: Option<String>,
113 #[arg(long, value_parser = chain)]
115 pub chain: Option<EvmChain>,
116 #[arg(long)]
118 pub private: bool,
119}
120
121#[derive(Debug, Args)]
123pub struct SwapCommon {
124 #[arg(long, value_parser = chain)]
126 pub chain: Option<EvmChain>,
127 #[arg(long)]
129 pub private: bool,
130 #[arg(long)]
132 pub relay: bool,
133 #[arg(long)]
136 pub no_refund: bool,
137 #[arg(long, short = 'y')]
139 pub yes: bool,
140}
141
142#[derive(Debug, Args)]
144pub struct SwapArgs {
145 #[arg(long, value_name = "ZEC")]
147 pub zec: String,
148 #[command(flatten)]
150 pub common: SwapCommon,
151 #[arg(long)]
153 pub fresh_recipient: bool,
154 #[arg(long)]
156 pub standard_only: bool,
157 #[arg(long, value_name = "MS")]
159 pub jitter: Option<u64>,
160 #[arg(long)]
162 pub refund_demo: bool,
163}
164
165#[derive(Debug, Args)]
167pub struct PayArgs {
168 #[arg(long, value_name = "USD")]
170 pub usd: String,
171 #[arg(long, value_name = "0x…")]
173 pub to: String,
174 #[arg(long)]
176 pub label: Option<String>,
177 #[command(flatten)]
179 pub common: SwapCommon,
180}
181
182#[derive(Debug, Args)]
184pub struct SwapsArgs {
185 #[arg(long)]
187 pub all: bool,
188}
189
190#[derive(Debug, Args)]
192pub struct ResumeArgs {
193 pub id: String,
195 #[arg(long)]
197 pub no_relay: bool,
198}
199
200#[derive(Debug, Args)]
202pub struct RefundArgs {
203 pub id: String,
205 #[arg(long)]
207 pub no_wait: bool,
208}
209
210#[derive(Debug, Args)]
212pub struct MakerArgs {
213 #[arg(long, value_name = "ADDR", conflicts_with = "port")]
215 pub listen: Option<SocketAddr>,
216 #[arg(long)]
218 pub port: Option<u16>,
219 #[arg(long)]
222 pub no_mine: bool,
223}