From 84043f187d79ca4ba6bb42f48264466a57d91611 Mon Sep 17 00:00:00 2001
From: Hugo Trentesaux <hugo@trentesaux.fr>
Date: Wed, 31 May 2023 08:58:16 +0200
Subject: [PATCH] refac

WIP add tracking progress

WIP refac progress tracking

WIP generic runtime

wip needing runtime name

wip refac smith cert
---
 Cargo.toml                 |   8 ++
 src/cache.rs               |   5 +-
 src/commands/collective.rs |  15 +--
 src/commands/expire.rs     |  23 ++--
 src/commands/identity.rs   |  39 ++++---
 src/commands/net_test.rs   |  11 +-
 src/commands/oneshot.rs    |  30 ++---
 src/commands/revocation.rs |   6 +-
 src/commands/smith.rs      |  57 +++-------
 src/commands/sudo.rs       |   4 +-
 src/commands/transfer.rs   |  13 ++-
 src/keys.rs                |   3 +-
 src/main.rs                | 224 +++++++++++++++++++++++++++----------
 13 files changed, 272 insertions(+), 166 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index fce1a3e..06ab6f5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,3 +22,11 @@ serde_json = "1.0.94"
 sp-core = { git = "https://github.com/duniter/substrate", branch = "duniter-substrate-v0.9.32" }
 subxt =  { git = "https://github.com/duniter/subxt.git", branch = "duniter-substrate-v0.9.32" }
 tokio = { version = "1.26.0", features = ["macros"] }
+
+# allows to build gcli with different predefined networks 
+[features]
+default = ["dev"] # default feature is "dev"
+dev = []
+gdev = []
+gtest = []
+g1 = []
diff --git a/src/cache.rs b/src/cache.rs
index b77206d..643f7a4 100644
--- a/src/cache.rs
+++ b/src/cache.rs
@@ -1,4 +1,5 @@
-use crate::{gdev, indexer::*, Client};
+use crate::indexer::*;
+use crate::*;
 
 use anyhow::{anyhow, Result};
 use std::collections::{hash_map, HashMap};
