From 8760c3bdda27db588c26911edfb6775583f1b7a9 Mon Sep 17 00:00:00 2001
From: poka <poka@p2p.legal>
Date: Thu, 13 Mar 2025 23:21:46 +0100
Subject: [PATCH] use json output for all usefull commands

---
 src/commands/account.rs       |  42 +++-
 src/commands/blockchain.rs    |  31 ++-
 src/commands/oneshot.rs       |  55 ++++--
 src/commands/runtime.rs       | 363 +++++++++++++++++++++-------------
 src/commands/smith.rs         | 184 ++++++++++++-----
 src/commands/vault.rs         | 104 +++++++---
 src/commands/vault/display.rs |  86 ++++++++
 src/conf.rs                   |  54 ++++-
 8 files changed, 672 insertions(+), 247 deletions(-)

diff --git a/src/commands/account.rs b/src/commands/account.rs
index c1216b6..1d0c452 100644
--- a/src/commands/account.rs
+++ b/src/commands/account.rs
@@ -1,4 +1,6 @@
 use crate::*;
+use anyhow::anyhow;
+use serde::Serialize;
 
 /// define account subcommands
 #[derive(Clone, Default, Debug, clap::Parser)]
@@ -57,18 +59,42 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 	Ok(())
 }
 
+/// Balance view for JSON serialization
+#[derive(Serialize)]
+struct BalanceView {
+	account_id: String,
+	free_balance: u64,
+	formatted_balance: String,
+	exists: bool,
+}
+
 /// get balance
