diff --git a/Cargo.lock b/Cargo.lock
index 03dd29be194292551798e936a2eddb312e1d4907..52b2ad57fd20b1aa63b34d06ce1bfeee6dd7d4f1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -855,7 +855,7 @@ dependencies = [
  "futures",
  "graphql_client",
  "hex",
- "logs",
+ "log",
  "parity-scale-codec",
  "reqwest",
  "rpassword",
@@ -1509,16 +1509,6 @@ dependencies = [
  "cfg-if",
 ]
 
-[[package]]
-name = "logs"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "218ee85e6f2ed7e15253cbfcb61bf960ea797b4d8869d36f1a95bbc7fdc3686d"
-dependencies = [
- "log",
- "time",
-]
-
 [[package]]
 name = "lru"
 version = "0.7.8"
@@ -3105,33 +3095,6 @@ dependencies = [
  "once_cell",
 ]
 
-[[package]]
-name = "time"
-version = "0.3.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890"
-dependencies = [
- "itoa",
- "serde",
- "time-core",
- "time-macros",
-]
-
-[[package]]
-name = "time-core"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
-
-[[package]]
-name = "time-macros"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36"
-dependencies = [
- "time-core",
-]
-
 [[package]]
 name = "tiny-bip39"
 version = "0.8.2"
diff --git a/Cargo.toml b/Cargo.toml
index e88715bd18683a78811bc4ae1ed413d31cca4bc7..fce1a3e18202fa60a0bc506af615dedb9a601695 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,7 @@ env_logger = "0.10"
 futures = "0.3.27"
 graphql_client = { version = "0.12.0", features = ["reqwest"] }
 hex = "0.4.3"
-logs = "0.7"
+log = "0.4.17"
 reqwest = "0.11.14"
 rpassword = "7.2.0"
 serde = { version = "1.0", features = ["derive"] }
diff --git a/README.md b/README.md
index e04dce2e703ae122ac6c11f6cdb79f7fce1d920c..0f97ffbfc17acfd1e540c4dd431b368a2ae0b1dd 100644
--- a/README.md
+++ b/README.md
@@ -20,6 +20,22 @@ List certifications and session keys that will expire within one month:
 
 	cargo run -- --url wss://gdev.p2p.legal:443/ws expire --blocks 432000
 
+#### Log level
+
+You can adjust the log level:
+
+```
+export RUST_LOG=gcli=info
+```
+
+#### Runtime metadata
+
+To update runtime metadata:
+
+```
+subxt metadata -f bytes > res/metadata.scale
+```
+
 ### Smith
 
 You want to rotate keys and go online to start forging blocks.
diff --git a/res/metadata.scale b/res/metadata.scale
index d7c41820b07a0a2e6198009041d239e70602a2d9..3af1b14cabe3e2c9b3561e0ef21320d47923125d 100644
Binary files a/res/metadata.scale and b/res/metadata.scale differ
diff --git a/src/commands/expire.rs b/src/commands/expire.rs
index d051ea80f2719ad208d4476f38ee8a3df72fa37c..a23a5300509ee3e0f4e249352e8d7f25466af9f8 100644
--- a/src/commands/expire.rs
+++ b/src/commands/expire.rs
@@ -101,7 +101,7 @@ pub async fn monitor_expirations(
 	let mut smith_certs_iter = client
 		.storage()
 		.iter(
-			gdev::storage().smiths_cert().storage_certs_removable_on(0),
+			gdev::storage().smith_cert().storage_certs_removable_on(0),
 			10,
 			Some(parent_hash),
 		)
@@ -162,7 +162,7 @@ pub async fn monitor_expirations(
 	let mut smith_membership_iter = client
 		.storage()
 		.iter(
-			gdev::storage().smiths_membership().memberships_expire_on(0),
+			gdev::storage().smith_membership().memberships_expire_on(0),
 			10,
 			Some(parent_hash),
 		)
diff --git a/src/commands/identity.rs b/src/commands/identity.rs
index 6cde145611f6cca3fa09fea638539055f4b6fbc0..e5ee3ca056d4cdb710997b37dad424012482e47e 100644
--- a/src/commands/identity.rs
+++ b/src/commands/identity.rs
@@ -1,5 +1,7 @@
 use crate::{gdev, indexer::*, Args, Client};
 
+use crate::gdev::runtime_types::common_runtime::entities::IdtyData;
+use crate::gdev::runtime_types::pallet_identity::types::*;
 use anyhow::{anyhow, Result};
 use sp_core::{crypto::AccountId32, sr25519::Pair};
 use std::str::FromStr;
@@ -12,63 +14,50 @@ pub async fn get_identity(
 	mut username: Option<String>,
 	args: &Args,
 ) -> Result<()> {
-	let parent_hash = client
-		.storage()
-		.fetch(&gdev::storage().system().parent_hash(), None)
-		.await?
-		.unwrap();
-
-	let gql_client = reqwest::Client::builder()
-		.user_agent("gcli/0.1.0")
-		.build()?;
-
+	// build indexer if not disabled
 	let indexer = if args.no_indexer {
 		None
 	} else {
 		Some(Indexer {
-			gql_client,
+			gql_client: reqwest::Client::builder()
+				.user_agent("gcli/0.1.0")
+				.build()?,
 			gql_url: &args.indexer,
 		})
 	};
 
-	if let Some(account_id) = &account_id {
-		identity_id = client
-			.storage()
-			.fetch(
-				&gdev::storage().identity().identity_index_of(account_id),
-				Some(parent_hash),
-			)
-			.await?;
-	} else if let Some(identity_id) = &identity_id {
-		account_id = client
-			.storage()
-			.fetch(
-				&gdev::storage().identity().identities(identity_id),
-				Some(parent_hash),
-			)
-			.await?
-			.map(|idty| idty.owner_key);
-	} else if let Some(username) = &username {
-		let indexer = indexer.as_ref().ok_or(anyhow!(
-			"Cannot fetch identity from username without indexer."
-		))?;
-		if let Some(pubkey) = indexer.pubkey_by_username(username).await? {
-			let some_account_id = AccountId32::from_str(&pubkey).map_err(|e| anyhow!(e))?;
-			identity_id = client
-				.storage()
-				.fetch(
-					&gdev::storage()
-						.identity()
-						.identity_index_of(&some_account_id),
-					Some(parent_hash),
-				)
-				.await?;
-			account_id = Some(some_account_id);
+	// fetch missing information
+	match (&account_id, identity_id, &username) {
+		(None, Some(identity_id), None) => {
+			account_id = get_identity_by_index(client, identity_id)
+				.await?
+				.map(|idty| idty.owner_key);
 		}
-	} else {
-		return Err(anyhow!("One argument is needed to fetch the identity."));
-	}
+		(Some(account_id), None, None) => {
+			identity_id = get_idty_index_by_account_id(client, account_id).await?;
+		}
+		(None, None, Some(username)) => {
+			let indexer = indexer.as_ref().ok_or(anyhow!(
+				"Cannot fetch identity from username without indexer."
+			))?;
+			if let Some(pubkey) = indexer.pubkey_by_username(username).await? {
+				// convert string to accountid
+				let fetched_account_id = AccountId32::from_str(&pubkey).map_err(|e| anyhow!(e))?;
+				// in the future, also ask indexer the identity index
+				identity_id = get_idty_index_by_account_id(client, &fetched_account_id).await?;
+				account_id = Some(fetched_account_id);
+			} else {
+				return Err(anyhow!("no identity found for this username"));
+			}
+		}
+		_ => {
+			return Err(anyhow!(
+				"One and only one argument is needed to fetch the identity."
+			));
+		}
+	};
 
+	// print result
 	println!(
 		"Account id:  {}",
 		account_id
@@ -83,12 +72,34 @@ pub async fn get_identity(
 	if let (Some(indexer), Some(account_id), None) = (&indexer, &account_id, &username) {
 		username = indexer.username_by_pubkey(&account_id.to_string()).await?;
 	}
-
 	println!("Username:    {}", username.unwrap_or_default());
 
 	Ok(())
 }
 
+pub async fn get_idty_index_by_account_id(
+	client: Client,
+	account_id: &AccountId32,
+) -> Result<Option<u32>> {
+	Ok(client
+		.storage()
+		.fetch(
+			&gdev::storage().identity().identity_index_of(account_id),
+			None,
+		)
+		.await?)
+}
+
+pub async fn get_identity_by_index(
+	client: Client,
+	idty_index: u32,
+) -> Result<Option<IdtyValue<u32, AccountId32, IdtyData>>> {
+	Ok(client
+		.storage()
+		.fetch(&gdev::storage().identity().identities(idty_index), None)
+		.await?)
+}
+
 pub async fn create_identity(pair: Pair, client: Client, target: AccountId32) -> Result<()> {
 	client
 		.tx()
diff --git a/src/commands/net_test.rs b/src/commands/net_test.rs
index 335e0d6374bae3d1bbe0603c72181c3aae4c42dd..ab2dff3f711eb57379da6963d860dd79ca94cff6 100644
--- a/src/commands/net_test.rs
+++ b/src/commands/net_test.rs
@@ -41,7 +41,7 @@ pub async fn repart(
 			)
 			.await?
 		{
-			logs::info!("account //{} balance: {}", i, pair_i_account.data.free);
+			log::info!("account //{} balance: {}", i, pair_i_account.data.free);
 		}
 	}
 
@@ -76,7 +76,7 @@ pub async fn spam_roll(pair: Pair, client: Client, actual_repart: usize) -> Resu
 				.submit_and_watch()
 				.await?;
 			nonce += 1;
-			logs::info!("send 1 cent from //{} to //{}", i, i + 1);
+			log::info!("send 1 cent from //{} to //{}", i, i + 1);
 			watchers.push(watcher);
 		}
 		let dest: AccountId32 = pairs[0].1.clone();
@@ -89,7 +89,7 @@ pub async fn spam_roll(pair: Pair, client: Client, actual_repart: usize) -> Resu
 			)
 			.await?;
 		nonce += 1;
-		logs::info!("send 1 cent from //{} to //0", actual_repart - 1);
+		log::info!("send 1 cent from //{} to //0", actual_repart - 1);
 		watchers.push(watcher);
 
 		// Wait all transactions
diff --git a/src/commands/oneshot.rs b/src/commands/oneshot.rs
index 640cf9f6fe5073cb49305272e4db8ceeb6f84616..99336933a87ca6f26b2adc2f9ee4b1b6025d211b 100644
--- a/src/commands/oneshot.rs
+++ b/src/commands/oneshot.rs
@@ -106,7 +106,7 @@ pub async fn consume_oneshot_account_with_remaining(
 }
 
 pub async fn oneshot_account_balance(client: Client, account: AccountId32) -> Result<()> {
-	logs::info!(
+	log::info!(
 		"{}",
 		client
 			.storage()
diff --git a/src/commands/smith.rs b/src/commands/smith.rs
index 463c179a77ba0a43add4ed6bd7072cc38daa60aa..ca8928f1497415efc46a41c92f044177ee84f2cf 100644
--- a/src/commands/smith.rs
+++ b/src/commands/smith.rs
@@ -1,9 +1,10 @@
-use crate::{cache, gdev, indexer::*, Args, Client};
+use crate::indexer::Indexer;
+use crate::*;
 
 use anyhow::{anyhow, Result};
 use sp_core::{crypto::AccountId32, sr25519::Pair, Pair as _};
 use std::ops::Deref;
-use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
+use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner, TxStatus};
 
 type SessionKeys = [u8; 128];
 
@@ -162,3 +163,74 @@ 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<()> {
+	let mut progress = client
+		.tx()
+		.sign_and_submit_then_watch(
+			&gdev::tx().smith_cert().add_cert(issuer, receiver),
+			&PairSigner::new(pair),
+			BaseExtrinsicParamsBuilder::new(),
+		)
+		.await?;
+
+	let in_block = loop {
+		if let Some(status) = progress.next_item().await {
+			match status? {
+				TxStatus::Ready => {
+					println!("transaction submitted to the network, waiting 6 seconds...");
+				}
+				TxStatus::InBlock(in_block) => break in_block,
+				TxStatus::Invalid => {
+					println!("Invalid");
+				}
+				_ => continue,
+			}
+		}
+	};
+
+	// 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>()?;
+
+	if let Some(event) = new_cert_event {
+		println!("{event:?}");
+	}
+	if let Some(event) = renew_cert_event {
+		println!("{event:?}");
+	}
+
+	Ok(())
+}
diff --git a/src/commands/transfer.rs b/src/commands/transfer.rs
index 4e51cf3124cff223a6f9eab547664a7ec40c9b3b..857dbde5927b76ecbfbec225bffe5abc13acb671 100644
--- a/src/commands/transfer.rs
+++ b/src/commands/transfer.rs
@@ -4,7 +4,7 @@ use anyhow::Result;
 use sp_core::{crypto::AccountId32, sr25519::Pair};
 use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
 
-type Call = gdev::runtime_types::gdev_runtime::Call;
+type Call = gdev::runtime_types::gdev_runtime::RuntimeCall;
 type BalancesCall = gdev::runtime_types::pallet_balances::pallet::Call;
 
 pub async fn transfer(
diff --git a/src/main.rs b/src/main.rs
index 7b451af894722ccb7c8bfd79520d708e335b1374..f251d642020bb5225bdfd4eb2a389159ec868b49 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -56,11 +56,7 @@ pub struct Args {
 	pub subcommand: Subcommand,
 
 	/// Indexer URL
-	#[clap(
-		short,
-		long,
-		default_value = "https://gdev-indexer.p2p.legal/v1/graphql"
-	)]
+	#[clap(short, long, default_value = "http://localhost:8080/v1/graphql")]
 	indexer: String,
 	/// Do not use indexer
 	#[clap(long)]
@@ -154,6 +150,10 @@ pub enum Subcommand {
 	SudoSetKey {
 		new_key: sp_core::crypto::AccountId32,
 	},
+	/// Emit a smith certification
+	SmithCert {
+		to: u32,
+	},
 	/// List members of the technical committee
 	TechMembers,
 	/// List proposals to the technical committee
@@ -419,6 +419,7 @@ async fn main() -> Result<()> {
 			)
 			.await?
 		}
+		Subcommand::SmithCert { to } => commands::smith::emit_cert(args, to).await?,
 		Subcommand::TechMembers => {
 			commands::collective::technical_committee_members(
 				Client::from_url(&args.url).await.unwrap(),