22
22
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
23
// SOFTWARE.
24
24
25
+ //! Command line interface
26
+ //!
27
+ //! This module provides a [clap](https://docs.rs/clap/) `App` pre-configured to parse the command
28
+ //! line subcommands and global flags needed for basic wallet functionality.
29
+ //!
30
+ //! See the `repl.rs` example for how to use this module to create a simple command line wallet
31
+ //! application.
32
+ //!
33
+ //! See [`make_cli_subcommands()`] for list of sub-commands, and [`add_global_flags()`] for list of global flags.
34
+ //!
35
+ //! # Example
36
+ //!
37
+ //! ```no_run
38
+ //! # use bdk::blockchain::esplora::EsploraBlockchainConfig;
39
+ //! # use bdk::blockchain::{
40
+ //! # AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain, ElectrumBlockchainConfig,
41
+ //! # };
42
+ //! # use bdk::database::MemoryDatabase;
43
+ //! # use bdk::{cli, Wallet};
44
+ //! # use bitcoin::Network;
45
+ //! # use std::sync::Arc;
46
+ //!
47
+ //! let app = cli::make_cli_subcommands();
48
+ //! let app = cli::add_global_flags(app);
49
+ //!
50
+ //! let matches = app.get_matches();
51
+ //! let network = match matches.value_of("network") {
52
+ //! Some("regtest") => Network::Regtest,
53
+ //! Some("testnet") | _ => Network::Testnet,
54
+ //! };
55
+ //!
56
+ //! let descriptor = matches.value_of("descriptor").unwrap();
57
+ //! let change_descriptor = matches.value_of("change_descriptor");
58
+ //!
59
+ //! let database = MemoryDatabase::new();
60
+ //!
61
+ //! let config = match matches.value_of("esplora") {
62
+ //! Some(base_url) => AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
63
+ //! base_url: base_url.to_string(),
64
+ //! concurrency: matches
65
+ //! .value_of("esplora_concurrency")
66
+ //! .and_then(|v| v.parse::<u8>().ok()),
67
+ //! }),
68
+ //! None => AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
69
+ //! url: matches.value_of("server").unwrap().to_string(),
70
+ //! socks5: matches.value_of("proxy").map(ToString::to_string),
71
+ //! }),
72
+ //! };
73
+ //!
74
+ //! let wallet = Arc::new(
75
+ //! Wallet::new(
76
+ //! descriptor,
77
+ //! change_descriptor,
78
+ //! network,
79
+ //! database,
80
+ //! AnyBlockchain::from_config(&config).unwrap(),
81
+ //! )
82
+ //! .unwrap(),
83
+ //! );
84
+ //!
85
+ //! let result = cli::handle_matches(&wallet, matches).unwrap();
86
+ //! println!("{}", serde_json::to_string_pretty(&result).unwrap());
87
+ //! ```
88
+
25
89
use std:: collections:: BTreeMap ;
26
90
use std:: str:: FromStr ;
27
91
@@ -70,6 +134,50 @@ fn outpoint_validator(s: String) -> Result<(), String> {
70
134
parse_outpoint ( & s) . map ( |_| ( ) )
71
135
}
72
136
137
+ /// Create new [clap](https://docs.rs/clap/) `App` with wallet sub-commands
138
+ ///
139
+ /// Configuration such as name, version, author, about can be overridden. Additional sub-commands
140
+ /// sub-commands can also be added. See [clap](https://docs.rs/clap/) for more information on how
141
+ /// to customize an `App`.
142
+ ///
143
+ /// # Wallet sub-commands
144
+ ///
145
+ /// | Sub-command | Description |
146
+ /// |--------------------|----------------------------------------------- |
147
+ /// | broadcast | Broadcasts a transaction to the network. Takes either a raw transaction or a PSBT to extract |
148
+ /// | bump_fee | Bumps the fees of an RBF transaction |
149
+ /// | combine_psbt | Combines multiple PSBTs into one |
150
+ /// | create_tx | Creates a new unsigned tranasaction |
151
+ /// | extract_psbt | Extracts a raw transaction from a PSBT |
152
+ /// | finalize_psbt | Finalizes a psbt |
153
+ /// | get_balance | Returns the current wallet balance |
154
+ /// | get_new_address | Generates a new external address |
155
+ /// | help | Prints this message or the help of the given subcommand(s) |
156
+ /// | list_transactions | Lists all the incoming and outgoing transactions of the wallet |
157
+ /// | list_unspent | Lists the available spendable UTXOs |
158
+ /// | policies | Returns the available spending policies for the descriptor |
159
+ /// | public_descriptor | Returns the public version of the wallet's descriptor(s) |
160
+ /// | repl | Opens an interactive shell |
161
+ /// | sign | Signs and tries to finalize a PSBT |
162
+ /// | sync | Syncs with the chosen Electrum server |
163
+ ///
164
+ /// # Example
165
+ ///
166
+ /// ```
167
+ /// # use bdk::cli;
168
+ /// # use clap::SubCommand;
169
+ ///
170
+ /// let app = cli::make_cli_subcommands();
171
+ /// let app = app
172
+ /// .name("Demo App")
173
+ /// .version("0.1.0")
174
+ /// .author("Demo Author")
175
+ /// .about("A demo cli wallet based on the bdk library.");
176
+ ///
177
+ /// let app = app
178
+ /// .subcommand(SubCommand::with_name("do_something").about("Do something else with your app"));
179
+ /// ```
180
+
73
181
pub fn make_cli_subcommands < ' a , ' b > ( ) -> App < ' a , ' b > {
74
182
App :: new ( "Magical Bitcoin Wallet" )
75
183
. version ( option_env ! ( "CARGO_PKG_VERSION" ) . unwrap_or ( "unknown" ) )
@@ -215,11 +323,11 @@ pub fn make_cli_subcommands<'a, 'b>() -> App<'a, 'b> {
215
323
. subcommand (
216
324
SubCommand :: with_name ( "policies" )
217
325
. about ( "Returns the available spending policies for the descriptor" )
218
- )
326
+ )
219
327
. subcommand (
220
328
SubCommand :: with_name ( "public_descriptor" )
221
329
. about ( "Returns the public version of the wallet's descriptor(s)" )
222
- )
330
+ )
223
331
. subcommand (
224
332
SubCommand :: with_name ( "sign" )
225
333
. about ( "Signs and tries to finalize a PSBT" )
@@ -260,7 +368,7 @@ pub fn make_cli_subcommands<'a, 'b>() -> App<'a, 'b> {
260
368
. takes_value ( true )
261
369
. required_unless ( "psbt" )
262
370
. number_of_values ( 1 ) )
263
- )
371
+ )
264
372
. subcommand (
265
373
SubCommand :: with_name ( "extract_psbt" )
266
374
. about ( "Extracts a raw transaction from a PSBT" )
@@ -272,7 +380,7 @@ pub fn make_cli_subcommands<'a, 'b>() -> App<'a, 'b> {
272
380
. takes_value ( true )
273
381
. required ( true )
274
382
. number_of_values ( 1 ) )
275
- )
383
+ )
276
384
. subcommand (
277
385
SubCommand :: with_name ( "finalize_psbt" )
278
386
. about ( "Finalizes a psbt" )
@@ -292,7 +400,7 @@ pub fn make_cli_subcommands<'a, 'b>() -> App<'a, 'b> {
292
400
. takes_value ( true )
293
401
. number_of_values ( 1 )
294
402
. required ( false ) )
295
- )
403
+ )
296
404
. subcommand (
297
405
SubCommand :: with_name ( "combine_psbt" )
298
406
. about ( "Combines multiple PSBTs into one" )
@@ -305,9 +413,48 @@ pub fn make_cli_subcommands<'a, 'b>() -> App<'a, 'b> {
305
413
. number_of_values ( 1 )
306
414
. required ( true )
307
415
. multiple ( true ) )
308
- )
416
+ )
309
417
}
310
418
419
+ /// Add [clap](https://docs.rs/clap/) global wallet options to an `App`
420
+ ///
421
+ /// Additional app specific options can be added. See [clap](https://docs.rs/clap/) for more
422
+ /// information on how to customize the global flags for an `App`.
423
+ ///
424
+ /// # Wallet global flags
425
+ ///
426
+ /// | Short | Long | Description |
427
+ /// |-------|-------|-------------|
428
+ /// | -c | --change_descriptor <DESCRIPTOR> | Sets the descriptor to use for internal addresses |
429
+ /// | -d | --descriptor <DESCRIPTOR> | Sets the descriptor to use for the external addresses |
430
+ /// | -e | --esplora <ESPLORA> | Use the esplora server if given as parameter |
431
+ /// | | --esplora_concurrency <ESPLORA_CONCURRENCY> | Concurrency of requests made to the esplora server [default: 4] |
432
+ /// | -n | --network <NETWORK> | Sets the network [default: testnet] [possible values: testnet, regtest] |
433
+ /// | -p | --proxy <SERVER\:PORT> | Sets the SOCKS5 proxy for the Electrum client |
434
+ /// | -s | --server <SERVER\:PORT> | Sets the Electrum server to use [default: ssl://electrum.blockstream.info:60002] |
435
+ /// | -w | --wallet <WALLET_NAME> | Selects the wallet to use [default: main] |
436
+ ///
437
+ /// # Example
438
+ ///
439
+ /// ```
440
+ /// # use bdk::cli;
441
+ /// # use clap::Arg;
442
+ ///
443
+ /// let app = cli::make_cli_subcommands();
444
+ /// let app = cli::add_global_flags(app);
445
+ ///
446
+ /// let app = app.arg(
447
+ /// Arg::with_name("extra")
448
+ /// .short("x")
449
+ /// .long("extra")
450
+ /// .value_name("EXTRA")
451
+ /// .help("Sets the extra option")
452
+ /// .takes_value(true)
453
+ /// .default_value("extra-stuff")
454
+ /// .possible_values(&["extra1", "extra2"]),
455
+ /// );
456
+ /// ```
457
+
311
458
pub fn add_global_flags < ' a , ' b > ( app : App < ' a , ' b > ) -> App < ' a , ' b > {
312
459
let mut app = app
313
460
. arg (
@@ -396,6 +543,12 @@ pub fn add_global_flags<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
396
543
app. subcommand ( SubCommand :: with_name ( "repl" ) . about ( "Opens an interactive shell" ) )
397
544
}
398
545
546
+ /// Execute wallet sub-commands given to an `App`
547
+ ///
548
+ /// Wallet sub-command with global flags are given by the user to the [clap](https://docs.rs/clap/)
549
+ /// `App`. Wallet sub-commands are described in [`make_cli_subcommands()`] with global flags as
550
+ /// described in [`add_global_flags()`]. See [`super::cli`] for example usage.
551
+
399
552
#[ maybe_async]
400
553
pub fn handle_matches < C , D > (
401
554
wallet : & Wallet < C , D > ,
0 commit comments