1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "bsv-wallet", about = "Self-contained BSV wallet")]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
/// Use testnet instead of mainnet
#[arg(long, global = true)]
pub testnet: bool,
/// SQLite database path
#[arg(long, global = true, default_value = "wallet.db")]
pub db: String,
/// HTTP server port
#[arg(long, global = true, default_value_t = 3322)]
pub port: u16,
/// Output JSON instead of tables
#[arg(long, global = true)]
pub json: bool,
/// Enable debug logging
#[arg(short, long, global = true)]
pub verbose: bool,
}
#[derive(Subcommand)]
pub enum Commands {
/// Generate/import identity, create wallet database
Init {
/// Import existing root key (hex)
#[arg(long)]
key: Option<String>,
/// Overwrite existing .env (DESTROYS the existing wallet's key)
#[arg(long)]
force: bool,
},
/// Show public key and address
Identity,
/// Show spendable balance
Balance,
/// Show BRC-29 funding address
Address,
/// Send BSV to a P2PKH address
Send {
/// Destination address
address: String,
/// Amount in satoshis
satoshis: u64,
},
/// Send a time-locked BSV gift to a recipient's public key
GiftSend {
/// Recipient's 33-byte compressed public key (hex)
recipient: String,
/// Gift amount in satoshis
satoshis: u64,
/// Unlock time: unix timestamp, YYYY-MM-DD, or RFC-3339
#[arg(long)]
unlock: String,
/// Recipient-owned fee UTXO (sats) bundled so the claim self-funds its fee
#[arg(long, default_value_t = 1000)]
fee_utxo: u64,
},
/// Inspect a time-locked gift (prove it's yours + when it unlocks; no claim)
GiftInspect {
/// The gift (deposit) transaction id
txid: String,
},
/// Claim a time-locked gift after its unlock time
GiftClaim {
/// The gift (deposit) transaction id
txid: String,
/// Pre-broadcast before unlock (sits unconfirmed, auto-confirms at unlock)
#[arg(long)]
force: bool,
},
/// Send all spendable funds to one address (no change)
Drain {
/// Destination address
address: String,
},
/// Internalize a BEEF transaction (receive funds)
Fund {
/// BEEF transaction in hex (standard or AtomicBEEF)
beef_hex: String,
/// Output index to internalize (default: 0)
#[arg(long, default_value_t = 0)]
vout: u32,
},
/// Fetch a tx by txid from WhatsOnChain and internalize it
Receive {
/// Transaction id (hex)
txid: String,
/// Output index; if omitted, auto-selects the vout matching our deposit address
#[arg(long)]
vout: Option<u32>,
},
/// List unspent outputs
Outputs {
/// Filter by basket name
#[arg(long)]
basket: Option<String>,
/// Filter by tag
#[arg(long)]
tag: Option<String>,
},
/// List transaction history
Actions {
/// Filter by label
#[arg(long)]
label: Option<String>,
},
/// Scan WhatsOnChain for unspent UTXOs at our deposit address and internalize new ones
Sync,
/// Run all monitor tasks once and exit (one-shot equivalent of `daemon`)
Tick,
/// Run monitor + HTTP server (foreground)
Daemon,
/// Run HTTP server only (no monitor)
Serve,
/// Split UTXOs into multiple outputs for concurrency
Split {
/// Number of output UTXOs to create
#[arg(long, default_value_t = 3)]
count: u32,
},
/// Show blockchain service status
Services,
/// Open database inspector UI in browser
Ui {
/// UI server port (default: 9321)
#[arg(long, default_value_t = 9321)]
ui_port: u16,
},
/// Compact stored BEEF blobs to reduce transaction proof sizes
Compact,
/// Bundle .env + wallet.db into a tar.gz for off-machine backup
Backup {
/// Output path (default: ./bsv-wallet-backup-<timestamp>.tar.gz)
#[arg(long)]
to: Option<std::path::PathBuf>,
},
/// Fetch BEEFs from WhatsOnChain for every unspent UTXO and write them as files
ExportBeefs {
/// Output directory (will be created if missing)
#[arg(long)]
to: std::path::PathBuf,
},
/// Find unproven txs that aren't on chain, mark failed, restore their inputs
CleanupAbandoned {
/// Apply changes (default is dry-run)
#[arg(long)]
execute: bool,
},
}