diff --git a/README.md b/README.md
index 8b082058984c748a53d2f7ba913d003697475f1a..38eb3a801b0c9dcd4f4974323548c194ff36c793 100644
--- a/README.md
+++ b/README.md
@@ -66,3 +66,11 @@ Secret and/or public keys can always be passed using `--secret` and `--address`.
 Secret key format can be changed using `--secret-format` with the following values:
 * `substrate`: a Substrate secret address (optionally followed by a derivation path), or BIP39 mnemonic
 * `seed`: a 32-bytes seed in hexadecimal (Duniter v1 compatible)
+
+## TODO
+
+- [x] implement config formatter
+- [x] add link/unlink account commands
+- [ ] migrate all xt to submit_call_and_look_event
+- [ ] add transfer with unit (ÄžD, UD...)
+- [ ] 
\ No newline at end of file
diff --git a/doc/config.md b/doc/config.md
new file mode 100644
index 0000000000000000000000000000000000000000..541c0a0cc96e0fecea4c18011c779628942f9425
--- /dev/null
+++ b/doc/config.md
@@ -0,0 +1,27 @@
+# Äžcli config 
+
+Some Äžcli commands require to have an address configured (for example to get account balance), some require to have a secret configured (to sign extrinsics).
+Äžcli allows to save what you want in a config file and to overwrite parts in command line arguments. Example:
+
+```sh
+# save Alice secret to config file
+cargo run -- -S predefined -s Alice config save
+
+# show config 
+cargo run -- config show
+# [stdout]
+# Äžcli config
+# duniter endpoint ws://localhost:9944
+# indexer endpoint http://localhost:8080/v1/graphql
+# address 5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY (secret defined)
+
+# use different address in command line
+cargo run -- --address 5Fxune7f71ZbpP2FoY3mhYcmM596Erhv1gRue4nsPwkxMR4n config show
+# [stdout]
+# Äžcli config
+# duniter endpoint ws://localhost:9944
+# indexer endpoint http://localhost:8080/v1/graphql
+# address 5Fxune7f71ZbpP2FoY3mhYcmM596Erhv1gRue4nsPwkxMR4n (no secret)
+```
+
+You can see that if a secret is defined, the associated address is used, but if an other address is given, the secret is silenced.
\ No newline at end of file
diff --git a/doc/example.md b/doc/example.md
index 51cf095ef866f209daa2d00a3164e4d69678f545..7d3ea4515e4e8d76037cbca39f9763e5bbab7a54 100644
--- a/doc/example.md
+++ b/doc/example.md
@@ -20,7 +20,7 @@ gcli -S predefined -s Alice config save
 gcli --network local -S predefined -s test1 config save
 ```
 
-In the following, we assume this last command was run.
+In the following, we assume this last command was run. More about the config in [config.md](./config.md).
 
 ## Commands
 
diff --git a/src/commands/account.rs b/src/commands/account.rs
index dc86d706acaceeaa13d5121f537e6e4c2485ca5b..3d8844d96a2421dd31ef5038d3c3b26840bcb951 100644
--- a/src/commands/account.rs
+++ b/src/commands/account.rs
@@ -27,6 +27,8 @@ pub enum Subcommand {
 		/// List of target addresses
 		dests: Vec<AccountId>,
 	},
+	/// Unlink the account from the linked identity
+	Unlink,
 }
 
 /// handle account commands
@@ -48,6 +50,9 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
 		Subcommand::TransferMultiple { amount, dests } => {
 			commands::transfer::transfer_multiple(&data, amount, dests).await?;
 		}
+		Subcommand::Unlink => {
+			unlink_account(&data).await?;
+		}
 	};
 
 	Ok(())
@@ -78,3 +83,12 @@ pub async fn get_account_info(
 		.fetch(&runtime::storage().system().account(account_id), None)
 		.await
 }
+
+/// unlink account
+pub async fn unlink_account(data: &Data) -> Result<(), subxt::Error> {
+	submit_call_and_look_event::<
+		runtime::account::events::AccountUnlinked,
+		StaticTxPayload<runtime::account::calls::UnlinkIdentity>,
+	>(data, &runtime::tx().account().unlink_identity())
+	.await
+}
diff --git a/src/commands/certification.rs b/src/commands/certification.rs
index 69a2f11eb4f81d2e21471497d0e3ae47b2b3a17b..366b85321933feff3a61bc7c552476eb5611712d 100644
--- a/src/commands/certification.rs
+++ b/src/commands/certification.rs
@@ -2,31 +2,17 @@ use crate::*;
 
 /// submit a certification and track progress
 pub async fn certify(data: &Data, receiver: u32) -> Result<(), anyhow::Error> {
-	let progress = data
-		.client()
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx().cert().add_cert(data.idty_index(), receiver),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
-
+	let progress = submit_call(
+		data,
+		&runtime::tx().cert().add_cert(data.idty_index(), receiver),
+	)
+	.await?;
 	if data.args.no_wait {
 		return Ok(());
 	}
 	let events = track_progress(progress).await?;
-
 	// look for the expected event
-	let new_cert_event = events.find_first::<runtime::cert::events::NewCert>()?;
-	let renew_cert_event = events.find_first::<runtime::cert::events::RenewedCert>()?;
-
-	if let Some(event) = new_cert_event {
-		println!("{event:?}");
-	}
-	if let Some(event) = renew_cert_event {
-		println!("{event:?}");
-	}
-
+	look_event::<runtime::cert::events::NewCert>(&events)?;
+	look_event::<runtime::cert::events::RenewedCert>(&events)?;
 	Ok(())
 }
diff --git a/src/commands/collective.rs b/src/commands/collective.rs
index 5ca295e7958ec04e772c54edf256bd6d8081e6a9..cc7f10e5819506004c769bc56bacf3c83c67aa86 100644
--- a/src/commands/collective.rs
+++ b/src/commands/collective.rs
@@ -135,26 +135,14 @@ pub async fn technical_committee_vote(
 	proposal_index: u32,
 	vote: bool,
 ) -> Result<(), subxt::Error> {
-	let progress = data
-		.client()
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx()
-				.technical_committee()
-				.vote(proposal_hash, proposal_index, vote),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
-
-	if data.args.no_wait {
-		return Ok(());
-	}
-	let events = track_progress(progress).await?;
-
-	if let Some(e) = events.find_first::<runtime::technical_committee::events::Voted>()? {
-		println!("{e:?}");
-	}
-
-	Ok(())
+	submit_call_and_look_event::<
+		runtime::technical_committee::events::Voted,
+		StaticTxPayload<runtime::technical_committee::calls::Vote>,
+	>(
+		data,
+		&runtime::tx()
+			.technical_committee()
+			.vote(proposal_hash, proposal_index, vote),
+	)
+	.await
 }
diff --git a/src/commands/identity.rs b/src/commands/identity.rs
index 02f75fbbd0e34379a218f89a6e2e266b8b30e89e..5543854cac331ce31bf47a86be8317b8e90faca0 100644
--- a/src/commands/identity.rs
+++ b/src/commands/identity.rs
@@ -3,7 +3,9 @@ use crate::*;
 use crate::{
 	commands::revocation::generate_revoc_doc,
 	runtime::runtime_types::{
-		common_runtime::entities::IdtyData, pallet_identity::types::*, sp_core::sr25519::Signature,
+		common_runtime::entities::{IdtyData, NewOwnerKeySignature},
+		pallet_identity::types::*,
+		sp_core::sr25519::Signature,
 		sp_runtime::MultiSignature,
 	},
 };
@@ -32,6 +34,12 @@ pub enum Subcommand {
 	///
 	/// To be called by the certified not-yet-member account, to become member.
 	Confirm { name: String },
+	/// Validate an identity
+	/// Should be called when the distance has been evaluated positively
+	Validate { index: u32 },
+	/// Renew membership  
+	/// When membership comes to and end, it should be renewed for the identity to stay member
+	RenewMembership,
 	/// Certify an identity
 	Certify { target: u32 },
 	/// Revoke an identity immediately
@@ -40,6 +48,11 @@ pub enum Subcommand {
 	GenRevocDoc,
 	/// Display member count
 	MemberCount,
+	/// Link an account to the identity
+	LinkAccount {
+		/// address of the account that has to be linked
+		address: AccountId,
+	},
 }
 
 /// handle identity commands
@@ -61,6 +74,12 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
 		Subcommand::Confirm { name } => {
 			confirm_identity(&data, name).await?;
 		}
+		Subcommand::Validate { index } => {
+			validate_identity(&data, index).await?;
+		}
+		Subcommand::RenewMembership => {
+			renew_membership(&data).await?;
+		}
 		Subcommand::Certify { target } => {
 			data = data.fetch_idty_index().await?;
 			// TODO fetch target username / key / index
@@ -69,7 +88,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
 		}
 		Subcommand::Revoke => {
 			data = data.fetch_idty_index().await?;
-			revoke_identity(data).await?;
+			revoke_identity(&data).await?;
 		}
 		Subcommand::GenRevocDoc => {
 			data = data.fetch_idty_index().await?;
@@ -88,6 +107,10 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
 					.unwrap()
 			)
 		}
+		Subcommand::LinkAccount { address } => {
+			data = data.fetch_idty_index().await?; // idty index required for payload
+			link_account(&data, address).await?;
+		}
 	};
 
 	Ok(())
@@ -186,50 +209,42 @@ pub async fn get_identity_by_index(
 
 /// created identity
 pub async fn create_identity(data: &Data, target: AccountId) -> Result<(), subxt::Error> {
-	let progress = data
-		.client()
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx().identity().create_identity(target),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
-
-	if data.args.no_wait {
-		return Ok(());
-	}
-	let events = track_progress(progress).await?;
-	if let Some(e) = events.find_first::<runtime::identity::events::IdtyCreated>()? {
-		println!("{e:?}");
-	}
-	Ok(())
+	submit_call_and_look_event::<
+		runtime::identity::events::IdtyCreated,
+		StaticTxPayload<runtime::identity::calls::CreateIdentity>,
+	>(data, &runtime::tx().identity().create_identity(target))
+	.await
 }
 
 /// confirm identity
 pub async fn confirm_identity(data: &Data, name: String) -> Result<(), subxt::Error> {
-	let progress = data
-		.client()
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx().identity().confirm_identity(name),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
+	submit_call_and_look_event::<
+		runtime::identity::events::IdtyConfirmed,
+		StaticTxPayload<runtime::identity::calls::ConfirmIdentity>,
+	>(data, &runtime::tx().identity().confirm_identity(name))
+	.await
+}
 
-	if data.args.no_wait {
-		return Ok(());
-	}
-	let events = track_progress(progress).await?;
-	if let Some(e) = events.find_first::<runtime::identity::events::IdtyConfirmed>()? {
-		println!("{e:?}");
-	}
-	Ok(())
+/// confirm identity
+pub async fn validate_identity(data: &Data, index: u32) -> Result<(), subxt::Error> {
+	submit_call_and_look_event::<
+		runtime::identity::events::IdtyValidated,
+		StaticTxPayload<runtime::identity::calls::ValidateIdentity>,
+	>(data, &runtime::tx().identity().validate_identity(index))
+	.await
+}
+
+/// renew membership
+pub async fn renew_membership(data: &Data) -> Result<(), subxt::Error> {
+	submit_call_and_look_event::<
+		runtime::membership::events::MembershipRenewed,
+		StaticTxPayload<runtime::membership::calls::RenewMembership>,
+	>(data, &runtime::tx().membership().renew_membership())
+	.await
 }
 
 /// generate revokation document and submit it immediately
-pub async fn revoke_identity(data: Data) -> Result<(), subxt::Error> {
+pub async fn revoke_identity(data: &Data) -> Result<(), subxt::Error> {
 	let (_payload, signature) = generate_revoc_doc(&data);
 
 	// Transform signature to MultiSignature
@@ -237,24 +252,45 @@ pub async fn revoke_identity(data: Data) -> Result<(), subxt::Error> {
 	let signature = Signature(signature.0);
 	let multisign = MultiSignature::Sr25519(signature);
 
-	let progress = data
-		.client()
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx()
-				.identity()
-				.revoke_identity(data.idty_index(), data.address(), multisign),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
+	submit_call_and_look_event::<
+		runtime::identity::events::IdtyRemoved,
+		StaticTxPayload<runtime::identity::calls::RevokeIdentity>,
+	>(
+		data,
+		&runtime::tx()
+			.identity()
+			.revoke_identity(data.idty_index(), data.address(), multisign),
+	)
+	.await
+}
 
-	if data.args.no_wait {
-		return Ok(());
-	}
-	let events = track_progress(progress).await?;
-	if let Some(e) = events.find_first::<runtime::identity::events::IdtyRemoved>()? {
-		println!("{e:?}");
-	}
-	Ok(())
+type LinkAccountPayload = Vec<u8>;
+/// generate link account document
+pub fn generate_link_account(
+	data: &Data,
+	address: AccountId,
+) -> (LinkAccountPayload, sp_core::sr25519::Signature) {
+	let payload = (b"link", data.genesis_hash, data.idty_index(), address).encode();
+	let signature = data.keypair().sign(&payload);
+	(payload, signature)
+}
+
+/// link an account to the identity
+pub async fn link_account(data: &Data, address: AccountId) -> Result<(), subxt::Error> {
+	let (_payload, signature) = generate_link_account(data, address.clone());
+
+	// this is a hack, see
+	// https://substrate.stackexchange.com/questions/10309/how-to-use-core-crypto-types-instead-of-runtime-types
+	let signature = Signature(signature.0);
+
+	submit_call_and_look_event::<
+		runtime::account::events::AccountLinked,
+		StaticTxPayload<runtime::identity::calls::LinkAccount>,
+	>(
+		data,
+		&runtime::tx()
+			.identity()
+			.link_account(address, NewOwnerKeySignature(signature)),
+	)
+	.await
 }
diff --git a/src/commands/oneshot.rs b/src/commands/oneshot.rs
index a395f78bd3a006f810f5e607e323ec2948cc1c58..c4cf3c362941e21b0aea0b462bdde9b03cd5e8dc 100644
--- a/src/commands/oneshot.rs
+++ b/src/commands/oneshot.rs
@@ -176,46 +176,31 @@ pub async fn consume_oneshot_account_with_remaining(
 		.fetch(&runtime::storage().system().number(), None)
 		.await?
 		.unwrap();
-	let progress = client
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx()
-				.oneshot_account()
-				.consume_oneshot_account_with_remaining(
-					number,
-					if dest_oneshot {
-						runtime::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
-							dest.into(),
-						)
-					} else {
-						runtime::runtime_types::pallet_oneshot_account::types::Account::Normal(
-							dest.into(),
-						)
-					},
-					if remaining_to_oneshot {
-						runtime::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
-							remaining_to.into(),
-						)
-					} else {
-						runtime::runtime_types::pallet_oneshot_account::types::Account::Normal(
-							remaining_to.into(),
-						)
-					},
-					balance,
-				),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
 
-	if data.args.no_wait {
-		return Ok(());
-	}
-	let events = track_progress(progress).await?;
-	if let Some(e) =
-		events.find_first::<runtime::oneshot_account::events::OneshotAccountConsumed>()?
-	{
-		println!("{e:?}");
-	}
-	Ok(())
+	let call = &runtime::tx()
+		.oneshot_account()
+		.consume_oneshot_account_with_remaining(
+			number,
+			if dest_oneshot {
+				runtime::runtime_types::pallet_oneshot_account::types::Account::Oneshot(dest.into())
+			} else {
+				runtime::runtime_types::pallet_oneshot_account::types::Account::Normal(dest.into())
+			},
+			if remaining_to_oneshot {
+				runtime::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
+					remaining_to.into(),
+				)
+			} else {
+				runtime::runtime_types::pallet_oneshot_account::types::Account::Normal(
+					remaining_to.into(),
+				)
+			},
+			balance,
+		);
+
+	submit_call_and_look_event::<
+		runtime::oneshot_account::events::OneshotAccountConsumed,
+		StaticTxPayload<runtime::oneshot_account::calls::ConsumeOneshotAccountWithRemaining>,
+	>(data, call)
+	.await
 }
diff --git a/src/commands/smith.rs b/src/commands/smith.rs
index 8a69b7a727f52228d6749889473d12372091e85e..f7b25e21c12413cf85d7a30a7d4c783351a71f28 100644
--- a/src/commands/smith.rs
+++ b/src/commands/smith.rs
@@ -310,35 +310,21 @@ pub async fn online(data: &Data) -> Result<(), anyhow::Error> {
 	Ok(())
 }
 
-/// submit a certification and track progress
+/// submit a smith certification and track progress
 pub async fn cert(data: &Data, receiver: u32) -> Result<(), anyhow::Error> {
-	let progress = data
-		.client()
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx()
-				.smith_cert()
-				.add_cert(data.idty_index(), receiver),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
-
+	let progress = submit_call(
+		data,
+		&runtime::tx()
+			.smith_cert()
+			.add_cert(data.idty_index(), receiver),
+	)
+	.await?;
 	if data.args.no_wait {
 		return Ok(());
 	}
 	let events = track_progress(progress).await?;
-
 	// look for the expected event
-	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:?}");
-	}
-	if let Some(event) = renew_cert_event {
-		println!("{event:?}");
-	}
-
+	look_event::<runtime::smith_cert::events::NewCert>(&events)?;
+	look_event::<runtime::smith_cert::events::RenewedCert>(&events)?;
 	Ok(())
 }
diff --git a/src/commands/sudo.rs b/src/commands/sudo.rs
index 9e5d56cf5ea18e3a38d641889ff4b372ce058116..7b7b5053687eb01b839ce08b5535a2ad0969e028 100644
--- a/src/commands/sudo.rs
+++ b/src/commands/sudo.rs
@@ -2,19 +2,9 @@ use crate::*;
 
 /// set sudo key
 pub async fn set_key(data: &Data, new_key: AccountId) -> Result<(), subxt::Error> {
-	let progress = data
-		.client()
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx().sudo().set_key(new_key.into()),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
-
-	if data.args.no_wait {
-		return Ok(());
-	}
-	let _ = track_progress(progress).await?; // TODO
-	Ok(())
+	submit_call_and_look_event::<
+		runtime::sudo::events::KeyChanged,
+		StaticTxPayload<runtime::sudo::calls::SetKey>,
+	>(data, &runtime::tx().sudo().set_key(new_key.into()))
+	.await
 }
diff --git a/src/commands/transfer.rs b/src/commands/transfer.rs
index c49512b89dc80b8acab25c1fa10a649fb7e57b87..555c05e7b428b76f75a848bbd153fe7887023feb 100644
--- a/src/commands/transfer.rs
+++ b/src/commands/transfer.rs
@@ -78,25 +78,10 @@ pub async fn transfer_multiple(
 			})
 		})
 		.collect();
-
 	// wrap these calls in a batch call
-	let progress = data
-		.client()
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx().utility().batch(transactions),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
-
-	if data.args.no_wait {
-		return Ok(());
-	}
-	let events = track_progress(progress).await?;
-	// TODO all transfer
-	if let Some(e) = events.find_first::<runtime::balances::events::Transfer>()? {
-		println!("{e:?}");
-	}
-	Ok(())
+	submit_call_and_look_event::<
+		runtime::utility::events::BatchCompleted,
+		StaticTxPayload<runtime::utility::calls::Batch>,
+	>(data, &runtime::tx().utility().batch(transactions))
+	.await
 }
diff --git a/src/commands/ud.rs b/src/commands/ud.rs
index bda66f03909c87cde64ae697a7da193ff74432c4..28d7ef4e13826a9d2c1f61a3479b2c0ee9ad8438 100644
--- a/src/commands/ud.rs
+++ b/src/commands/ud.rs
@@ -15,7 +15,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
 	// match subcommand
 	match command {
 		Subcommand::Claim => {
-			claim_ud(data).await?;
+			claim_ud(&data).await?;
 		}
 	};
 
@@ -23,24 +23,10 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<(
 }
 
 /// claim universal dividend
-pub async fn claim_ud(data: Data) -> Result<(), anyhow::Error> {
-	let progress = data
-		.client()
-		.tx()
-		.sign_and_submit_then_watch(
-			&runtime::tx().universal_dividend().claim_uds(),
-			&PairSigner::new(data.keypair()),
-			BaseExtrinsicParamsBuilder::new(),
-		)
-		.await?;
-
-	if data.args.no_wait {
-		return Ok(());
-	}
-	let events = track_progress(progress).await?;
-
-	if let Some(e) = events.find_first::<runtime::universal_dividend::events::UdsClaimed>()? {
-		println!("{e:?}");
-	}
-	Ok(())
+pub async fn claim_ud(data: &Data) -> Result<(), subxt::Error> {
+	submit_call_and_look_event::<
+		runtime::universal_dividend::events::UdsClaimed,
+		StaticTxPayload<runtime::universal_dividend::calls::ClaimUds>,
+	>(data, &runtime::tx().universal_dividend().claim_uds())
+	.await
 }
diff --git a/src/conf.rs b/src/conf.rs
index 5b7976bdca743ac066a777139845350e307ef223..9801172134129417509ae5b240e2f87421f87da4 100644
--- a/src/conf.rs
+++ b/src/conf.rs
@@ -27,6 +27,25 @@ impl std::default::Default for Config {
 	}
 }
 
+impl std::fmt::Display for Config {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		let secret = if self.secret.is_some() {
+			"(secret defined)"
+		} else {
+			"(no secret)"
+		};
+		let address = if let Some(address) = &self.address {
+			format!("{}", address)
+		} else {
+			"(no address)".to_string()
+		};
+		writeln!(f, "Äžcli config")?;
+		writeln!(f, "duniter endpoint {}", self.duniter_endpoint)?;
+		writeln!(f, "indexer endpoint {}", self.indexer_endpoint)?;
+		write!(f, "address {address} {secret}")
+	}
+}
+
 /// load config file and manage error if could not
 pub fn load_conf() -> Config {
 	match confy::load(APP_NAME, None) {
@@ -68,7 +87,7 @@ pub fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<()> {
 			);
 		}
 		Subcommand::Show => {
-			println!("{:?}", data.cfg);
+			println!("{}", data.cfg);
 		}
 		Subcommand::Save => {
 			confy::store(APP_NAME, None, &data.cfg).expect("unable to write config");
diff --git a/src/main.rs b/src/main.rs
index e4c0b008508f7c00651d44bf841772ccc9bd8b9b..1c90aacc3cbf9037f90b0a032c2d2d89ffe1f39f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -92,7 +92,7 @@ pub enum Subcommand {
 	Config(conf::Subcommand),
 }
 
-/// maint function
+/// main function
 #[tokio::main(flavor = "current_thread")]
 async fn main() -> Result<(), GcliError> {
 	// init logger
diff --git a/src/runtime_config.rs b/src/runtime_config.rs
index d62c5e1172f96d69ab3eed69296462a8a4d0ff31..509c78551328ac90d72f713fe39f868bbfeb096b 100644
--- a/src/runtime_config.rs
+++ b/src/runtime_config.rs
@@ -1,3 +1,4 @@
+// #[allow(clippy::enum_variant_names)]
 #[cfg(feature = "gdev")]
 #[subxt::subxt(
 	runtime_metadata_path = "res/metadata.scale",
@@ -5,8 +6,8 @@
 )]
 pub mod runtime {
 	// IF NEEDED
-	// #[subxt(substitute_type = "spcore::sr25519::Signature")]
-	// use crate::gdev::runtime_types::sp_core::sr25519::Signature;
+	// #[subxt(substitute_type = "sp_core::sr25519::Signature")]
+	// use crate::runtime::runtime_types::sp_core::sr25519::Signature;
 }
 
 // declare custom types
diff --git a/src/utils.rs b/src/utils.rs
index 4bfc80149e1281623d44c5f0c255c2fbd09cd89f..d3077f78708fde761f038ac5b6bcb05613dcf235 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -28,24 +28,40 @@ pub async fn submit_call_and_look_event<E: std::fmt::Debug + StaticEvent, Call:
 	data: &Data,
 	call: &Call,
 ) -> Result<(), subxt::Error> {
-	let progress = data
-		.client()
+	// submit call
+	let progress = submit_call(data, call).await?;
+	// if no wait, return immediately
+	if data.args.no_wait {
+		return Ok(());
+	}
+	// collect events
+	let events = track_progress(progress).await?;
+	// print given event if there
+	look_event::<E>(&events)
+}
+
+/// submit call
+pub async fn submit_call<Call: TxPayload>(
+	data: &Data,
+	call: &Call,
+) -> Result<TxProgress, subxt::Error> {
+	data.client()
 		.tx()
 		.sign_and_submit_then_watch(
 			call,
 			&PairSigner::new(data.keypair()),
 			BaseExtrinsicParamsBuilder::new(),
 		)
-		.await?;
+		.await
+}
 
-	if data.args.no_wait {
-		return Ok(());
-	}
-	let events = track_progress(progress).await?;
+/// look event
+pub fn look_event<E: std::fmt::Debug + StaticEvent>(
+	events: &ExtrinsicEvents<Runtime>,
+) -> Result<(), subxt::Error> {
 	if let Some(e) = events.find_first::<E>()? {
 		println!("{e:?}");
 	}
-
 	Ok(())
 }