1use super::*;
2
3pub async fn cmd_wallet(url: &str, json: bool) -> Result<(), Box<dyn std::error::Error>> {
4 let (DIM, BOLD, ACCENT, GREEN, YELLOW, RED, CYAN, RESET, MONO) = colors();
5 let (OK, ACTION, WARN, DETAIL, ERR) = icons();
6 let c = RoboticusClient::new(url)?;
7 let balance = c.get("/api/wallet/balance").await.map_err(|e| {
8 RoboticusClient::check_connectivity_hint(&*e);
9 e
10 })?;
11 let address = c.get("/api/wallet/address").await?;
12 if json {
13 let combined = serde_json::json!({ "balance": balance, "address": address });
14 println!("{}", serde_json::to_string_pretty(&combined)?);
15 return Ok(());
16 }
17 heading("Wallet");
18 let bal = balance["balance"].as_str().unwrap_or("0.00");
19 let currency = balance["currency"].as_str().unwrap_or("USDC");
20 let addr = address["address"].as_str().unwrap_or("not connected");
21 let treasury = &balance["treasury"];
22 let swap = &treasury["revenue_swap"];
23 let tax = &balance["self_funding"]["tax"];
24 let accounting = &balance["revenue_accounting"];
25 let swap_queue = &balance["revenue_swap_queue"];
26 let tax_queue = &balance["revenue_tax_queue"];
27 let strategy_summary = balance["revenue_strategy_summary"]
28 .as_array()
29 .cloned()
30 .unwrap_or_default();
31 let feedback_summary = balance["revenue_feedback_summary"]
32 .as_array()
33 .cloned()
34 .unwrap_or_default();
35 let seed_readiness = &balance["seed_exercise_readiness"];
36 let seed_progress = &balance["seed_exercise_progress"];
37 let seed_plan = balance["seed_exercise_plan"]
38 .as_object()
39 .cloned()
40 .unwrap_or_default();
41 kv_accent("Balance", &format!("{bal} {currency}"));
42 kv_mono("Address", addr);
43 if swap.is_object() {
44 let swap_status = if swap["enabled"].as_bool().unwrap_or(false) {
45 "enabled"
46 } else {
47 "disabled"
48 };
49 let target = swap["target_symbol"].as_str().unwrap_or("PUSD");
50 let chain = swap["default_chain"].as_str().unwrap_or("ETH");
51 kv(
52 "Revenue Swap",
53 &format!("{swap_status} -> {target} on {chain}"),
54 );
55 if let Some(chains) = swap["chains"].as_array() {
56 let configured: Vec<String> = chains
57 .iter()
58 .filter_map(|entry| entry["chain"].as_str())
59 .map(str::to_string)
60 .collect();
61 if !configured.is_empty() {
62 kv("Swap Chains", &configured.join(", "));
63 }
64 }
65 }
66 if tax.is_object() {
67 let tax_status = if tax["enabled"].as_bool().unwrap_or(false) {
68 "enabled"
69 } else {
70 "disabled"
71 };
72 let tax_rate = tax["rate"].as_f64().unwrap_or(0.0) * 100.0;
73 kv("Profit Tax", &format!("{tax_status} @ {tax_rate:.2}%"));
74 if let Some(dest) = tax["destination_wallet"].as_str().filter(|s| !s.is_empty()) {
75 kv("Tax Wallet", dest);
76 }
77 }
78 if accounting.is_object() {
79 kv(
80 "Revenue Gross",
81 &format!(
82 "{:.2} USDC",
83 accounting["gross_revenue_usdc"].as_f64().unwrap_or(0.0)
84 ),
85 );
86 kv(
87 "Revenue Net",
88 &format!(
89 "{:.2} USDC",
90 accounting["net_profit_usdc"].as_f64().unwrap_or(0.0)
91 ),
92 );
93 kv(
94 "Retained",
95 &format!(
96 "{:.2} USDC",
97 accounting["retained_earnings_usdc"].as_f64().unwrap_or(0.0)
98 ),
99 );
100 }
101 if swap_queue.is_object() {
102 kv(
103 "Swap Queue",
104 &format!(
105 "total={} pending={} in_progress={} failed={} stale={}",
106 swap_queue["total"].as_i64().unwrap_or(0),
107 swap_queue["pending"].as_i64().unwrap_or(0),
108 swap_queue["in_progress"].as_i64().unwrap_or(0),
109 swap_queue["failed"].as_i64().unwrap_or(0),
110 swap_queue["stale_in_progress"].as_i64().unwrap_or(0),
111 ),
112 );
113 }
114 if tax_queue.is_object() {
115 kv(
116 "Tax Queue",
117 &format!(
118 "total={} pending={} in_progress={} failed={} completed={}",
119 tax_queue["total"].as_i64().unwrap_or(0),
120 tax_queue["pending"].as_i64().unwrap_or(0),
121 tax_queue["in_progress"].as_i64().unwrap_or(0),
122 tax_queue["failed"].as_i64().unwrap_or(0),
123 tax_queue["completed"].as_i64().unwrap_or(0),
124 ),
125 );
126 }
127 if !strategy_summary.is_empty() {
128 let top = &strategy_summary[0];
129 kv(
130 "Top Revenue Strategy",
131 &format!(
132 "{} (net {:.2} USDC)",
133 top["strategy"].as_str().unwrap_or("unknown"),
134 top["net_profit_usdc"].as_f64().unwrap_or(0.0)
135 ),
136 );
137 }
138 if !feedback_summary.is_empty() {
139 let top = &feedback_summary[0];
140 kv(
141 "Top Feedback Strategy",
142 &format!(
143 "{} ({:.2}/5 over {} signals)",
144 top["strategy"].as_str().unwrap_or("unknown"),
145 top["avg_grade"].as_f64().unwrap_or(0.0),
146 top["feedback_count"].as_i64().unwrap_or(0)
147 ),
148 );
149 }
150 if seed_readiness.is_object() {
151 kv(
152 "$50 Seed Readiness",
153 if seed_readiness["meets_seed_target"]
154 .as_bool()
155 .unwrap_or(false)
156 {
157 "ready"
158 } else {
159 "not ready"
160 },
161 );
162 kv(
163 "Stable Balance",
164 &format!(
165 "{:.2} / {:.2} USDC target",
166 seed_readiness["stable_balance_usdc"]
167 .as_f64()
168 .unwrap_or(0.0),
169 seed_readiness["seed_target_usdc"].as_f64().unwrap_or(50.0)
170 ),
171 );
172 }
173 if seed_progress.is_object() {
174 kv(
175 "Seed Next Action",
176 seed_progress["next_action"]
177 .as_str()
178 .unwrap_or("no action available"),
179 );
180 }
181 if let Some(phases) = seed_plan.get("phases").and_then(|v| v.as_array()) {
182 kv("Seed Exercise Phases", &phases.len().to_string());
183 if let Some(first) = phases.first() {
184 kv(
185 "Seed First Phase",
186 first["label"].as_str().unwrap_or("no phase available"),
187 );
188 }
189 }
190 if let Some(note) = balance["note"].as_str() {
191 eprintln!();
192 eprintln!(" {DIM}\u{2139} {note}{RESET}");
193 }
194 eprintln!();
195 Ok(())
196}
197
198pub async fn cmd_wallet_address(url: &str, json: bool) -> Result<(), Box<dyn std::error::Error>> {
199 let (DIM, BOLD, ACCENT, GREEN, YELLOW, RED, CYAN, RESET, MONO) = colors();
200 let (OK, ACTION, WARN, DETAIL, ERR) = icons();
201 let c = RoboticusClient::new(url)?;
202 let address = c.get("/api/wallet/address").await.map_err(|e| {
203 RoboticusClient::check_connectivity_hint(&*e);
204 e
205 })?;
206 if json {
207 println!("{}", serde_json::to_string_pretty(&address)?);
208 return Ok(());
209 }
210 let addr = address["address"].as_str().unwrap_or("not connected");
211 eprintln!();
212 eprintln!(" {MONO}{addr}{RESET}");
213 eprintln!();
214 Ok(())
215}
216
217pub async fn cmd_wallet_balance(url: &str, json: bool) -> Result<(), Box<dyn std::error::Error>> {
218 let (DIM, BOLD, ACCENT, GREEN, YELLOW, RED, CYAN, RESET, MONO) = colors();
219 let (OK, ACTION, WARN, DETAIL, ERR) = icons();
220 let c = RoboticusClient::new(url)?;
221 let balance = c.get("/api/wallet/balance").await.map_err(|e| {
222 RoboticusClient::check_connectivity_hint(&*e);
223 e
224 })?;
225 if json {
226 println!("{}", serde_json::to_string_pretty(&balance)?);
227 return Ok(());
228 }
229 let bal = balance["balance"].as_str().unwrap_or("0.00");
230 let currency = balance["currency"].as_str().unwrap_or("USDC");
231 let swap = &balance["treasury"]["revenue_swap"];
232 let tax = &balance["self_funding"]["tax"];
233 let accounting = &balance["revenue_accounting"];
234 let swap_queue = &balance["revenue_swap_queue"];
235 let tax_queue = &balance["revenue_tax_queue"];
236 let strategy_summary = balance["revenue_strategy_summary"]
237 .as_array()
238 .cloned()
239 .unwrap_or_default();
240 let feedback_summary = balance["revenue_feedback_summary"]
241 .as_array()
242 .cloned()
243 .unwrap_or_default();
244 let seed_readiness = &balance["seed_exercise_readiness"];
245 let seed_progress = &balance["seed_exercise_progress"];
246 let seed_plan = balance["seed_exercise_plan"]
247 .as_object()
248 .cloned()
249 .unwrap_or_default();
250 eprintln!();
251 kv_accent("Balance", &format!("{bal} {currency}"));
252 if swap.is_object() {
253 let swap_status = if swap["enabled"].as_bool().unwrap_or(false) {
254 "enabled"
255 } else {
256 "disabled"
257 };
258 let target = swap["target_symbol"].as_str().unwrap_or("PUSD");
259 let chain = swap["default_chain"].as_str().unwrap_or("ETH");
260 kv(
261 "Revenue Swap",
262 &format!("{swap_status} -> {target} on {chain}"),
263 );
264 }
265 if tax.is_object() {
266 let tax_status = if tax["enabled"].as_bool().unwrap_or(false) {
267 "enabled"
268 } else {
269 "disabled"
270 };
271 let tax_rate = tax["rate"].as_f64().unwrap_or(0.0) * 100.0;
272 kv("Profit Tax", &format!("{tax_status} @ {tax_rate:.2}%"));
273 }
274 if accounting.is_object() {
275 kv(
276 "Revenue Net",
277 &format!(
278 "{:.2} USDC",
279 accounting["net_profit_usdc"].as_f64().unwrap_or(0.0)
280 ),
281 );
282 }
283 if swap_queue.is_object() {
284 kv(
285 "Swap Queue",
286 &format!(
287 "pending={} in_progress={} failed={}",
288 swap_queue["pending"].as_i64().unwrap_or(0),
289 swap_queue["in_progress"].as_i64().unwrap_or(0),
290 swap_queue["failed"].as_i64().unwrap_or(0),
291 ),
292 );
293 }
294 if tax_queue.is_object() {
295 kv(
296 "Tax Queue",
297 &format!(
298 "pending={} in_progress={} failed={} completed={}",
299 tax_queue["pending"].as_i64().unwrap_or(0),
300 tax_queue["in_progress"].as_i64().unwrap_or(0),
301 tax_queue["failed"].as_i64().unwrap_or(0),
302 tax_queue["completed"].as_i64().unwrap_or(0),
303 ),
304 );
305 }
306 if !strategy_summary.is_empty() {
307 let top = &strategy_summary[0];
308 kv(
309 "Top Strategy",
310 &format!(
311 "{} ({:.2} USDC net)",
312 top["strategy"].as_str().unwrap_or("unknown"),
313 top["net_profit_usdc"].as_f64().unwrap_or(0.0)
314 ),
315 );
316 }
317 if !feedback_summary.is_empty() {
318 let top = &feedback_summary[0];
319 kv(
320 "Top Feedback",
321 &format!(
322 "{} ({:.2}/5 over {} signals)",
323 top["strategy"].as_str().unwrap_or("unknown"),
324 top["avg_grade"].as_f64().unwrap_or(0.0),
325 top["feedback_count"].as_i64().unwrap_or(0)
326 ),
327 );
328 }
329 if seed_readiness.is_object() {
330 kv(
331 "Seed Readiness",
332 if seed_readiness["meets_seed_target"]
333 .as_bool()
334 .unwrap_or(false)
335 {
336 "ready"
337 } else {
338 "not ready"
339 },
340 );
341 }
342 if seed_progress.is_object() {
343 kv(
344 "Seed Next Action",
345 seed_progress["next_action"]
346 .as_str()
347 .unwrap_or("no action available"),
348 );
349 }
350 if let Some(phases) = seed_plan.get("phases").and_then(|v| v.as_array()) {
351 kv("Seed Exercise Phases", &phases.len().to_string());
352 }
353 eprintln!();
354 Ok(())
355}