-pub async fn get_balance(data: Data) -> Result<(), subxt::Error> {
+pub async fn get_balance(data: Data) -> Result<(), GcliError> {
 	let account_id = data.address();
 	let account_info = get_account_info(data.client(), &account_id).await?;
-	if let Some(account_info) = account_info {
-		println!(
-			"{account_id} has {}",
-			data.format_balance(account_info.data.free)
-		);
-	} else {
-		println!("account {account_id} does not exist")
+	
+	match data.args.output_format {
+		OutputFormat::Human => {
+			if let Some(account_info) = account_info {
+				println!(
+					"{account_id} has {}",
+					data.format_balance(account_info.data.free)
+				);
+			} else {
+				println!("account {account_id} does not exist")
+			}
+		}
+		OutputFormat::Json => {
+			let view = BalanceView {
+				account_id: account_id.to_string(),
+				free_balance: account_info.as_ref().map_or(0, |info| info.data.free),
+				formatted_balance: account_info.as_ref().map_or("0".to_string(), |info| data.format_balance(info.data.free)),
+				exists: account_info.is_some(),
+			};
+			println!("{}", serde_json::to_string(&view).map_err(|e| anyhow!(e))?);
+		}
 	}
+	
 	Ok(())
 }
 
diff --git a/src/commands/blockchain.rs b/src/commands/blockchain.rs
index ee46955..0062071 100644
--- a/src/commands/blockchain.rs
+++ b/src/commands/blockchain.rs
@@ -1,4 +1,6 @@
 use crate::*;
+use anyhow::anyhow;
+use serde::Serialize;
 
 /// define blockchain subcommands
 #[derive(Clone, Default, Debug, clap::Parser)]
@@ -23,6 +25,14 @@ pub enum Subcommand {
 	CreateBlock,
 }
 
+/// Block information for JSON serialization
+#[derive(Serialize)]
+struct BlockInfoView {
+	endpoint: String,
+	finalized_block: u32,
+	current_block: u32,
+}
+
 /// handle blockchain commands
 pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError> {
 	let mut data = data.build_client().await?;
@@ -36,14 +46,27 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 		}
 		Subcommand::RuntimeInfo => {
 			data = data.fetch_system_properties().await?;
-			commands::runtime::runtime_info(data).await;
+			commands::runtime::runtime_info(data).await?;
 		}
 		Subcommand::CurrentBlock => {
 			let finalized_number = fetch_finalized_number(&data).await?;
 			let current_number = fetch_latest_number_and_hash(&data).await?.0;
-			println!("on {}", data.cfg.duniter_endpoint);
-			println!("finalized block\t{}", finalized_number);
-			println!("current block\t{}", current_number);
+			
+			match data.args.output_format {
+				OutputFormat::Human => {
+					println!("on {}", data.cfg.duniter_endpoint);
+					println!("finalized block\t{}", finalized_number);
+					println!("current block\t{}", current_number);
+				}
+				OutputFormat::Json => {
+					let view = BlockInfoView {
+						endpoint: data.cfg.duniter_endpoint.clone(),
+						finalized_block: finalized_number,
+						current_block: current_number,
+					};
+					println!("{}", serde_json::to_string(&view).map_err(|e| anyhow!(e))?);
+				}
+			}
 		}
 		Subcommand::CreateBlock => {
 			todo!()
diff --git a/src/commands/oneshot.rs b/src/commands/oneshot.rs
index 44ffee8..086fc9c 100644
--- a/src/commands/oneshot.rs
+++ b/src/commands/oneshot.rs
@@ -1,4 +1,6 @@
 use crate::*;
+use anyhow::anyhow;
+use serde::Serialize;
 
 /// define oneshot account subcommands
 #[derive(Clone, Default, Debug, clap::Parser)]
@@ -64,23 +66,44 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 	Ok(())
 }
 
+/// Oneshot balance view for JSON serialization
+#[derive(Serialize)]
+struct OneshotBalanceView {
+	account_id: String,
+	balance: u64,
+}
+
 /// get balance of oneshot account
-pub async fn oneshot_account_balance(data: &Data) -> Result<(), subxt::Error> {
-	println!(
-		"balance of oneshot account {} is: {}",
-		data.address(),
-		data.client()
-			.storage()
-			.at_latest()
-			.await?
-			.fetch(
-				&runtime::storage()
-					.oneshot_account()
-					.oneshot_accounts(data.address()),
-			)
-			.await?
-			.unwrap_or(0)
-	);
+pub async fn oneshot_account_balance(data: &Data) -> Result<(), GcliError> {
+	let account_id = data.address();
+	let balance = data.client()
+		.storage()
+		.at_latest()
+		.await?
+		.fetch(
+			&runtime::storage()
+				.oneshot_account()
+				.oneshot_accounts(&account_id),
+		)
+		.await?
+		.unwrap_or(0);
+	
+	match data.args.output_format {
+		OutputFormat::Human => {
+			println!(
+				"balance of oneshot account {} is: {}",
+				account_id,
+				balance
+			);
+		}
+		OutputFormat::Json => {
+			let view = OneshotBalanceView {
+				account_id: account_id.to_string(),
+				balance,
+			};
+			println!("{}", serde_json::to_string(&view).map_err(|e| anyhow!(e))?);
+		}
+	}
 
 	Ok(())
 }
diff --git a/src/commands/runtime.rs b/src/commands/runtime.rs
index d71b102..4045a10 100644
--- a/src/commands/runtime.rs
+++ b/src/commands/runtime.rs
@@ -1,6 +1,23 @@
 use crate::*;
+use anyhow::anyhow;
+use serde::Serialize;
+use std::collections::HashMap;
 
-pub async fn runtime_info(data: Data) {
+/// Runtime information for JSON serialization
+#[derive(Serialize)]
+struct RuntimeInfoView {
+	identity: HashMap<String, u32>,
+	certification: HashMap<String, u32>,
+	wot: HashMap<String, u32>,
+	membership: HashMap<String, u32>,
+	smith_members: HashMap<String, u32>,
+	distance: HashMap<String, serde_json::Value>,
+	currency: HashMap<String, String>,
+	provide_randomness: HashMap<String, serde_json::Value>,
+	universal_dividend: HashMap<String, serde_json::Value>,
+}
+
+pub async fn runtime_info(data: Data) -> Result<(), GcliError> {
 	let api = data.client();
 	let consts = runtime::constants();
 	// get constant u32 value
@@ -12,137 +29,215 @@ pub async fn runtime_info(data: Data) {
 	// get formatted currency value
 	let getf = |c| data.format_balance(api.constants().at(&c).unwrap());
 
-	// identity
-	println!("--- identity ---");
-	println!(
-		"confirm period: {} blocks",
-		getu32(consts.identity().confirm_period())
-	);
-	println!(
-		"validation period: {} blocks",
-		getu32(consts.identity().validation_period())
-	);
-	println!(
-		"autorevocation period: {} blocks",
-		getu32(consts.identity().autorevocation_period())
-	);
-	println!(
-		"deletion period: {} blocks",
-		getu32(consts.identity().deletion_period())
-	);
-	println!(
-		"change owner key period: {} blocks",
-		getu32(consts.identity().change_owner_key_period())
-	);
-	println!(
-		"identity creation period: {} blocks",
-		getu32(consts.identity().idty_creation_period())
-	);
-	// certification
-	println!("--- certification ---");
-	println!(
-		"certification period: {} blocks",
-		getu32(consts.certification().cert_period())
-	);
-	println!(
-		"max certs by issuer: {}",
-		getu32(consts.certification().max_by_issuer())
-	);
-	println!(
-		"min received cert to issue cert: {}",
-		getu32(
-			consts
-				.certification()
-				.min_received_cert_to_be_able_to_issue_cert()
-		)
-	);
-	println!(
-		"certification validity: {} blocks",
-		getu32(consts.certification().validity_period())
-	);
-	// wot
-	println!("--- wot ---");
-	println!(
-		"first issuable on: {}",
-		getu32(consts.wot().first_issuable_on())
-	);
-	println!(
-		"min cert for membership: {}",
-		getu32(consts.wot().min_cert_for_membership())
-	);
-	println!(
-		"min cert for create identity: {}",
-		getu32(consts.wot().min_cert_for_create_idty_right())
-	);
-	// membership
-	println!("--- membership ---");
-	println!(
-		"membership validity: {} blocks",
-		getu32(consts.membership().membership_period())
-	);
-	// smith members
-	println!("--- smith members ---");
-	println!(
-		"max certs by issuer: {}",
-		getu32(consts.smith_members().max_by_issuer())
-	);
-	println!(
-		"min cert for membership: {}",
-		getu32(consts.smith_members().min_cert_for_membership())
-	);
-	println!(
-		"smith inactivity max duration: {}",
-		getu32(consts.smith_members().smith_inactivity_max_duration())
-	);
-	// todo membership renewal period
-	// distance
-	println!("--- distance ---");
-	println!(
-		"max referee distance: {}",
-		getu32(consts.distance().max_referee_distance())
-	);
-	println!(
-		"min accessible referees: {:?}",
-		getp(consts.distance().min_accessible_referees())
-	);
-	println!(
-		"distance evaluation price: {}",
-		getf(consts.distance().evaluation_price())
-	);
-	// currency
-	println!("--- currency ---");
-	println!(
-		"existential deposit: {}",
-		getf(consts.balances().existential_deposit())
-	);
-	// provide randomness
-	println!("--- provide randomness ---");
-	println!(
-		"max requests: {}",
-		getu32(consts.provide_randomness().max_requests())
-	);
-	println!(
-		"request price: {}",
-		getf(consts.provide_randomness().request_price())
-	);
-	// universal dividend
-	println!("--- universal dividend ---");
-	println!(
-		"max past reevals: {}",
-		getu32(consts.universal_dividend().max_past_reeval())
-	);
-	println!(
-		"square money growth rate: {:?}",
-		getp(consts.universal_dividend().square_money_growth_rate())
-	);
-	println!(
-		"UD creation period: {}",
-		getu64(consts.universal_dividend().ud_creation_period())
-	);
-	println!(
-		"UD reeval period: {}",
-		getu64(consts.universal_dividend().ud_reeval_period())
-	);
-	// todo treasury, technical committee, transaction payment, authority members
-	// consts.system().ss58_prefix()
+	match data.args.output_format {
+		OutputFormat::Human => {
+			// identity
+			println!("--- identity ---");
+			println!(
+				"confirm period: {} blocks",
+				getu32(consts.identity().confirm_period())
+			);
+			println!(
+				"validation period: {} blocks",
+				getu32(consts.identity().validation_period())
+			);
+			println!(
+				"autorevocation period: {} blocks",
+				getu32(consts.identity().autorevocation_period())
+			);
+			println!(
+				"deletion period: {} blocks",
+				getu32(consts.identity().deletion_period())
+			);
+			println!(
+				"change owner key period: {} blocks",
+				getu32(consts.identity().change_owner_key_period())
+			);
+			println!(
+				"identity creation period: {} blocks",
+				getu32(consts.identity().idty_creation_period())
+			);
+			// certification
+			println!("--- certification ---");
+			println!(
+				"certification period: {} blocks",
+				getu32(consts.certification().cert_period())
+			);
+			println!(
+				"max certs by issuer: {}",
+				getu32(consts.certification().max_by_issuer())
+			);
+			println!(
+				"min received cert to issue cert: {}",
+				getu32(
+					consts
+						.certification()
+						.min_received_cert_to_be_able_to_issue_cert()
+				)
+			);
+			println!(
+				"certification validity: {} blocks",
+				getu32(consts.certification().validity_period())
+			);
+			// wot
+			println!("--- wot ---");
+			println!(
+				"first issuable on: {}",
+				getu32(consts.wot().first_issuable_on())
+			);
+			println!(
+				"min cert for membership: {}",
+				getu32(consts.wot().min_cert_for_membership())
+			);
+			println!(
+				"min cert for create identity: {}",
+				getu32(consts.wot().min_cert_for_create_idty_right())
+			);
+			// membership
+			println!("--- membership ---");
+			println!(
+				"membership validity: {} blocks",
+				getu32(consts.membership().membership_period())
+			);
+			// smith members
+			println!("--- smith members ---");
+			println!(
+				"max certs by issuer: {}",
+				getu32(consts.smith_members().max_by_issuer())
+			);
+			println!(
+				"min cert for membership: {}",
+				getu32(consts.smith_members().min_cert_for_membership())
+			);
+			println!(
+				"smith inactivity max duration: {}",
+				getu32(consts.smith_members().smith_inactivity_max_duration())
+			);
+			// todo membership renewal period
+			// distance
+			println!("--- distance ---");
+			println!(
+				"max referee distance: {}",
+				getu32(consts.distance().max_referee_distance())
+			);
+			println!(
+				"min accessible referees: {:?}",
+				getp(consts.distance().min_accessible_referees())
+			);
+			println!(
+				"distance evaluation price: {}",
+				getf(consts.distance().evaluation_price())
+			);
+			// currency
+			println!("--- currency ---");
+			println!(
+				"existential deposit: {}",
+				getf(consts.balances().existential_deposit())
+			);
+			// provide randomness
+			println!("--- provide randomness ---");
+			println!(
+				"max requests: {}",
+				getu32(consts.provide_randomness().max_requests())
+			);
+			println!(
+				"request price: {}",
+				getf(consts.provide_randomness().request_price())
+			);
+			// universal dividend
+			println!("--- universal dividend ---");
+			println!(
+				"max past reevals: {}",
+				getu32(consts.universal_dividend().max_past_reeval())
+			);
+			println!(
+				"square money growth rate: {:?}",
+				getp(consts.universal_dividend().square_money_growth_rate())
+			);
+			println!(
+				"UD creation period: {}",
+				getu64(consts.universal_dividend().ud_creation_period())
+			);
+			println!(
+				"UD reeval period: {}",
+				getu64(consts.universal_dividend().ud_reeval_period())
+			);
+			// todo treasury, technical committee, transaction payment, authority members
+			// consts.system().ss58_prefix()
+		}
+		OutputFormat::Json => {
+			// Create HashMaps for each section
+			let mut identity = HashMap::new();
+			identity.insert("confirm_period".to_string(), getu32(consts.identity().confirm_period()));
+			identity.insert("validation_period".to_string(), getu32(consts.identity().validation_period()));
+			identity.insert("autorevocation_period".to_string(), getu32(consts.identity().autorevocation_period()));
+			identity.insert("deletion_period".to_string(), getu32(consts.identity().deletion_period()));
+			identity.insert("change_owner_key_period".to_string(), getu32(consts.identity().change_owner_key_period()));
+			identity.insert("idty_creation_period".to_string(), getu32(consts.identity().idty_creation_period()));
+			
+			let mut certification = HashMap::new();
+			certification.insert("cert_period".to_string(), getu32(consts.certification().cert_period()));
+			certification.insert("max_by_issuer".to_string(), getu32(consts.certification().max_by_issuer()));
+			certification.insert("min_received_cert_to_be_able_to_issue_cert".to_string(), 
+				getu32(consts.certification().min_received_cert_to_be_able_to_issue_cert()));
+			certification.insert("validity_period".to_string(), getu32(consts.certification().validity_period()));
+			
+			let mut wot = HashMap::new();
+			wot.insert("first_issuable_on".to_string(), getu32(consts.wot().first_issuable_on()));
+			wot.insert("min_cert_for_membership".to_string(), getu32(consts.wot().min_cert_for_membership()));
+			wot.insert("min_cert_for_create_idty_right".to_string(), getu32(consts.wot().min_cert_for_create_idty_right()));
+			
+			let mut membership = HashMap::new();
+			membership.insert("membership_period".to_string(), getu32(consts.membership().membership_period()));
+			
+			let mut smith_members = HashMap::new();
+			smith_members.insert("max_by_issuer".to_string(), getu32(consts.smith_members().max_by_issuer()));
+			smith_members.insert("min_cert_for_membership".to_string(), getu32(consts.smith_members().min_cert_for_membership()));
+			smith_members.insert("smith_inactivity_max_duration".to_string(), getu32(consts.smith_members().smith_inactivity_max_duration()));
+			
+			let mut distance = HashMap::new();
+			distance.insert("max_referee_distance".to_string(), 
+				serde_json::to_value(getu32(consts.distance().max_referee_distance())).map_err(|e| anyhow!(e))?);
+			distance.insert("min_accessible_referees".to_string(), 
+				serde_json::to_value(format!("{:?}", getp(consts.distance().min_accessible_referees()))).map_err(|e| anyhow!(e))?);
+			distance.insert("evaluation_price".to_string(), 
+				serde_json::to_value(getf(consts.distance().evaluation_price())).map_err(|e| anyhow!(e))?);
+			
+			let mut currency = HashMap::new();
+			currency.insert("existential_deposit".to_string(), getf(consts.balances().existential_deposit()));
+			
+			let mut provide_randomness = HashMap::new();
+			provide_randomness.insert("max_requests".to_string(), 
+				serde_json::to_value(getu32(consts.provide_randomness().max_requests())).map_err(|e| anyhow!(e))?);
+			provide_randomness.insert("request_price".to_string(), 
+				serde_json::to_value(getf(consts.provide_randomness().request_price())).map_err(|e| anyhow!(e))?);
+			
+			let mut universal_dividend = HashMap::new();
+			universal_dividend.insert("max_past_reeval".to_string(), 
+				serde_json::to_value(getu32(consts.universal_dividend().max_past_reeval())).map_err(|e| anyhow!(e))?);
+			universal_dividend.insert("square_money_growth_rate".to_string(), 
+				serde_json::to_value(format!("{:?}", getp(consts.universal_dividend().square_money_growth_rate()))).map_err(|e| anyhow!(e))?);
+			universal_dividend.insert("ud_creation_period".to_string(), 
+				serde_json::to_value(getu64(consts.universal_dividend().ud_creation_period())).map_err(|e| anyhow!(e))?);
+			universal_dividend.insert("ud_reeval_period".to_string(), 
+				serde_json::to_value(getu64(consts.universal_dividend().ud_reeval_period())).map_err(|e| anyhow!(e))?);
+			
+			// Create the view
+			let view = RuntimeInfoView {
+				identity,
+				certification,
+				wot,
+				membership,
+				smith_members,
+				distance,
+				currency,
+				provide_randomness,
+				universal_dividend,
+			};
+			
+			println!("{}", serde_json::to_string_pretty(&view).map_err(|e| anyhow!(e))?);
+		}
+	}
+	
+	Ok(())
 }
diff --git a/src/commands/smith.rs b/src/commands/smith.rs
index c2190c5..4e6789d 100644
--- a/src/commands/smith.rs
+++ b/src/commands/smith.rs
@@ -5,6 +5,8 @@ use commands::identity::try_get_idty_index_by_name;
 use runtime::runtime_types::gdev_runtime::opaque::SessionKeys as RuntimeSessionKeys;
 use std::collections::HashMap;
 use std::ops::Deref;
+use anyhow::anyhow;
+use serde::Serialize;
 
 type SessionKeys = [u8; 128];
 
@@ -187,8 +189,23 @@ pub async fn go_offline(data: &Data) -> Result<(), subxt::Error> {
 	.await
 }
 
-/// get online authorities
-pub async fn online(data: &Data) -> Result<(), subxt::Error> {
+/// Smith authorities view for JSON serialization
+#[derive(Serialize)]
+struct SmithAuthoritiesView {
+	online: Vec<SmithAuthorityView>,
+	incoming: Vec<SmithAuthorityView>,
+	outgoing: Vec<SmithAuthorityView>,
+}
+
+/// Smith authority view for JSON serialization
+#[derive(Serialize)]
+struct SmithAuthorityView {
+	id: IdtyId,
+	name: Option<String>,
+}
+
+/// get online smiths
+pub async fn online(data: &Data) -> Result<(), GcliError> {
 	let client = data.client();
 
 	let online_authorities = client
@@ -221,62 +238,121 @@ pub async fn online(data: &Data) -> Result<(), subxt::Error> {
 		.await?
 		.unwrap_or_default();
 
-	if let Some(indexer) = &data.indexer {
-		let mut names = HashMap::<IdtyId, String>::new();
-		indexer
-			.names_by_indexes(&online_authorities)
-			.await
-			.into_iter()
-			.for_each(|e| {
-				names.insert(e.0, e.1);
-			});
-		println!("Online:");
-		println!(
-			"{}",
-			online_authorities
-				.iter()
-				.map(|i| names
-					.get(i)
-					.map(|n| n.to_string())
-					.unwrap_or(format!("{} <no name found>", i)))
-				.collect::<Vec<String>>()
-				.join(", ")
-		);
+	match data.args.output_format {
+		OutputFormat::Human => {
+			if let Some(indexer) = &data.indexer {
+				let mut names = HashMap::<IdtyId, String>::new();
+				indexer
+					.names_by_indexes(&online_authorities)
+					.await
+					.into_iter()
+					.for_each(|e| {
+						names.insert(e.0, e.1);
+					});
+				println!("Online:");
+				println!(
+					"{}",
+					online_authorities
+						.iter()
+						.map(|i| names
+							.get(i)
+							.map(|n| n.to_string())
+							.unwrap_or(format!("{} <no name found>", i)))
+						.collect::<Vec<String>>()
+						.join(", ")
+				);
 
-		println!("Incoming:");
-		println!(
-			"{}",
-			incoming_authorities
-				.iter()
-				.map(|i| names
-					.get(i)
-					.map(|n| n.to_string())
-					.unwrap_or(format!("{} <no name found>", i)))
-				.collect::<Vec<String>>()
-				.join(", ")
-		);
+				println!("Incoming:");
+				println!(
+					"{}",
+					incoming_authorities
+						.iter()
+						.map(|i| names
+							.get(i)
+							.map(|n| n.to_string())
+							.unwrap_or(format!("{} <no name found>", i)))
+						.collect::<Vec<String>>()
+						.join(", ")
+				);
 
-		println!("Outgoing:");
-		println!(
-			"{}",
-			outgoing_authorities
-				.iter()
-				.map(|i| names
-					.get(i)
-					.map(|n| n.to_string())
-					.unwrap_or(format!("{} <no name found>", i)))
-				.collect::<Vec<String>>()
-				.join(", ")
-		);
-	} else {
-		println!("Online:");
-		println!("{online_authorities:?}");
+				println!("Outgoing:");
+				println!(
+					"{}",
+					outgoing_authorities
+						.iter()
+						.map(|i| names
+							.get(i)
+							.map(|n| n.to_string())
+							.unwrap_or(format!("{} <no name found>", i)))
+						.collect::<Vec<String>>()
+						.join(", ")
+				);
+			} else {
+				println!("Online:");
+				println!("{online_authorities:?}");
 
-		println!("Incoming:");
-		println!("{incoming_authorities:?}");
+				println!("Incoming:");
+				println!("{incoming_authorities:?}");
 
-		println!("Outgoing:");
-		println!("{outgoing_authorities:?}");
+				println!("Outgoing:");
+				println!("{outgoing_authorities:?}");
+			}
+		}
+		OutputFormat::Json => {
+			let mut online_view = Vec::new();
+			let mut incoming_view = Vec::new();
+			let mut outgoing_view = Vec::new();
+			
+			let names = if let Some(indexer) = &data.indexer {
+				let mut names_map = HashMap::<IdtyId, String>::new();
+				let all_authorities = [
+					online_authorities.clone(),
+					incoming_authorities.clone(),
+					outgoing_authorities.clone(),
+				].concat();
+				
+				indexer
+					.names_by_indexes(&all_authorities)
+					.await
+					.into_iter()
+					.for_each(|e| {
+						names_map.insert(e.0, e.1);
+					});
+				
+				names_map
+			} else {
+				HashMap::new()
+			};
+			
+			for id in online_authorities {
+				online_view.push(SmithAuthorityView {
+					id,
+					name: names.get(&id).cloned(),
+				});
+			}
+			
+			for id in incoming_authorities {
+				incoming_view.push(SmithAuthorityView {
+					id,
+					name: names.get(&id).cloned(),
+				});
+			}
+			
+			for id in outgoing_authorities {
+				outgoing_view.push(SmithAuthorityView {
+					id,
+					name: names.get(&id).cloned(),
+				});
+			}
+			
+			let view = SmithAuthoritiesView {
+				online: online_view,
+				incoming: incoming_view,
+				outgoing: outgoing_view,
+			};
+			
+			println!("{}", serde_json::to_string(&view).map_err(|e| anyhow!(e))?);
+		}
 	}
 
 	Ok(())
diff --git a/src/commands/vault.rs b/src/commands/vault.rs
index 07b40fc..f9a21db 100644
--- a/src/commands/vault.rs
+++ b/src/commands/vault.rs
@@ -6,6 +6,7 @@ use crate::entities::vault_account::{AccountTreeNode, ActiveModel, DbAccountId};
 use crate::*;
 use crate::keys::seed_from_cesium;
 use age::secrecy::Secret;
+use anyhow::anyhow;
 use sea_orm::ActiveValue::Set;
 use sea_orm::{ConnectionTrait, TransactionTrait};
 use sea_orm::ModelTrait;
@@ -213,19 +214,33 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 				let all_account_tree_node_hierarchies =
 					vault_account::fetch_all_base_account_tree_node_hierarchies(db).await?;
 
-				let table = display::compute_vault_accounts_table_with_g1v1(&all_account_tree_node_hierarchies, show_g1v1, show_type)?;
-
-				println!("available SS58 Addresses:");
-				println!("{table}");
+				match data.args.output_format {
+					OutputFormat::Human => {
+						let table = display::compute_vault_accounts_table_with_g1v1(&all_account_tree_node_hierarchies, show_g1v1, show_type)?;
+						println!("available SS58 Addresses:");
+						println!("{table}");
+					}
+					OutputFormat::Json => {
+						let json_view = display::compute_vault_accounts_json(&all_account_tree_node_hierarchies, show_g1v1);
+						println!("{}", serde_json::to_string(&json_view).map_err(|e| anyhow!(e))?);
+					}
+				}
 			}
 			ListChoice::Base { show_g1v1, show_type } => {
 				let base_account_tree_nodes =
 					vault_account::fetch_only_base_account_tree_nodes(db).await?;
 
-				let table = display::compute_vault_accounts_table_with_g1v1(&base_account_tree_nodes, show_g1v1, show_type)?;
-
-				println!("available <Base> SS58 Addresses:");
-				println!("{table}");
+				match data.args.output_format {
+					OutputFormat::Human => {
+						let table = display::compute_vault_accounts_table_with_g1v1(&base_account_tree_nodes, show_g1v1, show_type)?;
+						println!("available <Base> SS58 Addresses:");
+						println!("{table}");
+					}
+					OutputFormat::Json => {
+						let json_view = display::compute_vault_accounts_json(&base_account_tree_nodes, show_g1v1);
+						println!("{}", serde_json::to_string(&json_view).map_err(|e| anyhow!(e))?);
+					}
+				}
 			}
 			ListChoice::For {
 				address_or_vault_name,
@@ -238,29 +253,52 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 				let base_account_tree_node =
 					vault_account::get_base_account_tree_node(&account_tree_node);
 
-				let table = display::compute_vault_accounts_table_with_g1v1(&[base_account_tree_node], show_g1v1, show_type)?;
-
-				println!(
-					"available SS58 Addresses linked to {}:",
-					account_tree_node.borrow().account
-				);
-				println!("{table}");
+				match data.args.output_format {
+					OutputFormat::Human => {
+						let table = display::compute_vault_accounts_table_with_g1v1(&[base_account_tree_node.clone()], show_g1v1, show_type)?;
+						println!(
+							"available SS58 Addresses linked to {}:",
+							account_tree_node.borrow().account
+						);
+						println!("{table}");
+					}
+					OutputFormat::Json => {
+						let json_view = display::compute_vault_accounts_json(&[base_account_tree_node], show_g1v1);
+						println!("{}", serde_json::to_string(&json_view).map_err(|e| anyhow!(e))?);
+					}
+				}
 			}
 		},
 		Subcommand::ListFiles => {
 			let vault_key_addresses = fetch_vault_key_addresses(&data).await?;
 
-			let table = display::compute_vault_key_files_table(&vault_key_addresses)?;
-
-			println!("available key files (needs to be migrated with command `vault migrate` in order to use them):");
-			println!("{table}");
+			match data.args.output_format {
+				OutputFormat::Human => {
+					let table = display::compute_vault_key_files_table(&vault_key_addresses)?;
+					println!("available key files (needs to be migrated with command `vault migrate` in order to use them):");
+					println!("{table}");
+				}
+				OutputFormat::Json => {
+					println!("{}", serde_json::to_string(&vault_key_addresses).map_err(|e| anyhow!(e))?);
+				}
+			}
 		}
 		Subcommand::Use {
 			address_or_vault_name,
 		} => {
 			let account = retrieve_vault_account(db, address_or_vault_name).await?;
 
-			println!("Using: {}", account);
+			match data.args.output_format {
+				OutputFormat::Human => {
+					println!("Using: {}", account);
+				}
+				OutputFormat::Json => {
+					let json_view = serde_json::json!({
+						"address": account.to_string()
+					});
+					println!("{}", serde_json::to_string(&json_view).map_err(|e| anyhow!(e))?);
+				}
+			}
 
 			let updated_cfg = conf::Config {
 				address: Some(account.address.0),
@@ -516,7 +554,17 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 				password,
 			)?;
 
-			println!("Substrate URI: '{account_to_derive_secret_suri}'")
+			match data.args.output_format {
+				OutputFormat::Human => {
+					println!("Substrate URI: '{account_to_derive_secret_suri}'")
+				}
+				OutputFormat::Json => {
+					let json_view = serde_json::json!({
+						"substrate_uri": account_to_derive_secret_suri
+					});
+					println!("{}", serde_json::to_string(&json_view).map_err(|e| anyhow!(e))?);
+				}
+			}
 		}
 		Subcommand::Migrate => {
 			println!("Migrating existing key files to db");
@@ -584,7 +632,19 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 			println!("Migration done");
 		}
 		Subcommand::Where => {
-			println!("{}", data.project_dir.data_dir().to_str().unwrap());
+			let data_dir = data.project_dir.data_dir().to_str().unwrap();
+			
+			match data.args.output_format {
+				OutputFormat::Human => {
+					println!("{}", data_dir);
+				}
+				OutputFormat::Json => {
+					let json_view = serde_json::json!({
+						"data_dir": data_dir
+					});
+					println!("{}", serde_json::to_string(&json_view).map_err(|e| anyhow!(e))?);
+				}
+			}
 		}
 	};
 
diff --git a/src/commands/vault/display.rs b/src/commands/vault/display.rs
index 5f024d6..35e952e 100644
--- a/src/commands/vault/display.rs
+++ b/src/commands/vault/display.rs
@@ -4,6 +4,7 @@ use crate::entities::vault_account::AccountTreeNode;
 use crate::keys::CryptoScheme;
 use crate::utils::GcliError;
 use comfy_table::{Cell, Table};
+use serde::Serialize;
 use std::cell::RefCell;
 use std::rc::Rc;
 use std::str;
@@ -181,6 +182,91 @@ pub fn compute_vault_accounts_row_with_g1v1(
 	Ok(rows)
 }
 
+/// Serializable structure for JSON output
+#[derive(Serialize)]
+pub struct VaultAccountView {
+	address: String,
+	crypto_scheme: Option<String>,
+	wallet_type: Option<String>,
+	path: String,
+	name: String,
+	g1v1_public_key: Option<String>,
+	children: Vec<VaultAccountView>,
+}
+
+/// Compute a serializable structure for JSON output
+pub fn compute_vault_accounts_json(
+	account_tree_nodes: &[Rc<RefCell<AccountTreeNode>>],
+	show_g1v1: bool,
+) -> Vec<VaultAccountView> {
+	let mut result = Vec::new();
+	
+	for account_tree_node in account_tree_nodes {
+		result.push(compute_vault_account_json(account_tree_node, show_g1v1));
+	}
+	
+	result
+}
+
+/// Compute a serializable structure for a single account tree node
+fn compute_vault_account_json(
+	account_tree_node: &Rc<RefCell<AccountTreeNode>>,
+	show_g1v1: bool,
+) -> VaultAccountView {
+	let empty_string = "".to_string();
+	let borrowed_node = account_tree_node.borrow();
+	
+	let name = if let Some(name) = borrowed_node.account.name.clone() {
+		name
+	} else if let Some(computed_name) = vault_account::compute_name_account_tree_node(account_tree_node) {
+		format!("<{}>", computed_name)
+	} else {
+		empty_string.clone()
+	};
+	
+	let (path, crypto_scheme, wallet_type, g1v1_public_key) = if let Some(path) = borrowed_node.account.path.clone() {
+		(path, None, None, None)
+	} else {
+		let crypto_scheme = borrowed_node.account.crypto_scheme.map(|cs| {
+			let scheme = CryptoScheme::from(cs);
+			let scheme_str: &str = scheme.into();
+			scheme_str.to_string()
+		});
+		
+		let wallet_type = borrowed_node.account.secret_format.map(|sf| {
+			match crate::keys::SecretFormat::from(sf) {
+				crate::keys::SecretFormat::G1v1 => "G1v1".to_string(),
+				crate::keys::SecretFormat::Substrate => "Mnemonic".to_string(),
+				crate::keys::SecretFormat::Seed => "Seed".to_string(),
+				crate::keys::SecretFormat::Predefined => "Predefined".to_string(),
+			}
+		});
+		
+		let g1v1_public_key = if show_g1v1 && crypto_scheme.as_deref() == Some("ed25519") {
+			Some(cesium::compute_g1v1_public_key_from_ed25519_account_id(&borrowed_node.account.address.0))
+		} else {
+			None
+		};
+		
+		(format!("<{}>", borrowed_node.account.account_type()), crypto_scheme, wallet_type, g1v1_public_key)
+	};
+	
+	let mut children = Vec::new();
+	for child in &borrowed_node.children {
+		children.push(compute_vault_account_json(child, show_g1v1));
+	}
+	
+	VaultAccountView {
+		address: borrowed_node.account.address.to_string(),
+		crypto_scheme,
+		wallet_type,
+		path,
+		name,
+		g1v1_public_key,
+		children,
+	}
+}
+
 #[cfg(test)]
 mod tests {
 	mod vault_accounts_table_tests {
diff --git a/src/conf.rs b/src/conf.rs
index 509e0c5..09c83f4 100644
--- a/src/conf.rs
+++ b/src/conf.rs
@@ -2,6 +2,7 @@ use crate::entities::vault_account;
 use crate::entities::vault_account::DbAccountId;
 use crate::*;
 use serde::{Deserialize, Serialize};
+use anyhow::anyhow;
 
 const APP_NAME: &str = "gcli";
 
@@ -73,6 +74,15 @@ pub enum Subcommand {
 	Default,
 }
 
+/// Config view for JSON serialization
+#[derive(Serialize)]
+struct ConfigView {
+	duniter_endpoint: String,
+	indexer_endpoint: String,
+	address: Option<String>,
+	vault_account: Option<String>,
+}
+
 /// handle conf command
 pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliError> {
 	// match subcommand
@@ -84,15 +94,41 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 			);
 		}
 		Subcommand::Show => {
-			println!("{}", data.cfg);
-			if let Some(ref account_id) = data.cfg.address {
-				if let Some(account) = vault_account::find_by_id(
-					data.connect_db(),
-					&DbAccountId::from(account_id.clone()),
-				)
-				.await?
-				{
-					println!("(Vault: {})", account);
+			match data.args.output_format {
+				OutputFormat::Human => {
+					println!("{}", data.cfg);
+					if let Some(ref account_id) = data.cfg.address {
+						if let Some(account) = vault_account::find_by_id(
+							data.connect_db(),
+							&DbAccountId::from(account_id.clone()),
+						)
+						.await?
+						{
+							println!("(Vault: {})", account);
+						}
+					}
+				}
+				OutputFormat::Json => {
+					let mut vault_account_str = None;
+					if let Some(ref account_id) = data.cfg.address {
+						if let Some(account) = vault_account::find_by_id(
+							data.connect_db(),
+							&DbAccountId::from(account_id.clone()),
+						)
+						.await?
+						{
+							vault_account_str = Some(account.to_string());
+						}
+					}
+					
+					let view = ConfigView {
+						duniter_endpoint: data.cfg.duniter_endpoint.clone(),
+						indexer_endpoint: data.cfg.indexer_endpoint.clone(),
+						address: data.cfg.address.as_ref().map(|a| a.to_string()),
+						vault_account: vault_account_str,
+					};
+					
+					println!("{}", serde_json::to_string(&view).map_err(|e| anyhow!(e))?);
 				}
 			}
 		}
-- 
GitLab