@@ -31,7 +32,7 @@ impl<'a> IdentityCache<'a> {
 						.client
 						.storage()
 						.fetch(
-							&gdev::storage().identity().identities(identity_id),
+							&runtime::storage().identity().identities(identity_id),
 							Some(parent_hash),
 						)
 						.await?
diff --git a/src/commands/collective.rs b/src/commands/collective.rs
index 70dd364..a63b55b 100644
--- a/src/commands/collective.rs
+++ b/src/commands/collective.rs
@@ -1,4 +1,5 @@
-use crate::{gdev, indexer::*, Args, Client};
+use crate::indexer::*;
+use crate::*;
 
 use anyhow::Result;
 use sp_core::{sr25519::Pair, H256};
@@ -7,7 +8,7 @@ use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
 pub async fn technical_committee_members(client: Client, args: &Args) -> Result<()> {
 	let parent_hash = client
 		.storage()
-		.fetch(&gdev::storage().system().parent_hash(), None)
+		.fetch(&runtime::storage().system().parent_hash(), None)
 		.await?
 		.unwrap();
 
@@ -27,7 +28,7 @@ pub async fn technical_committee_members(client: Client, args: &Args) -> Result<
 	for account_id in client
 		.storage()
 		.fetch(
-			&gdev::storage().technical_committee().members(),
+			&runtime::storage().technical_committee().members(),
 			Some(parent_hash),
 		)
 		.await?
@@ -45,7 +46,7 @@ pub async fn technical_committee_members(client: Client, args: &Args) -> Result<
 				client
 					.storage()
 					.fetch(
-						&gdev::storage().identity().identity_index_of(&account_id),
+						&runtime::storage().identity().identity_index_of(&account_id),
 						Some(parent_hash),
 					)
 					.await
@@ -66,14 +67,14 @@ pub async fn technical_committee_members(client: Client, args: &Args) -> Result<
 pub async fn technical_committee_proposals(client: Client) -> Result<()> {
 	let parent_hash = client
 		.storage()
-		.fetch(&gdev::storage().system().parent_hash(), None)
+		.fetch(&runtime::storage().system().parent_hash(), None)
 		.await?
 		.unwrap();
 
 	let mut proposals_iter = client
 		.storage()
 		.iter(
-			gdev::storage()
+			runtime::storage()
 				.technical_committee()
 				.proposal_of(H256::default()),
 			10,
@@ -99,7 +100,7 @@ pub async fn technical_committee_vote(
 	client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx()
+			&runtime::tx()
 				.technical_committee()
 				.vote(proposal_hash, proposal_index, vote),
 			&PairSigner::new(pair),
diff --git a/src/commands/expire.rs b/src/commands/expire.rs
index a23a530..f749f89 100644
--- a/src/commands/expire.rs
+++ b/src/commands/expire.rs
@@ -1,4 +1,5 @@
-use crate::{cache, gdev, indexer::*, Args, Client};
+use crate::indexer::*;
+use crate::*;
 
 use anyhow::Result;
 use futures::join;
@@ -16,11 +17,11 @@ pub async fn monitor_expirations(
 
 	let parent_hash = client
 		.storage()
-		.fetch(&gdev::storage().system().parent_hash(), None)
+		.fetch(&runtime::storage().system().parent_hash(), None)
 		.await?
 		.unwrap();
-	let addr_current_block = gdev::storage().system().number();
-	let addr_current_session = gdev::storage().session().current_index();
+	let addr_current_block = runtime::storage().system().number();
+	let addr_current_session = runtime::storage().session().current_index();
 	let (current_block, current_session) = join!(
 		client
 			.storage()
@@ -51,7 +52,7 @@ pub async fn monitor_expirations(
 	let mut must_rotate_keys_before_iter = client
 		.storage()
 		.iter(
-			gdev::storage()
+			runtime::storage()
 				.authority_members()
 				.must_rotate_keys_before(0),
 			10,
@@ -85,7 +86,7 @@ pub async fn monitor_expirations(
 	let mut basic_certs_iter = client
 		.storage()
 		.iter(
-			gdev::storage().cert().storage_certs_removable_on(0),
+			runtime::storage().cert().storage_certs_removable_on(0),
 			10,
 			Some(parent_hash),
 		)
@@ -101,7 +102,9 @@ pub async fn monitor_expirations(
 	let mut smith_certs_iter = client
 		.storage()
 		.iter(
-			gdev::storage().smith_cert().storage_certs_removable_on(0),
+			runtime::storage()
+				.smith_cert()
+				.storage_certs_removable_on(0),
 			10,
 			Some(parent_hash),
 		)
@@ -143,7 +146,7 @@ pub async fn monitor_expirations(
 	let mut basic_membership_iter = client
 		.storage()
 		.iter(
-			gdev::storage().membership().memberships_expire_on(0),
+			runtime::storage().membership().memberships_expire_on(0),
 			10,
 			Some(parent_hash),
 		)
@@ -162,7 +165,9 @@ pub async fn monitor_expirations(
 	let mut smith_membership_iter = client
 		.storage()
 		.iter(
-			gdev::storage().smith_membership().memberships_expire_on(0),
+			runtime::storage()
+				.smith_membership()
+				.memberships_expire_on(0),
 			10,
 			Some(parent_hash),
 		)
diff --git a/src/commands/identity.rs b/src/commands/identity.rs
index e5ee3ca..71562d9 100644
--- a/src/commands/identity.rs
+++ b/src/commands/identity.rs
@@ -1,7 +1,8 @@
-use crate::{gdev, indexer::*, Args, Client};
+use crate::indexer::*;
+use crate::*;
 
-use crate::gdev::runtime_types::common_runtime::entities::IdtyData;
-use crate::gdev::runtime_types::pallet_identity::types::*;
+use crate::runtime::runtime_types::common_runtime::entities::IdtyData;
+use crate::runtime::runtime_types::pallet_identity::types::*;
 use anyhow::{anyhow, Result};
 use sp_core::{crypto::AccountId32, sr25519::Pair};
 use std::str::FromStr;
@@ -84,7 +85,7 @@ pub async fn get_idty_index_by_account_id(
 	Ok(client
 		.storage()
 		.fetch(
-			&gdev::storage().identity().identity_index_of(account_id),
+			&runtime::storage().identity().identity_index_of(account_id),
 			None,
 		)
 		.await?)
@@ -96,32 +97,36 @@ pub async fn get_identity_by_index(
 ) -> Result<Option<IdtyValue<u32, AccountId32, IdtyData>>> {
 	Ok(client
 		.storage()
-		.fetch(&gdev::storage().identity().identities(idty_index), None)
+		.fetch(&runtime::storage().identity().identities(idty_index), None)
 		.await?)
 }
 
-pub async fn create_identity(pair: Pair, client: Client, target: AccountId32) -> Result<()> {
-	client
+pub async fn create_identity(
+	pair: Pair,
+	client: Client,
+	target: AccountId32,
+) -> Result<TxProgress, subxt::Error> {
+	Ok(client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx().identity().create_identity(target),
+			&runtime::tx().identity().create_identity(target),
 			&PairSigner::new(pair),
 			BaseExtrinsicParamsBuilder::new(),
 		)
-		.await?;
-
-	Ok(())
+		.await?)
 }
 
-pub async fn confirm_identity(pair: Pair, client: Client, name: String) -> Result<()> {
-	client
+pub async fn confirm_identity(
+	pair: Pair,
+	client: Client,
+	name: String,
+) -> Result<TxProgress, subxt::Error> {
+	Ok(client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx().identity().confirm_identity(name),
+			&runtime::tx().identity().confirm_identity(name),
 			&PairSigner::new(pair),
 			BaseExtrinsicParamsBuilder::new(),
 		)
-		.await?;
-
-	Ok(())
+		.await?)
 }
diff --git a/src/commands/net_test.rs b/src/commands/net_test.rs
index ab2dff3..9a2c73b 100644
--- a/src/commands/net_test.rs
+++ b/src/commands/net_test.rs
@@ -1,4 +1,4 @@
-use crate::{gdev, Client, GdevConfig};
+use crate::*;
 
 use anyhow::{anyhow, Result};
 use sp_core::{crypto::AccountId32, sr25519::Pair, DeriveJunction, Pair as _};
@@ -36,7 +36,7 @@ pub async fn repart(
 		if let Some(pair_i_account) = client
 			.storage()
 			.fetch(
-				&gdev::storage().system().account(&pair_i.public().into()),
+				&runtime::storage().system().account(&pair_i.public().into()),
 				None,
 			)
 			.await?
@@ -50,8 +50,7 @@ pub async fn repart(
 
 pub async fn spam_roll(pair: Pair, client: Client, actual_repart: usize) -> Result<()> {
 	let mut nonce = 0;
-	let mut pairs =
-		Vec::<(PairSigner<GdevConfig, Pair>, AccountId32)>::with_capacity(actual_repart);
+	let mut pairs = Vec::<(PairSigner<Runtime, Pair>, AccountId32)>::with_capacity(actual_repart);
 	for i in 0..actual_repart {
 		let pair_i = pair
 			.derive(std::iter::once(DeriveJunction::hard::<u32>(i as u32)), None)
@@ -68,7 +67,7 @@ pub async fn spam_roll(pair: Pair, client: Client, actual_repart: usize) -> Resu
 			let watcher = client
 				.tx()
 				.create_signed_with_nonce(
-					&gdev::tx().balances().transfer(MultiAddress::Id(dest), 1),
+					&runtime::tx().balances().transfer(MultiAddress::Id(dest), 1),
 					&pairs[i].0,
 					nonce,
 					BaseExtrinsicParamsBuilder::new(),
@@ -83,7 +82,7 @@ pub async fn spam_roll(pair: Pair, client: Client, actual_repart: usize) -> Resu
 		let watcher = client
 			.tx()
 			.sign_and_submit_then_watch(
-				&gdev::tx().balances().transfer(MultiAddress::Id(dest), 1),
+				&runtime::tx().balances().transfer(MultiAddress::Id(dest), 1),
 				&pairs[actual_repart - 1].0,
 				BaseExtrinsicParamsBuilder::new(),
 			)
diff --git a/src/commands/oneshot.rs b/src/commands/oneshot.rs
index 9933693..c6d4fb9 100644
--- a/src/commands/oneshot.rs
+++ b/src/commands/oneshot.rs
@@ -1,4 +1,4 @@
-use crate::{gdev, Client};
+use crate::*;
 
 use anyhow::Result;
 use sp_core::{crypto::AccountId32, sr25519::Pair};
@@ -13,7 +13,7 @@ pub async fn create_oneshot_account(
 	client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx()
+			&runtime::tx()
 				.oneshot_account()
 				.create_oneshot_account(dest.into(), balance),
 			&PairSigner::new(pair),
@@ -32,20 +32,22 @@ pub async fn consume_oneshot_account(
 ) -> Result<()> {
 	let number = client
 		.storage()
-		.fetch(&gdev::storage().system().number(), None)
+		.fetch(&runtime::storage().system().number(), None)
 		.await?
 		.unwrap();
 	client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx().oneshot_account().consume_oneshot_account(
+			&runtime::tx().oneshot_account().consume_oneshot_account(
 				number,
 				if dest_oneshot {
-					gdev::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
+					runtime::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
 						dest.into(),
 					)
 				} else {
-					gdev::runtime_types::pallet_oneshot_account::types::Account::Normal(dest.into())
+					runtime::runtime_types::pallet_oneshot_account::types::Account::Normal(
+						dest.into(),
+					)
 				},
 			),
 			&PairSigner::new(pair),
@@ -67,31 +69,31 @@ pub async fn consume_oneshot_account_with_remaining(
 ) -> Result<()> {
 	let number = client
 		.storage()
-		.fetch(&gdev::storage().system().number(), None)
+		.fetch(&runtime::storage().system().number(), None)
 		.await?
 		.unwrap();
 	client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx()
+			&runtime::tx()
 				.oneshot_account()
 				.consume_oneshot_account_with_remaining(
 					number,
 					if dest_oneshot {
-						gdev::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
+						runtime::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
 							dest.into(),
 						)
 					} else {
-						gdev::runtime_types::pallet_oneshot_account::types::Account::Normal(
+						runtime::runtime_types::pallet_oneshot_account::types::Account::Normal(
 							dest.into(),
 						)
 					},
 					if remaining_to_oneshot {
-						gdev::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
+						runtime::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
 							remaining_to.into(),
 						)
 					} else {
-						gdev::runtime_types::pallet_oneshot_account::types::Account::Normal(
+						runtime::runtime_types::pallet_oneshot_account::types::Account::Normal(
 							remaining_to.into(),
 						)
 					},
@@ -111,7 +113,9 @@ pub async fn oneshot_account_balance(client: Client, account: AccountId32) -> Re
 		client
 			.storage()
 			.fetch(
-				&gdev::storage().oneshot_account().oneshot_accounts(&account),
+				&runtime::storage()
+					.oneshot_account()
+					.oneshot_accounts(&account),
 				None
 			)
 			.await?
diff --git a/src/commands/revocation.rs b/src/commands/revocation.rs
index b20ef0c..a03dd9c 100644
--- a/src/commands/revocation.rs
+++ b/src/commands/revocation.rs
@@ -1,4 +1,4 @@
-use crate::{gdev, Client};
+use crate::*;
 
 use anyhow::Result;
 use futures::join;
@@ -6,8 +6,8 @@ use sp_core::{sr25519::Pair, Encode, Pair as _};
 
 pub async fn gen_revoc_doc(api: &Client, pair: &Pair) -> Result<()> {
 	let account_id: sp_core::crypto::AccountId32 = pair.public().into();
-	let addr_idty_index = gdev::storage().identity().identity_index_of(&account_id);
-	let addr_block_hash = gdev::storage().system().block_hash(0);
+	let addr_idty_index = runtime::storage().identity().identity_index_of(&account_id);
+	let addr_block_hash = runtime::storage().system().block_hash(0);
 	let (idty_index, genesis_hash) = join!(
 		api.storage().fetch(&addr_idty_index, None,),
 		api.storage().fetch(&addr_block_hash, None)
diff --git a/src/commands/smith.rs b/src/commands/smith.rs
index ca8928f..3bc83d3 100644
--- a/src/commands/smith.rs
+++ b/src/commands/smith.rs
@@ -22,7 +22,7 @@ pub async fn set_session_keys(pair: Pair, client: Client, session_keys: SessionK
 	client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx()
+			&runtime::tx()
 				.authority_members()
 				.set_session_keys(session_keys),
 			&PairSigner::new(pair),
@@ -42,7 +42,7 @@ pub async fn go_online(pair: Pair, client: Client) -> Result<()> {
 	if client
 		.storage()
 		.fetch(
-			&gdev::storage()
+			&runtime::storage()
 				.session()
 				.next_keys(AccountId32::from(pair.public())),
 			None,
@@ -56,7 +56,7 @@ pub async fn go_online(pair: Pair, client: Client) -> Result<()> {
 	client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx().authority_members().go_online(),
+			&runtime::tx().authority_members().go_online(),
 			&PairSigner::new(pair),
 			BaseExtrinsicParamsBuilder::new(),
 		)
@@ -69,7 +69,7 @@ pub async fn go_offline(pair: Pair, client: Client) -> Result<()> {
 	client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx().authority_members().go_offline(),
+			&runtime::tx().authority_members().go_offline(),
 			&PairSigner::new(pair),
 			BaseExtrinsicParamsBuilder::new(),
 		)
@@ -81,7 +81,7 @@ pub async fn go_offline(pair: Pair, client: Client) -> Result<()> {
 pub async fn online(client: Client, args: &Args) -> Result<()> {
 	let parent_hash = client
 		.storage()
-		.fetch(&gdev::storage().system().parent_hash(), None)
+		.fetch(&runtime::storage().system().parent_hash(), None)
 		.await?
 		.unwrap();
 
@@ -104,7 +104,7 @@ pub async fn online(client: Client, args: &Args) -> Result<()> {
 	let online_authorities = client
 		.storage()
 		.fetch(
-			&gdev::storage().authority_members().online_authorities(),
+			&runtime::storage().authority_members().online_authorities(),
 			Some(parent_hash),
 		)
 		.await?
@@ -124,7 +124,9 @@ pub async fn online(client: Client, args: &Args) -> Result<()> {
 	let incoming_authorities = client
 		.storage()
 		.fetch(
-			&gdev::storage().authority_members().incoming_authorities(),
+			&runtime::storage()
+				.authority_members()
+				.incoming_authorities(),
 			Some(parent_hash),
 		)
 		.await?
@@ -144,7 +146,9 @@ pub async fn online(client: Client, args: &Args) -> Result<()> {
 	let outgoing_authorities = client
 		.storage()
 		.fetch(
-			&gdev::storage().authority_members().outgoing_authorities(),
+			&runtime::storage()
+				.authority_members()
+				.outgoing_authorities(),
 			Some(parent_hash),
 		)
 		.await?
@@ -164,41 +168,12 @@ pub async fn online(client: Client, args: &Args) -> Result<()> {
 	Ok(())
 }
 
-/// emit a new smith cert from signer's identity to target identity
-pub async fn emit_cert(args: Args, receiver: u32) -> Result<()> {
-	// issuer key
-	let pair = get_keys(
-		args.secret_format,
-		&args.address,
-		&args.secret,
-		NeededKeys::Secret,
-	)?
-	.1
-	.unwrap();
-
-	// connect to client
-	let client = Client::from_url(&args.url).await.unwrap();
-
-	// get issuer index
-	let issuer = commands::identity::get_idty_index_by_account_id(
-		client.clone(),
-		&AccountId32::from(pair.public()),
-	)
-	.await?
-	.ok_or(anyhow!("can not certify if not member"))?;
-
-	// submit and track certification
-	cert(client, pair, issuer, receiver).await?;
-
-	Ok(())
-}
-
 /// submit a certification and track progress
-async fn cert(client: Client, pair: Pair, issuer: u32, receiver: u32) -> Result<()> {
+pub async fn cert(client: Client, pair: Pair, issuer: u32, receiver: u32) -> Result<()> {
 	let mut progress = client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx().smith_cert().add_cert(issuer, receiver),
+			&runtime::tx().smith_cert().add_cert(issuer, receiver),
 			&PairSigner::new(pair),
 			BaseExtrinsicParamsBuilder::new(),
 		)
@@ -222,8 +197,8 @@ async fn cert(client: Client, pair: Pair, issuer: u32, receiver: u32) -> Result<
 	// get the block events and return if ExtrinsicFailed
 	let events = in_block.wait_for_success().await?;
 	// look for the expected event
-	let new_cert_event = events.find_first::<gdev::smith_cert::events::NewCert>()?;
-	let renew_cert_event = events.find_first::<gdev::smith_cert::events::RenewedCert>()?;
+	let new_cert_event = events.find_first::<runtime::smith_cert::events::NewCert>()?;
+	let renew_cert_event = events.find_first::<runtime::smith_cert::events::RenewedCert>()?;
 
 	if let Some(event) = new_cert_event {
 		println!("{event:?}");
diff --git a/src/commands/sudo.rs b/src/commands/sudo.rs
index 7987a08..728708f 100644
--- a/src/commands/sudo.rs
+++ b/src/commands/sudo.rs
@@ -1,4 +1,4 @@
-use crate::{gdev, Client};
+use crate::*;
 
 use anyhow::Result;
 use sp_core::{crypto::AccountId32, sr25519::Pair};
@@ -8,7 +8,7 @@ pub async fn set_key(pair: Pair, client: Client, new_key: AccountId32) -> Result
 	client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx().sudo().set_key(new_key.into()),
+			&runtime::tx().sudo().set_key(new_key.into()),
 			&PairSigner::new(pair),
 			BaseExtrinsicParamsBuilder::new(),
 		)
diff --git a/src/commands/transfer.rs b/src/commands/transfer.rs
index 857dbde..b3cefa6 100644
--- a/src/commands/transfer.rs
+++ b/src/commands/transfer.rs
@@ -1,11 +1,12 @@
-use crate::{gdev, Client};
+use crate::*;
 
 use anyhow::Result;
 use sp_core::{crypto::AccountId32, sr25519::Pair};
 use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
 
-type Call = gdev::runtime_types::gdev_runtime::RuntimeCall;
-type BalancesCall = gdev::runtime_types::pallet_balances::pallet::Call;
+#[cfg(any(feature = "dev", feature = "gdev"))] // find how to get runtime calls
+type Call = runtime::runtime_types::gdev_runtime::RuntimeCall;
+type BalancesCall = runtime::runtime_types::pallet_balances::pallet::Call;
 
 pub async fn transfer(
 	pair: Pair,
@@ -18,7 +19,7 @@ pub async fn transfer(
 		client
 			.tx()
 			.sign_and_submit_then_watch(
-				&gdev::tx().balances().transfer(dest.into(), balance),
+				&runtime::tx().balances().transfer(dest.into(), balance),
 				&PairSigner::new(pair),
 				BaseExtrinsicParamsBuilder::new(),
 			)
@@ -27,7 +28,7 @@ pub async fn transfer(
 		client
 			.tx()
 			.sign_and_submit_then_watch(
-				&gdev::tx()
+				&runtime::tx()
 					.balances()
 					.transfer_keep_alive(dest.into(), balance),
 				&PairSigner::new(pair),
@@ -60,7 +61,7 @@ pub async fn transfer_multiple(
 	client
 		.tx()
 		.sign_and_submit_then_watch(
-			&gdev::tx().utility().batch(transactions),
+			&runtime::tx().utility().batch(transactions),
 			&PairSigner::new(pair.clone()),
 			BaseExtrinsicParamsBuilder::new(),
 		)
diff --git a/src/keys.rs b/src/keys.rs
index 5940d29..fa75289 100644
--- a/src/keys.rs
+++ b/src/keys.rs
@@ -14,11 +14,12 @@ pub enum NeededKeys {
 	Secret,
 }
 
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Default)]
 pub enum SecretFormat {
 	/// Raw 32B seed
 	Seed,
 	/// Substrate secret key or BIP39 mnemonic (optionally followed by derivation path)
+	#[default]
 	Substrate,
 }
 
diff --git a/src/main.rs b/src/main.rs
index f251d64..00dfb47 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,25 +3,28 @@ mod commands;
 mod indexer;
 mod keys;
 
-use keys::*;
-
-use anyhow::Result;
 use clap::Parser;
 use codec::Encode;
+use keys::*;
+use sp_core::sr25519::Pair;
 use sp_core::H256;
 
+#[cfg(feature = "dev")]
 #[subxt::subxt(runtime_metadata_path = "res/metadata.scale")]
-pub mod gdev {}
+pub mod runtime {}
 
-pub type Client = subxt::OnlineClient<GdevConfig>;
+pub type Client = subxt::OnlineClient<Runtime>;
+pub type AccountId = subxt::ext::sp_runtime::AccountId32;
+pub type TxInBlock = subxt::tx::TxInBlock<Runtime, Client>;
+pub type TxProgress = subxt::tx::TxProgress<Runtime, Client>;
 
-pub enum GdevConfig {}
-impl subxt::config::Config for GdevConfig {
+pub enum Runtime {}
+impl subxt::config::Config for Runtime {
 	type Index = u32;
 	type BlockNumber = u32;
 	type Hash = sp_core::H256;
 	type Hashing = subxt::ext::sp_runtime::traits::BlakeTwo256;
-	type AccountId = subxt::ext::sp_runtime::AccountId32;
+	type AccountId = AccountId;
 	type Address = subxt::ext::sp_runtime::MultiAddress<Self::AccountId, u32>;
 	type Header = subxt::ext::sp_runtime::generic::Header<
 		Self::BlockNumber,
@@ -49,7 +52,7 @@ impl From<u64> for Tip {
 	}
 }
 
-#[derive(Parser, Debug)]
+#[derive(Clone, Parser, Debug, Default)]
 #[clap(author, version, about, long_about = None)]
 pub struct Args {
 	#[clap(subcommand)]
@@ -76,20 +79,144 @@ pub struct Args {
 	url: String,
 }
 
-#[derive(Debug, clap::Subcommand)]
+/// Data of current command
+/// can also include fetched information
+#[derive(Default)]
+pub struct Data {
+	pub args: Args,
+	pub client: Option<Client>,
+	pub address: Option<AccountId>,
+	pub keypair: Option<Pair>,
+	pub idty_index: Option<u32>,
+}
+
+// implement helper functions for Data
+impl Data {
+	/// constructor
+	pub fn new(args: Args) -> Self {
+		Self {
+			args,
+			..Default::default()
+		}
+	}
+	// --- getters ---
+	// the "unwrap" should not fail if data is well prepared
+	pub fn client(&self) -> Client {
+		self.client.clone().unwrap()
+	}
+	pub fn address(&self) -> AccountId {
+		self.address.clone().unwrap()
+	}
+	pub fn keypair(&self) -> Pair {
+		self.keypair.clone().unwrap()
+	}
+	pub fn idty_index(&self) -> u32 {
+		self.idty_index.clone().unwrap()
+	}
+	// --- mutators ---
+	/// force an address if needed
+	pub fn build_address(mut self) -> Self {
+		self.address = Some(
+			get_keys(
+				self.args.secret_format,
+				&self.args.address,
+				&self.args.secret,
+				NeededKeys::Public,
+			)
+			.expect("needed")
+			.0
+			.expect("needed"),
+		);
+		self
+	}
+	/// force a keypair if needed
+	pub fn build_keypair(mut self) -> Self {
+		let (address, keypair) = get_keys(
+			self.args.secret_format,
+			&self.args.address,
+			&self.args.secret,
+			NeededKeys::Secret,
+		)
+		.expect("needed");
+		self.address = address;
+		self.keypair = keypair;
+		self
+	}
+	/// build a client from url
+	// TODO get client from a pre-defined list
+	pub async fn build_client(mut self) -> Self {
+		self.client = Some(Client::from_url(&self.args.url).await.expect("needed"));
+		self
+	}
+	/// get issuer index
+	/// needs address and client first
+	pub async fn fetch_idty_index(mut self) -> Result<Self, anyhow::Error> {
+		self.idty_index = Some(
+			commands::identity::get_idty_index_by_account_id(
+				self.client().clone(),
+				&self.address(),
+			)
+			.await?
+			.ok_or(anyhow::anyhow!("needs to be member to use this command"))?,
+		);
+		Ok(self)
+	}
+}
+
+/// track progress of transaction on the network
+/// until it is in block with success or failure
+pub async fn track_progress(progress: TxProgress) -> anyhow::Result<()> {
+	println!("submitted transaction to network, waiting 6 seconds...");
+	// wait for in block
+	let tx = progress.wait_for_in_block().await?;
+	// print result
+	println!("{:?}", tx.wait_for_success().await?);
+	// return empty
+	Ok(())
+}
+
+/// custom error type intended to provide more convenient error message to user
+#[derive(Debug)]
+pub enum GcliError {
+	/// error coming from subxt
+	Subxt(subxt::Error),
+	/// error coming from anyhow
+	Anyhow(anyhow::Error),
+}
+impl std::fmt::Display for GcliError {
+	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+		write!(f, "{:?}", self)
+	}
+}
+impl std::error::Error for GcliError {}
+impl From<subxt::Error> for GcliError {
+	fn from(e: subxt::Error) -> GcliError {
+		GcliError::Subxt(e)
+	}
+}
+impl From<anyhow::Error> for GcliError {
+	fn from(e: anyhow::Error) -> GcliError {
+		GcliError::Anyhow(e)
+	}
+}
+
+#[derive(Clone, Debug, clap::Subcommand, Default)]
 pub enum Subcommand {
-	/// Confirm an identity
-	///
-	/// To be called by the certified not-yet-member account, to become member.
-	ConfirmIdentity {
-		name: String,
-	},
+	// TODO flodef
+	#[default]
+	GetBalance,
 	/// Create and certify an identity
 	///
 	/// Caller must be member, and the target account must exist.
 	CreateIdentity {
 		target: sp_core::crypto::AccountId32,
 	},
+	/// Confirm an identity
+	///
+	/// To be called by the certified not-yet-member account, to become member.
+	ConfirmIdentity {
+		name: String,
+	},
 	CreateOneshot {
 		balance: u64,
 		dest: sp_core::crypto::AccountId32,
@@ -189,54 +316,25 @@ pub enum Subcommand {
 }
 
 #[tokio::main(flavor = "current_thread")]
-async fn main() -> Result<()> {
+async fn main() -> Result<(), GcliError> {
 	env_logger::init();
 
 	let args = Args::parse();
-
-	/*if let Some(account_id) = &account_id {
-		println!("Account address: {account_id}");
-	}*/
-
-	/*if let Some(account_id) = &account_id {
-		let account = client
-			.storage()
-			.fetch(&gdev::storage().system().account(account_id), None)
-			.await?
-			.expect("Cannot fetch account");
-		logs::info!("Account free balance: {}", account.data.free);
-	}*/
+	let mut data = Data::new(args.clone());
 
 	match args.subcommand {
-		Subcommand::ConfirmIdentity { name } => {
-			commands::identity::confirm_identity(
-				get_keys(
-					args.secret_format,
-					&args.address,
-					&args.secret,
-					NeededKeys::Secret,
-				)?
-				.1
-				.unwrap(),
-				Client::from_url(&args.url).await.unwrap(),
-				name,
-			)
-			.await?
-		}
+		Subcommand::GetBalance => {}
 		Subcommand::CreateIdentity { target } => {
-			commands::identity::create_identity(
-				get_keys(
-					args.secret_format,
-					&args.address,
-					&args.secret,
-					NeededKeys::Secret,
-				)?
-				.1
-				.unwrap(),
-				Client::from_url(&args.url).await.unwrap(),
-				target,
-			)
-			.await?
+			data = data.build_client().await.build_keypair();
+			let progress =
+				commands::identity::create_identity(data.keypair(), data.client(), target).await?;
+			track_progress(progress).await?
+		}
+		Subcommand::ConfirmIdentity { name } => {
+			data = data.build_client().await.build_keypair();
+			let progress =
+				commands::identity::confirm_identity(data.keypair(), data.client(), name).await?;
+			track_progress(progress).await?
 		}
 		Subcommand::CreateOneshot { balance, dest } => {
 			commands::oneshot::create_oneshot_account(
@@ -419,7 +517,15 @@ async fn main() -> Result<()> {
 			)
 			.await?
 		}
-		Subcommand::SmithCert { to } => commands::smith::emit_cert(args, to).await?,
+		Subcommand::SmithCert { to } => {
+			data = data
+				.build_client()
+				.await
+				.build_keypair()
+				.fetch_idty_index()
+				.await?;
+			commands::smith::cert(data.client(), data.keypair(), data.idty_index(), to).await?
+		}
 		Subcommand::TechMembers => {
 			commands::collective::technical_committee_members(
 				Client::from_url(&args.url).await.unwrap(),
-- 
GitLab