diff --git a/Cargo.lock b/Cargo.lock
index 2005b13471f27ced27db06cde75c85bf65b62268..43d375be8b97d9a7d24339affec5e94f481b14d8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2353,11 +2353,11 @@ dependencies = [
  "futures",
  "graphql_client",
  "hex",
+ "indoc",
  "inquire",
  "log",
  "parity-scale-codec",
  "reqwest",
- "rpassword",
  "rstest",
  "scrypt",
  "sea-orm",
@@ -2911,6 +2911,12 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590"
 
+[[package]]
+name = "indoc"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
+
 [[package]]
 name = "inherent"
 version = "1.0.11"
@@ -4516,17 +4522,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "rpassword"
-version = "7.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f"
-dependencies = [
- "libc",
- "rtoolbox",
- "windows-sys 0.48.0",
-]
-
 [[package]]
 name = "rsa"
 version = "0.9.7"
@@ -4577,16 +4572,6 @@ dependencies = [
  "unicode-ident",
 ]
 
-[[package]]
-name = "rtoolbox"
-version = "0.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e"
-dependencies = [
- "libc",
- "windows-sys 0.48.0",
-]
-
 [[package]]
 name = "rust-embed"
 version = "8.5.0"
diff --git a/Cargo.toml b/Cargo.toml
index 48423dd41bee2516e85d8e27a96147fbc49e125b..31314799a82564928e1cbcf8fecb1e104ee31819 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,7 +37,6 @@ reqwest = { version = "^0.11.27", default-features = false, features = [
     "rustls-tls",
 ] }
 inquire = "^0.7.5"
-rpassword = "^7.3.1"
 serde = { version = "^1.0", features = ["derive"] }
 serde_json = "^1.0.128"
 tokio = { version = "^1.40.0", features = ["macros"] }
@@ -56,6 +55,7 @@ colored = "2.1.0"
 
 # Tests
 rstest = "0.23.0"
+indoc = "2.0.5"
 
 # allows to build gcli for different runtimes and with different predefined networks 
 [features]
diff --git a/doc/config.md b/doc/config.md
index 5c8d5919043c04eb92fd8f32fee9ea02b6e0a5ff..fbe59f562c861634503e3cec9054e0936ed2c972 100644
--- a/doc/config.md
+++ b/doc/config.md
@@ -36,6 +36,8 @@ gcli account transfer 1 5Fxune7f71ZbpP2FoY3mhYcmM596Erhv1gRue4nsPwkxMR4n
 # no need for password to sign transaction
 ```
 
+`FIXME` Need to update documentation when vault changes are stable.
+
 but in general usage, you want to store your secret in the local vault. This goes like this:
 
 ```sh
diff --git a/src/commands/vault.rs b/src/commands/vault.rs
index 8c8fc31dd69be7fb818815bbfef15308ea12151b..716947789d010412f329d410f6822bb9ca0b0bb3 100644
--- a/src/commands/vault.rs
+++ b/src/commands/vault.rs
@@ -1,15 +1,16 @@
 use crate::commands::cesium::compute_g1v1_public_key;
-use crate::entities::vault_account::CryptoType;
-use crate::entities::{vault_account, vault_derivation};
+use crate::entities::vault_account;
+use crate::entities::vault_account::{AccountTreeNode, DbAccountId};
 use crate::*;
 use age::secrecy::Secret;
 use comfy_table::{Cell, Table};
-use sea_orm::ActiveValue::Set;
-use sea_orm::{ActiveModelTrait, EntityTrait, ModelTrait};
-use sea_orm::{ColumnTrait, QueryFilter};
+use sea_orm::ModelTrait;
 use sea_orm::{ConnectionTrait, TransactionTrait};
+use sp_core::crypto::AddressUri;
+use std::cell::RefCell;
 use std::io::{Read, Write};
 use std::path::PathBuf;
+use std::rc::Rc;
 
 /// vault subcommands
 #[derive(Clone, Debug, clap::Parser)]
@@ -24,27 +25,31 @@ pub enum Subcommand {
 	},
 	/// Generate a mnemonic
 	Generate,
-	/// Import key from (substrate)mnemonic or other format with interactive prompt
+	/// Import key from (substrate uri) or other format with interactive prompt
 	#[clap(
-		long_about = "Import key from (substrate)mnemonic or other format with interactive prompt\n\
+		long_about = "Import key from (substrate uri) or other format with interactive prompt\n\
 		\n\
-		If a (substrate)mnemonic is provided with a derivation path, it will ensure the base <Account>\n\
-		and associated SS58 Address exists before creating the derivation; but please use command \n\
-		`vault derivation|derive|deriv` to add a derivation to an existing <Account> instead."
+		This will create a <Base> account in the vault for the provided/computed Substrate URI \n\
+		and associated SS58 Address.\n\
+		\n\
+		If using default format (or specifically \"substrate\") a derivation path is supported\n\
+		in the substrate uri value"
 	)]
 	Import {
 		/// Secret key format (substrate, seed, cesium)
 		#[clap(short = 'S', long, required = false, default_value = SecretFormat::Substrate)]
 		secret_format: SecretFormat,
 	},
-	/// Add a derivation to an existing <Account>
-	#[clap(long_about = "Add a derivation to an existing <Account>\n\
+	/// Add a derivation to an existing account
+	#[clap(long_about = "Add a derivation to an existing account\n\
 		\n\
 		Only \"substrate\" and \"seed\" format are supported for derivations\n\
-		Use command `vault list account` to see available <Account> and their format")]
+		\n\
+		Use command `vault list base` to see available <Base> account and their format\n\
+		And then use command 'vault list for' to find all accounts linked to that <Base> account")]
 	#[clap(alias = "deriv")]
-	#[clap(alias = "derive")]
-	Derivation {
+	#[clap(alias = "derivation")]
+	Derive {
 		#[clap(flatten)]
 		address_or_vault_name: AddressOrVaultNameGroup,
 	},
@@ -53,10 +58,10 @@ pub enum Subcommand {
 		/// SS58 Address
 		address: AccountId,
 	},
-	/// Remove an SS58 Address from the vault
+	/// Remove an SS58 Address from the vault together with it's linked derivations
 	#[clap(long_about = "Remove an SS58 Address from the vault\n\
 		\n\
-		If an <Account> Address is given it will also remove all linked derivations")]
+		If a <Base> Address is given it will also remove the saved key")]
 	Remove {
 		#[clap(flatten)]
 		address_or_vault_name: AddressOrVaultNameGroup,
@@ -71,16 +76,16 @@ pub enum Subcommand {
 
 #[derive(Clone, Default, Debug, clap::Parser)]
 pub enum ListChoice {
-	/// List all <Account> and their linked derivations SS58 Addresses in the vault
+	/// List all <Base> accounts and their linked derivations SS58 Addresses in the vault
 	#[default]
 	All,
-	/// List <Account> and derivations SS58 Addresses linked to the selected one
+	/// List <Base> and Derivations SS58 Addresses linked to the selected one
 	For {
 		#[clap(flatten)]
 		address_or_vault_name: AddressOrVaultNameGroup,
 	},
-	/// List all <Account> SS58 Addresses in the vault
-	Account,
+	/// List all <Base> SS58 Addresses in the vault
+	Base,
 }
 
 pub struct VaultDataToImport {
@@ -90,7 +95,7 @@ pub struct VaultDataToImport {
 }
 
 // encrypt input with passphrase
-fn encrypt(input: &[u8], passphrase: String) -> Result<Vec<u8>, age::EncryptError> {
+pub fn encrypt(input: &[u8], passphrase: String) -> Result<Vec<u8>, age::EncryptError> {
 	let encryptor = age::Encryptor::with_user_passphrase(Secret::new(passphrase));
 	let mut encrypted = vec![];
 	let mut writer = encryptor.wrap_output(age::armor::ArmoredWriter::wrap_output(
@@ -103,7 +108,7 @@ fn encrypt(input: &[u8], passphrase: String) -> Result<Vec<u8>, age::EncryptErro
 }
 
 // decrypt cypher with passphrase
-fn decrypt(input: &[u8], passphrase: String) -> Result<Vec<u8>, age::DecryptError> {
+pub fn decrypt(input: &[u8], passphrase: String) -> Result<Vec<u8>, age::DecryptError> {
 	let age::Decryptor::Passphrase(decryptor) =
 		age::Decryptor::new(age::armor::ArmoredReader::new(input))?
 	else {
@@ -123,36 +128,37 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 	match command {
 		Subcommand::List(choice) => match choice {
 			ListChoice::All => {
-				let derivations = vault_derivation::list_all_derivations_in_order(db).await?;
-
-				let table = compute_vault_derivations_table(db, &derivations).await?;
+				let all_account_tree_node_hierarchies =
+					vault_account::fetch_all_base_account_tree_node_hierarchies(db).await?;
+				let table = compute_vault_accounts_table(&all_account_tree_node_hierarchies)?;
 
 				println!("available SS58 Addresses:");
 				println!("{table}");
 			}
-			ListChoice::Account => {
-				let derivations = vault_derivation::list_all_root_derivations_in_order(db).await?;
+			ListChoice::Base => {
+				let base_account_tree_nodes =
+					vault_account::fetch_only_base_account_tree_nodes(db).await?;
 
-				let table = compute_vault_derivations_table(db, &derivations).await?;
+				let table = compute_vault_accounts_table(&base_account_tree_nodes)?;
 
-				println!("available <Account> SS58 Addresses:");
+				println!("available <Base> SS58 Addresses:");
 				println!("{table}");
 			}
 			ListChoice::For {
 				address_or_vault_name,
 			} => {
-				let selected_derivation =
-					retrieve_vault_derivation(&data, address_or_vault_name).await?;
+				let account = retrieve_vault_account(&data, address_or_vault_name).await?;
 
-				let linked_derivations = vault_derivation::fetch_all_linked_derivations_in_order(
-					db,
-					&selected_derivation.root_address,
-				)
-				.await?;
+				let account_tree_node_hierarchy =
+					vault_account::fetch_base_account_tree_node_hierarchy_unwrapped(
+						db,
+						&account.address.to_string(),
+					)
+					.await?;
 
-				let table = compute_vault_derivations_table(db, &linked_derivations).await?;
+				let table = compute_vault_accounts_table(&[account_tree_node_hierarchy])?;
 
-				println!("available SS58 Addresses linked to {selected_derivation}:");
+				println!("available SS58 Addresses linked to {account}:");
 				println!("{table}");
 			}
 		},
@@ -167,12 +173,12 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 		Subcommand::Use {
 			address_or_vault_name,
 		} => {
-			let derivation = retrieve_vault_derivation(&data, address_or_vault_name).await?;
+			let account = retrieve_vault_account(&data, address_or_vault_name).await?;
 
-			println!("Using: {}", derivation);
+			println!("Using: {}", account);
 
 			let updated_cfg = conf::Config {
-				address: Some(AccountId::from_str(&derivation.address).expect("invalid address")),
+				address: Some(account.address.0),
 				..data.cfg
 			};
 
@@ -200,30 +206,32 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 				}
 			}
 
-			let address_to_import = vault_data_for_import.key_pair.address().to_string();
+			let address_to_import = vault_data_for_import.key_pair.address();
 
 			println!("Trying to import for SS58 address :'{}'", address_to_import);
 
-			if let Some(derivation) = vault_derivation::Entity::find_by_id(&address_to_import)
-				.one(db)
-				.await?
+			if let Some(check_account) =
+				vault_account::find_by_id(db, &DbAccountId::from(address_to_import)).await?
 			{
 				println!(
 					"Vault entry already exists for that address: {}",
-					derivation
+					check_account
 				);
 
-				let linked_derivations = vault_derivation::fetch_all_linked_derivations_in_order(
-					db,
-					&derivation.root_address.clone(),
-				)
-				.await?;
+				let account_tree_node_hierarchy =
+					vault_account::fetch_base_account_tree_node_hierarchy_unwrapped(
+						db,
+						&check_account.address.to_string(),
+					)
+					.await?;
+
 				println!("Here are all the SS58 Addresses linked to it in the vault:");
 
-				let table = compute_vault_derivations_table(db, &linked_derivations).await?;
+				let table = compute_vault_accounts_table(&[account_tree_node_hierarchy])?;
 				println!("{table}");
 
 				return Ok(());
+				//TODO For later, possibly allow to replace the entry
 			}
 
 			println!("Enter password to protect the key");
@@ -234,7 +242,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 
 			let txn = db.begin().await?;
 
-			let _derivation = create_derivation_for_vault_data_to_import(
+			let _account = create_account_for_vault_data_to_import(
 				&txn,
 				&vault_data_for_import,
 				&password,
@@ -246,60 +254,63 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 
 			println!("Import done");
 		}
-		Subcommand::Derivation {
+		Subcommand::Derive {
 			address_or_vault_name,
 		} => {
-			let root_derivation = retrieve_vault_derivation(&data, address_or_vault_name).await?;
+			let account_tree_node_to_derive =
+				retrieve_account_tree_node(&data, address_or_vault_name).await?;
 
-			if root_derivation.path.is_some() {
-				println!("Can only add derivation on an <Account>");
-				println!(
-					"The selected address:'{}' already has an <Account> with address:'{}'",
-					root_derivation.address, root_derivation.root_address
-				);
-				println!("You can check for available <Account> addresses with command 'vault list account'");
-				return Ok(());
-			}
+			let account_to_derive = account_tree_node_to_derive.borrow().account.clone();
 
-			let vault_account = vault_account::Entity::find_by_id(&root_derivation.address)
-				.one(db)
-				.await?
-				.ok_or(GcliError::Input(format!(
-					"Could not find vault_account for address:'{}'",
-					root_derivation.address
-				)))?;
+			let base_account_tree_node =
+				vault_account::get_base_account_tree_node(&account_tree_node_to_derive);
 
-			if vault_account.crypto_type == CryptoType::G1v1Seed {
-				println!(
-					"Only \"{}\" and \"{}\" format are supported for derivations",
-					Into::<&str>::into(SecretFormat::Substrate),
-					Into::<&str>::into(SecretFormat::Seed)
-				);
-				println!(
-					"Use command `vault list account` to see available <Account> and their format"
-				);
-				return Ok(());
+			let base_account = &base_account_tree_node.borrow().account.clone();
+
+			if base_account.crypto_scheme.is_none() {
+				panic!("Crypto scheme is not set for the base account:{base_account} - should not happen");
+			}
+
+			if let Some(crypto_scheme) = base_account.crypto_scheme {
+				if CryptoScheme::from(crypto_scheme) == CryptoScheme::Ed25519 {
+					println!(
+						"Only \"{}\" and \"{}\" format are supported for derivations",
+						Into::<&str>::into(SecretFormat::Substrate),
+						Into::<&str>::into(SecretFormat::Seed)
+					);
+					println!();
+					println!(
+                        "Use command `vault list base` to see available <Base> account and their format\n\
+						And then use command 'vault list for' to find all accounts linked to that <Base> account"
+                    );
+					return Ok(());
+				}
 			}
 
-			println!("Adding derivation to: {root_derivation}");
+			println!("Adding derivation to: {account_to_derive}");
 
-			println!("Enter password to decrypt the <Account> key");
+			println!("Enter password to decrypt the <Base> account key");
+			let password = inputs::prompt_password()?;
 
-			let root_secret_suri = retrieve_suri_from_vault_account(&vault_account)?;
+			let account_to_derive_secret_suri = vault_account::compute_suri_account_tree_node(
+				&account_tree_node_to_derive,
+				password,
+			)?;
 
 			let derivation_path = inputs::prompt_vault_derivation_path()?;
 
-			let derivation_secret_suri = format!("{root_secret_suri}{derivation_path}");
+			let derivation_secret_suri =
+				format!("{account_to_derive_secret_suri}{derivation_path}");
 
 			let derivation_keypair =
-				compute_keypair(vault_account.crypto_type, &derivation_secret_suri)?;
+				compute_keypair(CryptoScheme::Sr25519, &derivation_secret_suri)?;
 
 			let derivation_address: String = derivation_keypair.address().to_string();
 
-			let check_derivation = vault_derivation::Entity::find_by_id(&derivation_address)
-				.one(db)
-				.await?;
+			let check_derivation =
+				vault_account::find_by_id(db, &DbAccountId::from_str(&derivation_address)?).await?;
 
+			//TODO For later, possibly allow to replace the entry
 			if check_derivation.is_some() {
 				println!("Derivation already exists for address:'{derivation_address}'");
 				return Ok(());
@@ -308,91 +319,88 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 			println!("(Optional) Enter a name for the new derivation");
 			let name = inputs::prompt_vault_name()?;
 
-			let derivation = vault_derivation::ActiveModel {
-				address: Set(derivation_address),
-				name: Set(name),
-				path: Set(Some(derivation_path)),
-				root_address: Set(root_derivation.root_address.clone()),
-			};
-			let derivation = derivation.insert(db).await?;
-			println!("Created: {}", derivation);
+			let _derivation = vault_account::create_derivation_account(
+				db,
+				&derivation_address,
+				name.as_ref(),
+				&derivation_path,
+				&account_to_derive.address.to_string(),
+			)
+			.await?;
+
+			println!("Derive done");
 		}
 		Subcommand::Rename { address } => {
-			let derivation = vault_derivation::Entity::find_by_id(address.to_string())
-				.one(db)
-				.await?;
+			let account =
+				vault_account::find_by_id(db, &DbAccountId::from(address.clone())).await?;
 
-			if derivation.is_none() {
+			if account.is_none() {
 				println!("No vault entry found for address:'{address}'");
 				println!("You might want to import it first with 'vault import'");
 				return Ok(());
 			}
 
-			let derivation = derivation.unwrap();
+			let account = account.unwrap();
 
 			println!(
 				"Current name for address:'{address}' is {:?}",
-				derivation.name
+				&account.name
 			);
 
 			println!("Enter new name for address (leave empty to remove the name)");
 			let name = inputs::prompt_vault_name()?;
 
-			let old_name = derivation.name.clone();
-			let mut derivation: vault_derivation::ActiveModel = derivation.into();
-			derivation.name = Set(name.clone());
-			let _derivation = derivation.update(db).await?;
-			println!(
-				"Renamed address:'{address}' from {:?} to {:?}",
-				old_name, name
-			);
+			let _account = vault_account::update_account_name(db, account, name.as_ref()).await?;
+
+			println!("Rename done");
 		}
 		Subcommand::Remove {
 			address_or_vault_name,
 		} => {
-			let derivation = retrieve_vault_derivation(&data, address_or_vault_name).await?;
-			let address_to_delete = derivation.address.clone();
+			let account_tree_node_to_delete =
+				retrieve_account_tree_node(&data, address_or_vault_name).await?;
 
 			let txn = db.begin().await?;
 
-			//If deleting a root derivation; also delete the vault account and all linked derivations
-			if derivation.path.is_none() {
-				let all_derivations_to_delete =
-					vault_derivation::fetch_all_linked_derivations_in_order(
-						&txn,
-						&address_to_delete,
-					)
-					.await?;
+			let account_to_delete = account_tree_node_to_delete.borrow().account.clone();
+			let address_to_delete = account_tree_node_to_delete.borrow().account.address.clone();
 
-				let table =
-					compute_vault_derivations_table(&txn, &all_derivations_to_delete).await?;
+			//If account to delete has children; also delete all linked derivations
+			if !account_tree_node_to_delete.borrow().children.is_empty() {
+				let table = compute_vault_accounts_table(&[account_tree_node_to_delete.clone()])?;
 
-				println!("All addresses linked to: {derivation}");
+				println!("All addresses linked to: {account_to_delete}");
 				println!("{table}");
 
 				println!(
-					"This <Account> has {} addresses in total",
-					all_derivations_to_delete.len()
+					"This {} account has {} addresses in total",
+					account_to_delete.account_type(),
+					vault_account::count_accounts_in_account_tree_node_and_children(
+						&account_tree_node_to_delete
+					)
 				);
-				let confirmed = inputs::confirm_action(
-					"Are you sure you want to delete it along with the saved key ?".to_string(),
-				)?;
+
+				let confirmation_message = if account_to_delete.is_base_account() {
+					"Are you sure you want to delete it along with the saved key ?"
+				} else {
+					"Are you sure you want to delete it ?"
+				};
+
+				let confirmed = inputs::confirm_action(confirmation_message.to_string())?;
 
 				if !confirmed {
 					return Ok(());
 				}
 
-				for derivation_to_delete in all_derivations_to_delete {
-					let delete_result = derivation_to_delete.delete(&txn).await?;
+				for account_to_delete in
+					vault_account::extract_accounts_depth_first_from_account_tree_node(
+						&account_tree_node_to_delete,
+					)? {
+					let delete_result = account_to_delete.delete(&txn).await?;
 					println!("Deleted {} address", delete_result.rows_affected);
 				}
-
-				let delete_result = vault_account::Entity::delete_by_id(&address_to_delete)
-					.exec(&txn)
-					.await?;
-				println!("Deleted {} vault account", delete_result.rows_affected);
 			} else {
-				let delete_result = derivation.delete(&txn).await?;
+				let delete_result = account_to_delete.delete(&txn).await?;
 				println!("Deleted {} address", delete_result.rows_affected);
 			}
 
@@ -411,11 +419,10 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 
 			for address in vault_key_addresses {
 				//Check if we already have a vault_derivation for that address
-				let derivation = vault_derivation::Entity::find_by_id(&address)
-					.one(db)
-					.await?;
+				let existing_account =
+					vault_account::find_by_id(db, &DbAccountId::from_str(&address)?).await?;
 
-				if derivation.is_some() {
+				if existing_account.is_some() {
 					//Already migrated
 					continue;
 				}
@@ -443,7 +450,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 
 				let txn = db.begin().await?;
 
-				let derivation = create_derivation_for_vault_data_to_import(
+				let account = create_account_for_vault_data_to_import(
 					&txn,
 					&vault_data_to_import,
 					&vault_data_from_file.password,
@@ -452,7 +459,7 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 				.await?;
 
 				txn.commit().await?;
-				println!("Import done: {}", derivation);
+				println!("Import done: {}", account);
 			}
 
 			println!("Migration done");
@@ -465,7 +472,8 @@ pub async fn handle_command(data: Data, command: Subcommand) -> Result<(), GcliE
 	Ok(())
 }
 
-fn parse_prefix_and_derivation_path_from_string(
+/// Method used to separate `name` part from optional `derivation` part in computed names
+fn parse_prefix_and_derivation_path_from_string_for_vault_name(
 	raw_string: String,
 ) -> Result<(String, Option<String>), GcliError> {
 	if raw_string.contains("/") {
@@ -480,12 +488,48 @@ fn parse_prefix_and_derivation_path_from_string(
 	}
 }
 
-fn map_secret_format_to_crypto_type(secret_format: SecretFormat) -> CryptoType {
+/// Method that can be used to parse a Substrate URI (which can also be only a derivation path)
+///
+/// Does some internal verification (done by sp_core::address_uri::AddressUri)
+///
+/// It extracts the (optional) `phrase` and the (optional) recomposed full `derivation path`
+///
+/// It also checks if a derivation `password` was provided and returns an error if one was found
+pub fn parse_prefix_and_derivation_path_from_suri(
+	raw_string: String,
+) -> Result<(Option<String>, Option<String>), GcliError> {
+	let address_uri =
+		AddressUri::parse(&raw_string).map_err(|e| GcliError::Input(e.to_string()))?;
+
+	if let Some(pass) = address_uri.pass {
+		return Err(GcliError::Input(format!(
+			"Having a password in the derivation path is not supported (password:'{}')",
+			pass
+		)));
+	}
+
+	let full_path = if address_uri.paths.is_empty() {
+		None
+	} else {
+		Some(
+			address_uri
+				.paths
+				.iter()
+				.map(|s| "/".to_string() + s)
+				.collect::<Vec<_>>()
+				.join(""),
+		)
+	};
+
+	Ok((address_uri.phrase.map(|s| s.to_string()), full_path))
+}
+
+fn map_secret_format_to_crypto_scheme(secret_format: SecretFormat) -> CryptoScheme {
 	match secret_format {
-		SecretFormat::Seed => vault_account::CryptoType::EntropyKdfSeed,
-		SecretFormat::Substrate => vault_account::CryptoType::Bip39Mnemonic,
-		SecretFormat::Predefined => vault_account::CryptoType::Bip39Mnemonic,
-		SecretFormat::Cesium => vault_account::CryptoType::G1v1Seed,
+		SecretFormat::Seed => CryptoScheme::Sr25519,
+		SecretFormat::Substrate => CryptoScheme::Sr25519,
+		SecretFormat::Predefined => CryptoScheme::Sr25519,
+		SecretFormat::Cesium => CryptoScheme::Ed25519,
 	}
 }
 
@@ -524,85 +568,76 @@ async fn compute_vault_key_files_table(vault_key_addresses: &[String]) -> Result
 	Ok(table)
 }
 
-async fn compute_vault_derivations_table<C>(
-	db: &C,
-	derivations_ordered: &[vault_derivation::Model],
-) -> Result<Table, GcliError>
-where
-	C: ConnectionTrait,
-{
+fn compute_vault_accounts_table(
+	account_tree_nodes: &[Rc<RefCell<AccountTreeNode>>],
+) -> Result<Table, GcliError> {
 	let mut table = Table::new();
 	table.load_preset(comfy_table::presets::UTF8_BORDERS_ONLY);
 	table.set_header(vec!["SS58 Address", "Format", "Account/Path", "Name"]);
+
+	for account_tree_node in account_tree_nodes {
+		add_account_tree_node_to_table(&mut table, account_tree_node);
+	}
+
+	Ok(table)
+}
+
+fn add_account_tree_node_to_table(
+	table: &mut Table,
+	account_tree_node: &Rc<RefCell<AccountTreeNode>>,
+) {
+	let row = compute_vault_accounts_row(account_tree_node);
+	table.add_row(row);
+
+	for child in &account_tree_node.borrow().children {
+		add_account_tree_node_to_table(table, child);
+	}
+}
+
+pub fn compute_vault_accounts_row(account_tree_node: &Rc<RefCell<AccountTreeNode>>) -> Vec<Cell> {
 	let empty_string = "".to_string();
-	let root_path = "<Account>".to_string();
-
-	let mut current_root_address = "".to_string();
-	let mut current_root_name: Option<String> = None;
-	let mut current_vault_format: Option<&str> = None;
-
-	for derivation in derivations_ordered {
-		if derivation.root_address != current_root_address {
-			// First entry when changing root address should be an account ("root" derivation)
-			if derivation.path.is_some() {
-				return Err(GcliError::Input(
-					"Order of derivations parameter is wrong".to_string(),
-				));
-			}
-			current_root_address = derivation.root_address.clone();
-			current_root_name = derivation.name.clone();
 
-			let vault_account = vault_account::Entity::find_by_id(current_root_address.clone())
-				.one(db)
-				.await?
-				.ok_or(GcliError::Input(format!(
-					"No vault <Account> found with address:'{current_root_address}'"
-				)))?;
+	let depth_account_tree_node = vault_account::count_depth_account_tree_node(account_tree_node);
 
-			current_vault_format = match vault_account.crypto_type {
-				CryptoType::Bip39Mnemonic => Some(SecretFormat::Substrate.into()),
-				CryptoType::EntropyKdfSeed => Some(SecretFormat::Seed.into()),
-				CryptoType::G1v1Seed => Some(SecretFormat::Cesium.into()),
-			};
-		}
+	let name = if let Some(name) = account_tree_node.borrow().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 address = if derivation.path.is_none() {
-			derivation.address.clone()
-		} else {
-			"  ".to_string() + &derivation.address
-		};
+	let account_tree_node = account_tree_node.borrow();
 
-		let (path, format) = if derivation.path.is_none() {
-			(root_path.clone(), current_vault_format.unwrap())
-		} else {
-			(derivation.path.clone().unwrap(), empty_string.as_str())
-		};
+	let address = if depth_account_tree_node > 0 {
+		let ancestors = "│ ".repeat(depth_account_tree_node - 1);
+		format!("{}├─{}", ancestors, account_tree_node.account.address)
+	} else {
+		account_tree_node.account.address.to_string()
+	};
 
-		let name = if derivation.name.is_none() {
-			if derivation.path.is_none() {
-				empty_string.clone()
-			} else if let Some(current_root_name) = &current_root_name {
-				format!(
-					"<{}{}>",
-					current_root_name,
-					derivation.path.clone().unwrap()
-				)
-			} else {
-				empty_string.clone()
-			}
-		} else {
-			derivation.name.clone().unwrap()
+	let (path, format) = if let Some(path) = account_tree_node.account.path.clone() {
+		(path, empty_string.clone())
+	} else {
+		let secret_format = match account_tree_node.account.crypto_scheme.unwrap().into() {
+			CryptoScheme::Sr25519 => SecretFormat::Substrate,
+			CryptoScheme::Ed25519 => SecretFormat::Cesium,
 		};
+		let secret_format_str: &str = secret_format.into();
+		(
+			format!("<{}>", account_tree_node.account.account_type()),
+			secret_format_str.to_string(),
+		)
+	};
 
-		table.add_row(vec![
-			Cell::new(&address),
-			Cell::new(format),
-			Cell::new(&path),
-			Cell::new(&name),
-		]);
-	}
-
-	Ok(table)
+	vec![
+		Cell::new(&address),
+		Cell::new(format),
+		Cell::new(&path),
+		Cell::new(&name),
+	]
 }
 
 pub async fn retrieve_address_string<T: AddressOrVaultName>(
@@ -613,57 +648,87 @@ pub async fn retrieve_address_string<T: AddressOrVaultName>(
 		return Ok(address.to_string());
 	}
 
-	let derivation = retrieve_vault_derivation(data, address_or_vault_name).await?;
+	let account = retrieve_vault_account(data, address_or_vault_name).await?;
 
-	Ok(derivation.address)
+	Ok(account.address.to_string())
 }
 
-pub async fn retrieve_vault_derivation<T: AddressOrVaultName>(
+pub async fn retrieve_account_tree_node<T: AddressOrVaultName>(
 	data: &Data,
 	address_or_vault_name: T,
-) -> Result<vault_derivation::Model, GcliError> {
-	let derivation = if let Some(name) = address_or_vault_name.name() {
+) -> Result<Rc<RefCell<AccountTreeNode>>, GcliError> {
+	//FIXME Should do the inverse as we do potentially several times same operation
+	let account = retrieve_vault_account(data, address_or_vault_name).await?;
+
+	let account_tree_node = vault_account::fetch_base_account_tree_node_hierarchy_unwrapped(
+		data.connect_db(),
+		&account.address.to_string(),
+	)
+	.await?;
+
+	Ok(vault_account::get_account_tree_node_for_address(
+		&account_tree_node,
+		&account.address.to_string(),
+	))
+}
+
+pub async fn retrieve_vault_account<T: AddressOrVaultName>(
+	data: &Data,
+	address_or_vault_name: T,
+) -> Result<vault_account::Model, GcliError> {
+	let account = if let Some(name_input) = address_or_vault_name.name() {
 		let (name, derivation_path_opt) =
-			parse_prefix_and_derivation_path_from_string(name.to_string())?;
+			parse_prefix_and_derivation_path_from_string_for_vault_name(name_input.to_string())?;
 
-		let derivation = vault_derivation::Entity::find()
-			.filter(vault_derivation::Column::Name.eq(Some(name.clone())))
-			.one(data.connect_db())
-			.await?;
+		let account = vault_account::find_by_name(data.connect_db(), &name).await?;
 
-		let derivation = derivation.ok_or(GcliError::Input(format!(
-			"No vault SS58 Address found with name:'{name}'"
+		let account = account.ok_or(GcliError::Input(format!(
+			"No account found with name:'{name}'"
 		)))?;
 
 		match derivation_path_opt {
-			None => derivation,
+			None => account,
 			Some(path) => {
-				let sub_derivation = vault_derivation::Entity::find()
-					.filter(
-						vault_derivation::Column::RootAddress.eq(derivation.root_address.clone()),
+				let account_tree_node_hierarchy =
+					vault_account::fetch_base_account_tree_node_hierarchy_unwrapped(
+						data.connect_db(),
+						&account.address.to_string(),
 					)
-					.filter(vault_derivation::Column::Path.eq(Some(path.clone())))
-					.one(data.connect_db())
 					.await?;
 
-				sub_derivation.ok_or(GcliError::Input(format!(
-					"No vault derivation found with <Account> name:'{name}' and path:'{path}'"
-				)))?
+				let account_tree_node_hierarchy = vault_account::get_account_tree_node_for_address(
+					&account_tree_node_hierarchy,
+					&account.address.to_string(),
+				);
+
+				let account_tree_node = vault_account::compute_name_map_for_account_tree_node(
+					&account_tree_node_hierarchy,
+				)?
+				.get(name_input)
+				.cloned()
+				.ok_or(GcliError::Input(format!(
+					"No account found with name:'{name}' and path:'{path}'"
+				)))?;
+
+				//Need this extra step to avoid borrowing issues
+				let account = account_tree_node.borrow().account.clone();
+
+				account
 			}
 		}
 	} else if let Some(address) = address_or_vault_name.address() {
-		let derivation = vault_derivation::Entity::find_by_id(address.to_string())
-			.one(data.connect_db())
-			.await?;
+		let account =
+			vault_account::find_by_id(data.connect_db(), &DbAccountId::from(address.clone()))
+				.await?;
 
-		derivation.ok_or(GcliError::Input(format!(
+		account.ok_or(GcliError::Input(format!(
 			"No vault entry found with Address:'{address}'"
 		)))?
 	} else {
 		//Should never happen since clap enforces exactly one of the 2 options
 		return Err(GcliError::Input("No address or name provided".to_string()));
 	};
-	Ok(derivation)
+	Ok(account)
 }
 
 fn create_vault_data_to_import<F, P>(
@@ -702,25 +767,25 @@ fn prompt_secret_and_compute_vault_data_to_import(
 	}
 }
 
-/// Creates derivation and if necessary root vault account and root derivation
+/// Creates an account for the vault data to import
 ///
 /// Does it all using "db" parameter that should better be a transaction since multiple operations can be done
-pub async fn create_derivation_for_vault_data_to_import<C>(
+pub async fn create_account_for_vault_data_to_import<C>(
 	db: &C,
 	vault_data: &VaultDataToImport,
 	password: &str,
 	name: Option<&String>,
-) -> Result<vault_derivation::Model, GcliError>
+) -> Result<vault_account::Model, GcliError>
 where
 	C: ConnectionTrait,
 {
 	let address_to_import = vault_data.key_pair.address().to_string();
 	//To be safe
-	if vault_derivation::Entity::find_by_id(&address_to_import)
-		.one(db)
+	if vault_account::find_by_id(db, &DbAccountId::from_str(&address_to_import)?)
 		.await?
 		.is_some()
 	{
+		//TODO Later possibly allow to replace the entry
 		return Err(GcliError::Input(format!(
 			"Vault entry already exists for address {}",
 			&address_to_import
@@ -729,136 +794,24 @@ where
 
 	let secret_format = vault_data.secret_format;
 
-	let (root_secret_suri, derivation_path_opt, root_address, derivation_address) =
-		compute_root_and_derivation_data(&secret_format, vault_data.secret_suri.clone())?;
-
-	// Making sure the computed address is the same as the address to import
-	if let Some(derivation_address) = &derivation_address {
-		if *derivation_address != address_to_import {
-			return Err(GcliError::Input(format!(
-				"Derivation address {} does not match the expected address {}",
-				derivation_address, address_to_import
-			)));
-		}
-	} else if root_address != address_to_import {
-		return Err(GcliError::Input(format!(
-			"Derivation address {} does not match the expected address {}",
-			root_address, address_to_import
-		)));
-	}
-
-	let encrypted_suri =
-		encrypt(root_secret_suri.as_bytes(), password.to_string()).map_err(|e| anyhow!(e))?;
-
-	let crypto_type = map_secret_format_to_crypto_type(secret_format);
-
-	let _root_account =
-		vault_account::create_vault_account(db, &root_address, crypto_type, encrypted_suri).await?;
-
-	let derivation = if let Some(derivation_path) = derivation_path_opt {
-		let derivation_address = derivation_address.unwrap();
-
-		// Extra check of derivation path to make sure it's not linking to the same SS58 Address as root
-		if root_address == derivation_address {
-			println!("Derivation path provided:'{derivation_path}' linked to the same SS58 Address than the base suri without derivation");
-
-			let root_derivation =
-				vault_derivation::create_root_vault_derivation(db, &root_address, name).await?;
-
-			println!("For that reason only the base suri was imported");
-			println!("Created: {}", root_derivation);
+	let encrypted_suri = encrypt(
+		vault_data.secret_suri.clone().as_bytes(),
+		password.to_string(),
+	)
+	.map_err(|e| anyhow!(e))?;
 
-			root_derivation
-		} else {
-			let _root_derivation =
-				vault_derivation::create_root_vault_derivation(db, &root_address, None).await?;
-
-			// Compute derivation !
-			let derivation = vault_derivation::ActiveModel {
-				address: Set(derivation_address.clone()),
-				name: Set(name.cloned()),
-				path: Set(Some(derivation_path)),
-				root_address: Set(root_address.clone()),
-			};
-
-			let derivation = derivation.insert(db).await?;
-
-			println!("Created: {}", derivation);
-			derivation
-		}
-	} else {
-		let derivation =
-			vault_derivation::create_root_vault_derivation(db, &root_address, name).await?;
-		println!("Created: {}", derivation);
-		derivation
-	};
+	let crypto_scheme = map_secret_format_to_crypto_scheme(secret_format);
 
-	Ok(derivation)
-}
+	let base_account = vault_account::create_base_account(
+		db,
+		&address_to_import,
+		name,
+		crypto_scheme,
+		encrypted_suri,
+	)
+	.await?;
 
-fn compute_root_and_derivation_data(
-	secret_format: &SecretFormat,
-	secret_suri: String,
-) -> Result<(String, Option<String>, String, Option<String>), GcliError> {
-	let (root_secret_suri, derivation_path_opt) =
-		parse_prefix_and_derivation_path_from_string(secret_suri)?;
-
-	let (root_address, derivation_address_opt) = match &secret_format {
-		SecretFormat::Cesium => match &derivation_path_opt {
-			None => {
-				let root_suri = &root_secret_suri;
-				let root_pair = pair_from_ed25519_str(root_suri)?;
-				let root_address: AccountId = root_pair.public().into();
-
-				(root_address.to_string(), None)
-			}
-			Some(derivation_path) => {
-				let root_suri = &root_secret_suri;
-				let root_pair = pair_from_ed25519_str(root_suri)?;
-				let root_address: AccountId = root_pair.public().into();
-
-				let derivation_suri = root_suri.clone() + derivation_path;
-				let derivation_pair = pair_from_ed25519_str(&derivation_suri)?;
-				let derivation_address: AccountId = derivation_pair.public().into();
-
-				(
-					root_address.to_string(),
-					Some(derivation_address.to_string()),
-				)
-			}
-		},
-		SecretFormat::Substrate | SecretFormat::Seed | SecretFormat::Predefined => {
-			match &derivation_path_opt {
-				None => {
-					let root_suri = &root_secret_suri;
-					let root_pair = pair_from_sr25519_str(root_suri)?;
-					let root_address: AccountId = root_pair.public().into();
-
-					(root_address.to_string(), None)
-				}
-				Some(derivation_path) => {
-					let root_suri = &root_secret_suri;
-					let root_pair = pair_from_sr25519_str(root_suri)?;
-					let root_address: AccountId = root_pair.public().into();
-
-					let derivation_suri = root_suri.clone() + derivation_path;
-					let derivation_pair = pair_from_sr25519_str(&derivation_suri)?;
-					let derivation_address: AccountId = derivation_pair.public().into();
-
-					(
-						root_address.to_string(),
-						Some(derivation_address.to_string()),
-					)
-				}
-			}
-		}
-	};
-	Ok((
-		root_secret_suri,
-		derivation_path_opt,
-		root_address,
-		derivation_address_opt,
-	))
+	Ok(base_account)
 }
 
 fn get_vault_key_path(data: &Data, vault_filename: &str) -> PathBuf {
@@ -880,62 +833,50 @@ pub async fn try_fetch_key_pair(
 	data: &Data,
 	address: AccountId,
 ) -> Result<Option<KeyPair>, GcliError> {
-	if let Some(derivation) = vault_derivation::Entity::find_by_id(address.to_string())
-		.one(data.connect_db())
+	if let Some(account_tree_node_hierarchy) =
+		vault_account::fetch_base_account_tree_node_hierarchy(
+			data.connect_db(),
+			&address.to_string(),
+		)
 		.await?
 	{
-		if let Some(vault_account) =
-			vault_account::Entity::find_by_id(derivation.root_address.clone())
-				.one(data.connect_db())
-				.await?
-		{
-			let root_secret_suri = retrieve_suri_from_vault_account(&vault_account)?;
-
-			let secret_suri = if let Some(derivation_path) = derivation.path {
-				format!("{root_secret_suri}{derivation_path}")
-			} else {
-				root_secret_suri
-			};
+		let account_tree_node = vault_account::get_account_tree_node_for_address(
+			&account_tree_node_hierarchy,
+			&address.to_string(),
+		);
 
-			let key_pair = compute_keypair(vault_account.crypto_type, &secret_suri)?;
+		let password = inputs::prompt_password()?;
+		let secret_suri =
+			vault_account::compute_suri_account_tree_node(&account_tree_node, password)?;
 
-			//To be safe
-			if address != key_pair.address() {
-				return Err(GcliError::Input(format!(
-					"Computed address {} does not match the expected address {}",
-					key_pair.address(),
-					address
-				)));
-			}
+		let base_account_tree_node = vault_account::get_base_account_tree_node(&account_tree_node);
 
-			Ok(Some(key_pair))
-		} else {
-			Ok(None)
+		let base_account = &base_account_tree_node.borrow().account.clone();
+
+		let key_pair = compute_keypair(base_account.crypto_scheme.unwrap().into(), &secret_suri)?;
+
+		//To be safe
+		if address != key_pair.address() {
+			return Err(GcliError::Input(format!(
+				"Computed address {} does not match the expected address {}",
+				key_pair.address(),
+				address
+			)));
 		}
+
+		Ok(Some(key_pair))
 	} else {
 		Ok(None)
 	}
 }
 
-pub fn retrieve_suri_from_vault_account(
-	vault_account: &vault_account::Model,
-) -> Result<String, GcliError> {
-	let password = inputs::prompt_password()?;
-
-	let cypher = &vault_account.encrypted_suri;
-	let secret_vec =
-		decrypt(cypher, password.clone()).map_err(|e| GcliError::Input(e.to_string()))?;
-	let secret_suri = String::from_utf8(secret_vec).map_err(|e| anyhow!(e))?;
-
-	Ok(secret_suri)
-}
-
-pub fn compute_keypair(crypto_type: CryptoType, secret_suri: &str) -> Result<KeyPair, GcliError> {
-	let key_pair = match crypto_type {
-		CryptoType::Bip39Mnemonic | CryptoType::EntropyKdfSeed => {
-			pair_from_sr25519_str(secret_suri)?.into()
-		}
-		CryptoType::G1v1Seed => pair_from_ed25519_str(secret_suri)?.into(),
+pub fn compute_keypair(
+	crypto_scheme: CryptoScheme,
+	secret_suri: &str,
+) -> Result<KeyPair, GcliError> {
+	let key_pair = match crypto_scheme {
+		CryptoScheme::Sr25519 => pair_from_sr25519_str(secret_suri)?.into(),
+		CryptoScheme::Ed25519 => pair_from_ed25519_str(secret_suri)?.into(),
 	};
 	Ok(key_pair)
 }
@@ -956,7 +897,7 @@ pub fn try_fetch_vault_data_from_file(
 ) -> Result<Option<VaultDataFromFile>, GcliError> {
 	if let Some(path) = find_substrate_vault_key_file(data, address)? {
 		println!("Enter password to unlock account {address}");
-		let password = rpassword::prompt_password("Password: ")?;
+		let password = inputs::prompt_password()?;
 		let mut file = std::fs::OpenOptions::new().read(true).open(path.clone())?;
 		let mut cypher = vec![];
 		file.read_to_end(&mut cypher)?;
@@ -996,50 +937,178 @@ mod tests {
 	#[rstest]
 	#[case(
 		String::from("bottom drive obey lake curtain smoke basket hold race lonely fit walk//0"),
-		String::from("bottom drive obey lake curtain smoke basket hold race lonely fit walk"),
+		Some(String::from(
+			"bottom drive obey lake curtain smoke basket hold race lonely fit walk"
+		)),
 		Some(String::from("//0"))
 	)]
 	#[case(
 		String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e//0"),
-		String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e"),
+		Some(String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e")),
 		Some(String::from("//0"))
 	)]
 	#[case(
 		String::from(
 			"bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice"
 		),
-		String::from("bottom drive obey lake curtain smoke basket hold race lonely fit walk"),
+		Some(String::from(
+			"bottom drive obey lake curtain smoke basket hold race lonely fit walk"
+		)),
 		Some(String::from("//Alice"))
 	)]
+	#[case(
+        String::from(
+            "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice//Bob/soft1/soft2"
+        ),
+        Some(String::from("bottom drive obey lake curtain smoke basket hold race lonely fit walk")),
+        Some(String::from("//Alice//Bob/soft1/soft2"))
+    )]
 	#[case(
 		String::from("bottom drive obey lake curtain smoke basket hold race lonely fit walk"),
-		String::from("bottom drive obey lake curtain smoke basket hold race lonely fit walk"),
+		Some(String::from(
+			"bottom drive obey lake curtain smoke basket hold race lonely fit walk"
+		)),
 		None
 	)]
 	#[case(
 		String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e"),
-		String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e"),
+		Some(String::from("0xfac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e")),
 		None
 	)]
 	#[case(
 		String::from("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e"),
-		String::from("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e"),
+		Some(String::from("fac7959dbfe72f052e5a0c3c8d6530f202b02fd8f9f5ca3580ec8deb7797479e")),
 		None
 	)]
 	#[case(
 		String::from("someVaultName//Alice"),
-		String::from("someVaultName"),
+		Some(String::from("someVaultName")),
 		Some(String::from("//Alice"))
 	)]
-	#[case(String::from("someVaultName"), String::from("someVaultName"), None)]
-	fn test_parse_prefix_and_derivation_path_from_string(
+	#[case(
+		String::from("someVaultName"),
+		Some(String::from("someVaultName")),
+		None
+	)]
+	fn test_parse_prefix_and_derivation_path_from_suri(
 		#[case] raw_string: String,
-		#[case] expected_prefix: String,
+		#[case] expected_prefix: Option<String>,
 		#[case] expected_derivation_path: Option<String>,
 	) {
 		let (root_secret, derivation_path) =
-			parse_prefix_and_derivation_path_from_string(raw_string).unwrap();
+			parse_prefix_and_derivation_path_from_suri(raw_string).unwrap();
 		assert_eq!(expected_prefix, root_secret);
 		assert_eq!(expected_derivation_path, derivation_path);
 	}
+
+	#[rstest]
+	#[case(
+		String::from("//Alice//Bob/soft1/soft2"),
+		None,
+		Some(String::from("//Alice//Bob/soft1/soft2"))
+	)]
+	#[case(String::from(""), None, None)]
+	#[case(String::from("//0"), None, Some(String::from("//0")))]
+	fn test_parse_prefix_and_derivation_path_from_suri_works_with_empty_prefix_phrase(
+		#[case] raw_string: String,
+		#[case] expected_prefix: Option<String>,
+		#[case] expected_derivation_path: Option<String>,
+	) {
+		let (root_secret, derivation_path) =
+			parse_prefix_and_derivation_path_from_suri(raw_string).unwrap();
+		assert_eq!(expected_prefix, root_secret);
+		assert_eq!(expected_derivation_path, derivation_path);
+	}
+
+	#[rstest]
+	#[case(
+        String::from(
+            "bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice//Bob/soft1/soft2///password"
+        ),
+    )]
+	#[case(String::from(
+		"bottom drive obey lake curtain smoke basket hold race lonely fit walk///password"
+	))]
+	#[case(String::from(
+		"bottom drive obey lake curtain smoke basket hold race lonely fit walk///"
+	))]
+	#[case(
+        String::from(
+            "bottom drive obey lake curtain smoke basket hold race lonely fit walk///password//NotDerivations//Still/password/part"
+        ),
+    )]
+	fn test_parse_prefix_and_derivation_path_from_suri_does_not_allow_password(
+		#[case] raw_string: String,
+	) {
+		let result = parse_prefix_and_derivation_path_from_suri(raw_string);
+		match result.unwrap_err() {
+			GcliError::Input(err) => {
+				println!("Error message: {}", err);
+				assert!(
+					err.starts_with("Having a password in the derivation path is not supported")
+				);
+			}
+			other => panic!("Should have been an Input error; got: {:?}", other),
+		}
+	}
+
+	mod vault_accounts_table_tests {
+		use crate::commands::vault::compute_vault_accounts_table;
+		use crate::entities::vault_account::tests::account_tree_node_tests::mother_account_tree_node;
+		use indoc::indoc;
+
+		#[test]
+		fn test_compute_vault_accounts_table_empty() {
+			let table = compute_vault_accounts_table(&[]).unwrap();
+
+			let expected_table = indoc! {r#"
+			┌─────────────────────────────────────────────┐
+			│ SS58 Address   Format   Account/Path   Name │
+			╞═════════════════════════════════════════════╡
+			└─────────────────────────────────────────────┘"#
+			};
+
+			assert_eq!(table.to_string(), expected_table);
+		}
+
+		#[test]
+		fn test_compute_vault_accounts_table() {
+			let account_tree_node = mother_account_tree_node();
+
+			let table = compute_vault_accounts_table(&[account_tree_node]).unwrap();
+
+			let expected_table = indoc! {r#"
+			┌──────────────────────────────────────────────────────────────────────────────────────────────────┐
+			│ SS58 Address                                           Format      Account/Path   Name           │
+			╞══════════════════════════════════════════════════════════════════════════════════════════════════╡
+			│ 5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV       substrate   <Base>         Mother         │
+			│ ├─5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH                 //0            Child 1        │
+			│ │ ├─5Fh5PLQNt1xuEXm71dfDtQdnwceSew4oHewWBLsWAkKspV7d               //0            Grandchild 1   │
+			│ ├─5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o                 //1            <Mother//1>    │
+			│ │ ├─5CvdJuB9HLXSi5FS9LW57cyHF13iCv5HDimo2C45KxnxriCT               //1            <Mother//1//1> │
+			└──────────────────────────────────────────────────────────────────────────────────────────────────┘"#
+			};
+
+			assert_eq!(table.to_string(), expected_table);
+		}
+
+		#[test]
+		fn test_compute_vault_accounts_table_partial() {
+			let mother = mother_account_tree_node();
+			let child1 = mother.borrow().children[0].clone();
+
+			let table = compute_vault_accounts_table(&[child1]).unwrap();
+
+			let expected_table = indoc! {r#"
+			┌─────────────────────────────────────────────────────────────────────────────────────────────┐
+			│ SS58 Address                                           Format   Account/Path   Name         │
+			╞═════════════════════════════════════════════════════════════════════════════════════════════╡
+			│ ├─5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH              //0            Child 1      │
+			│ │ ├─5Fh5PLQNt1xuEXm71dfDtQdnwceSew4oHewWBLsWAkKspV7d            //0            Grandchild 1 │
+			└─────────────────────────────────────────────────────────────────────────────────────────────┘"#
+			};
+
+			assert_eq!(table.to_string(), expected_table);
+		}
+	}
 }
diff --git a/src/conf.rs b/src/conf.rs
index 207e7c68b7ac8998e3c3240224eae52b01312b97..509e0c5bb9dfe36fb572a65eb6f0fff823b28546 100644
--- a/src/conf.rs
+++ b/src/conf.rs
@@ -1,4 +1,5 @@
-use crate::entities::vault_derivation;
+use crate::entities::vault_account;
+use crate::entities::vault_account::DbAccountId;
 use crate::*;
 use serde::{Deserialize, Serialize};
 
@@ -85,13 +86,13 @@ 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(derivation) = vault_derivation::fetch_vault_derivation(
+				if let Some(account) = vault_account::find_by_id(
 					data.connect_db(),
-					account_id.to_string().as_str(),
+					&DbAccountId::from(account_id.clone()),
 				)
 				.await?
 				{
-					println!("(Vault: {})", derivation);
+					println!("(Vault: {})", account);
 				}
 			}
 		}
diff --git a/src/database.rs b/src/database.rs
index fad4f71741268c3e75d24406fc388d37f127c47f..01ef735bf4e05a0e9b2f02e3f3e923d76860c76c 100644
--- a/src/database.rs
+++ b/src/database.rs
@@ -1,4 +1,4 @@
-use crate::entities::{vault_account, vault_derivation};
+use crate::entities::vault_account;
 use crate::utils::GcliError;
 use sea_orm::sea_query::IndexCreateStatement;
 use sea_orm::{ConnectionTrait, Database, DatabaseConnection, Schema};
@@ -32,7 +32,6 @@ pub async fn initialize_db(db_url: &str) -> Result<DatabaseConnection, GcliError
 	let schema = Schema::new(db.get_database_backend());
 
 	create_table_if_not_exists(&db, &schema, vault_account::Entity).await?;
-	create_table_if_not_exists(&db, &schema, vault_derivation::Entity).await?;
 
 	Ok(db)
 }
diff --git a/src/entities.rs b/src/entities.rs
index 2de801c7093606cdc3cdf62a8d85fe2b60955309..bd92a81f604c6e8f5756ee435b59dc62af75b50e 100644
--- a/src/entities.rs
+++ b/src/entities.rs
@@ -1,2 +1 @@
 pub mod vault_account;
-pub mod vault_derivation;
diff --git a/src/entities/vault_account.rs b/src/entities/vault_account.rs
index 76d34635165606c7c3d4522aa974d4ffe6b1af46..71905ec368d410630e4c5bedb9560e072b1098b9 100644
--- a/src/entities/vault_account.rs
+++ b/src/entities/vault_account.rs
@@ -1,106 +1,1225 @@
-use crate::inputs;
+use crate::commands::vault;
 use crate::utils::GcliError;
+use anyhow::anyhow;
+use sea_orm::prelude::async_trait::async_trait;
 use sea_orm::prelude::StringLen;
 use sea_orm::ActiveValue::Set;
+use sea_orm::FromJsonQueryResult;
+use sea_orm::QueryFilter;
 use sea_orm::{
-	ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, EnumIter, Related, RelationDef,
-	RelationTrait,
+	ActiveModelBehavior, ColumnTrait, DbErr, DeriveEntityModel, DerivePrimaryKey, EnumIter, Linked,
+	ModelTrait, QueryOrder, RelationDef, RelationTrait, TryFromU64,
 };
 use sea_orm::{ActiveModelTrait, ConnectionTrait, PrimaryKeyTrait};
 use sea_orm::{DeriveActiveEnum, EntityTrait};
+use serde::{Deserialize, Serialize};
+use std::cell::RefCell;
+use std::collections::HashMap;
 use std::fmt::Display;
+use std::future::Future;
+use std::pin::Pin;
+use std::rc::Rc;
+use std::str::FromStr;
+use subxt::utils::AccountId32;
 
 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
 #[sea_orm(table_name = "vault_account")]
 pub struct Model {
-	/// SS58 Address of (root) account
+	/// SS58 Address of account
 	#[sea_orm(primary_key, auto_increment = false)]
-	pub address: String,
-	pub crypto_type: CryptoType,
-	pub encrypted_suri: Vec<u8>,
+	pub address: DbAccountId,
+	/// Optional name for the account
+	#[sea_orm(unique)]
+	pub name: Option<String>,
+	/// derivation path - None if for a "base" account that has `crypto_scheme` and `encrypted_suri` set and no `parent`
+	pub path: Option<String>,
+	/// Crypto scheme used for the account - Only set for "base" accounts
+	pub crypto_scheme: Option<DbCryptoScheme>,
+	/// Encrypted SURI for the account - Only set for "base" accounts
+	pub encrypted_suri: Option<Vec<u8>>,
+	/// ForeignKey to parent vault_account SS58 Address - None if for a "base" account
+	pub parent: Option<DbAccountId>,
+}
+
+impl Model {
+	pub fn is_base_account(&self) -> bool {
+		self.parent.is_none()
+	}
+
+	#[allow(unused)]
+	pub fn is_derivation_account(&self) -> bool {
+		self.parent.is_some()
+	}
+
+	pub fn account_type(&self) -> String {
+		if self.is_base_account() {
+			"Base".to_string()
+		} else {
+			"Derivation".to_string()
+		}
+	}
 }
 
 impl Display for Model {
 	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-		write!(f, "[address:\"{}\"]", self.address)
+		if self.is_base_account() {
+			write!(
+				f,
+				"{}[address:{}, name:{:?}, crypto_scheme:{:?}]",
+				self.account_type(),
+				self.address,
+				self.name,
+				self.crypto_scheme
+			)
+		} else {
+			fn get_parent_name(parent: &Option<DbAccountId>) -> String {
+				if let Some(parent) = parent {
+					format!("Some(\"{parent}\")")
+				} else {
+					"None".to_string()
+				}
+			}
+
+			write!(
+				f,
+				"{}[address:{}, name:{:?}, path:{:?}, parent:{}]",
+				self.account_type(),
+				self.address,
+				self.name,
+				self.path,
+				get_parent_name(&self.parent)
+			)
+		}
 	}
 }
 
-#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
+/// Necessary to create a wrapper over AccountId32 to implement sea-orm traits
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, FromJsonQueryResult)]
+pub struct DbAccountId(pub AccountId32);
+
+impl FromStr for DbAccountId {
+	type Err = GcliError;
+
+	fn from_str(s: &str) -> Result<Self, Self::Err> {
+		AccountId32::from_str(s)
+			.map(DbAccountId)
+			.map_err(|_| GcliError::Input("Invalid AccountId32 format".to_string()))
+	}
+}
+
+impl Display for DbAccountId {
+	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+		write!(f, "{}", self.0)
+	}
+}
+
+impl From<AccountId32> for DbAccountId {
+	fn from(account_id32: AccountId32) -> Self {
+		DbAccountId(account_id32)
+	}
+}
+
+impl From<DbAccountId> for AccountId32 {
+	fn from(db_account_id: DbAccountId) -> Self {
+		db_account_id.0
+	}
+}
+
+impl From<String> for DbAccountId {
+	fn from(s: String) -> Self {
+		DbAccountId(AccountId32::from_str(&s).expect("Invalid AccountId32 format"))
+	}
+}
+
+/// sea-orm forces us to implement this one; but since we map from/to string, we can't convert from a u64
+impl TryFromU64 for DbAccountId {
+	fn try_from_u64(_v: u64) -> Result<Self, DbErr> {
+		Err(DbErr::Custom(
+			"AccountIdWrapper cannot be created from U64".to_owned(),
+		))
+	}
+}
+
+/// Didn't want to pollute the keys::CryptoScheme enum with sea-orm specific derivations
+///
+/// created a separate enum for the database with conversions between the two
+#[derive(Debug, Copy, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
 #[sea_orm(
 	rs_type = "String",
 	db_type = "String(StringLen::None)",
 	rename_all = "PascalCase"
 )]
-pub enum CryptoType {
-	/// The BIP39 mnemonic phrase (?12 words) (SR25519)
-	Bip39Mnemonic,
-	/// The 32B hexadecimal seed with "0x" prefix (64+2 characters when unencrypted) (SR25519)
-	EntropyKdfSeed,
-	/// The 32B hexadecimal seed with "0x" prefix for cesium v1 (64+2 characters when unencrypted) (ED25519)
-	G1v1Seed,
+pub enum DbCryptoScheme {
+	Ed25519,
+	Sr25519,
+}
+
+impl From<crate::keys::CryptoScheme> for DbCryptoScheme {
+	fn from(scheme: crate::keys::CryptoScheme) -> Self {
+		match scheme {
+			crate::keys::CryptoScheme::Ed25519 => DbCryptoScheme::Ed25519,
+			crate::keys::CryptoScheme::Sr25519 => DbCryptoScheme::Sr25519,
+		}
+	}
+}
+
+impl From<DbCryptoScheme> for crate::keys::CryptoScheme {
+	fn from(scheme: DbCryptoScheme) -> Self {
+		match scheme {
+			DbCryptoScheme::Ed25519 => crate::keys::CryptoScheme::Ed25519,
+			DbCryptoScheme::Sr25519 => crate::keys::CryptoScheme::Sr25519,
+		}
+	}
 }
 
 #[derive(Copy, Clone, Debug, EnumIter)]
 pub enum Relation {
-	Derivation,
+	ParentAccount,
 }
 
 impl RelationTrait for Relation {
 	fn def(&self) -> RelationDef {
 		match self {
-			Self::Derivation => Entity::has_many(super::vault_derivation::Entity).into(),
+			Self::ParentAccount => Entity::belongs_to(Entity)
+				.from(Column::Parent)
+				.to(Column::Address)
+				.into(),
+		}
+	}
+}
+
+pub struct ParentAccountLink;
+
+impl Linked for ParentAccountLink {
+	type FromEntity = Entity;
+
+	type ToEntity = Entity;
+
+	fn link(&self) -> Vec<RelationDef> {
+		vec![Relation::ParentAccount.def()]
+	}
+}
+
+#[async_trait]
+impl ActiveModelBehavior for ActiveModel {
+	/// This method is called before saving or updating the model to the database.
+	/// It ensures that the model is valid according to the following constraints:
+	///
+	/// - A "base" vault account must have path:None, parent:None, crypto_scheme:Some(_), encrypted_suri:Some(_)
+	/// - A "derivation" vault account must have path:Some(_), parent:Some(_), crypto_scheme:None, encrypted_suri:None
+	async fn before_save<C>(self, _db: &C, insert: bool) -> Result<Self, DbErr>
+	where
+		C: ConnectionTrait,
+	{
+		if insert {
+			// If one of the elements of a "base" account is seen, all must be correctly filled
+			if (self.path.is_not_set() || self.path.try_as_ref().unwrap().is_none())
+				|| (self.parent.is_not_set() || self.parent.try_as_ref().unwrap().is_none())
+				|| (self.crypto_scheme.is_set()
+					&& self.crypto_scheme.try_as_ref().unwrap().is_some())
+				|| (self.encrypted_suri.is_set()
+					&& self.encrypted_suri.try_as_ref().unwrap().is_some())
+			{
+				if !((self.path.is_not_set() || self.path.try_as_ref().unwrap().is_none())
+					&& (self.parent.is_not_set() || self.parent.try_as_ref().unwrap().is_none())
+					&& (self.crypto_scheme.is_set()
+						&& self.crypto_scheme.try_as_ref().unwrap().is_some())
+					&& (self.encrypted_suri.is_set()
+						&& self.encrypted_suri.try_as_ref().unwrap().is_some()))
+				{
+					return Err(DbErr::Custom(
+					"A \"base\" vault account must have path:None, parent:None, crypto_scheme:Some(_), encrypted_suri:Some(_)".into(),
+				));
+				}
+			} else if !((self.path.is_set() && self.path.try_as_ref().unwrap().is_some())
+				&& (self.parent.is_set() && self.parent.try_as_ref().unwrap().is_some())
+				&& (self.crypto_scheme.is_not_set()
+					|| self.crypto_scheme.try_as_ref().unwrap().is_none())
+				&& (self.encrypted_suri.is_not_set()
+					|| self.encrypted_suri.try_as_ref().unwrap().is_none()))
+			{
+				return Err(DbErr::Custom(
+					"A \"derivation\" vault account must have path:Some(_), parent:Some(_), crypto_scheme:None, encrypted_suri:None".into(),
+				));
+			}
+		} else {
+			//Update checks
+			if !(self.crypto_scheme.is_unchanged()
+				&& self.encrypted_suri.is_unchanged()
+				&& self.path.is_unchanged()
+				&& self.parent.is_unchanged()
+				&& self.address.is_unchanged())
+			{
+				return Err(DbErr::Custom(
+					"Only the name can be updated for a vault account".into(),
+				));
+			}
+		}
+
+		Ok(self)
+	}
+}
+
+pub async fn find_by_id<C>(db: &C, address: &DbAccountId) -> Result<Option<Model>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	Entity::find_by_id(address.clone())
+		.one(db)
+		.await
+		.map_err(GcliError::from)
+}
+
+pub async fn find_by_name<C>(db: &C, name: &str) -> Result<Option<Model>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	Entity::find()
+		.filter(Column::Name.eq(Some(name.to_string())))
+		.one(db)
+		.await
+		.map_err(GcliError::from)
+}
+
+pub async fn find_base_accounts<C>(db: &C) -> Result<Vec<Model>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	Entity::find()
+		.filter(Column::Path.is_null())
+		.order_by_asc(Column::Address)
+		.all(db)
+		.await
+		.map_err(GcliError::from)
+}
+
+/// Represents a node in the hierarchy of accounts
+pub struct AccountTreeNode {
+	pub account: Model,
+	pub children: Vec<Rc<RefCell<AccountTreeNode>>>,
+	pub parent: Option<Rc<RefCell<AccountTreeNode>>>,
+}
+
+/// Counts the depth of an `AccountTreeNode` in the hierarchy.
+pub fn count_depth_account_tree_node(account_tree_node: &Rc<RefCell<AccountTreeNode>>) -> usize {
+	let mut depth = 0;
+	let mut current_node = Rc::clone(account_tree_node);
+
+	while let Some(parent_node) = {
+		let borrowed_node = current_node.borrow();
+		borrowed_node.parent.as_ref().map(Rc::clone)
+	} {
+		depth += 1;
+		current_node = parent_node;
+	}
+
+	depth
+}
+
+/// Counts number of accounts in an `AccountTreeNode` hierarchy, starting from account_tree_node and only visiting children.
+pub fn count_accounts_in_account_tree_node_and_children(
+	node: &Rc<RefCell<AccountTreeNode>>,
+) -> usize {
+	let borrowed_node = node.borrow();
+	let mut count = 1; // Count the current node
+
+	for child in &borrowed_node.children {
+		count += count_accounts_in_account_tree_node_and_children(child);
+	}
+
+	count
+}
+
+/// Gets the base account tree node of the `AccountTreeNode` hierarchy.
+pub fn get_base_account_tree_node(
+	account_tree_node: &Rc<RefCell<AccountTreeNode>>,
+) -> Rc<RefCell<AccountTreeNode>> {
+	//Move up to the base node
+	let mut base_node = Rc::clone(account_tree_node);
+	while let Some(parent_node) = {
+		let borrowed_node = base_node.borrow();
+		borrowed_node.parent.as_ref().map(Rc::clone)
+	} {
+		base_node = parent_node;
+	}
+
+	Rc::clone(&base_node)
+}
+
+/// Gets the account tree node for given address from the `AccountTreeNode` hierarchy.
+pub fn get_account_tree_node_for_address(
+	account_tree_node: &Rc<RefCell<AccountTreeNode>>,
+	address: &str,
+) -> Rc<RefCell<AccountTreeNode>> {
+	fn find_address_recursive(
+		node: &Rc<RefCell<AccountTreeNode>>,
+		address: &str,
+	) -> Option<Rc<RefCell<AccountTreeNode>>> {
+		let borrowed_node = node.borrow();
+
+		if borrowed_node.account.address.to_string() == address {
+			return Some(Rc::clone(node));
+		}
+
+		for child in &borrowed_node.children {
+			if let Some(found) = find_address_recursive(child, address) {
+				return Some(found);
+			}
+		}
+
+		None
+	}
+
+	//Move up to the base node
+	let base_account_tree_node = get_base_account_tree_node(account_tree_node);
+
+	let account_tree_node_for_address = find_address_recursive(&base_account_tree_node, address)
+		.unwrap_or_else(|| {
+			panic!(
+				"Could not find account with address:{} in the hierarchy",
+				address
+			)
+		});
+
+	Rc::clone(&account_tree_node_for_address)
+}
+
+/// Returns a vec of all the accounts starting from `account_tree_node` and all its children; depth first
+///
+/// Can be used to delete all the accounts in the hierarchy in the proper order
+pub fn extract_accounts_depth_first_from_account_tree_node(
+	account_tree_node: &Rc<RefCell<AccountTreeNode>>,
+) -> Result<Vec<Model>, GcliError> {
+	fn retrieve_recursive_depth_first(
+		node: &Rc<RefCell<AccountTreeNode>>,
+		accounts: &mut Vec<Model>,
+	) -> Result<(), GcliError> {
+		let borrowed_node = node.borrow();
+
+		for child in &borrowed_node.children {
+			retrieve_recursive_depth_first(child, accounts)?;
+		}
+
+		accounts.push(borrowed_node.account.clone());
+
+		Ok(())
+	}
+
+	let mut accounts = Vec::new();
+	retrieve_recursive_depth_first(account_tree_node, &mut accounts)?;
+
+	Ok(accounts)
+}
+
+/// Computes the name to reference the `AccountTreeNode` in the hierarchy if we can find/compute one.
+///
+/// Returns `None` otherwise.
+pub fn compute_name_account_tree_node(
+	account_tree_node: &Rc<RefCell<AccountTreeNode>>,
+) -> Option<String> {
+	let mut name = String::new();
+	let mut current_node = Rc::clone(account_tree_node);
+
+	while let Some(parent_node) = {
+		let borrowed_node = current_node.borrow();
+		if let Some(account_name) = &borrowed_node.account.name {
+			name.insert_str(0, account_name);
+			return Some(name);
+		} else if let Some(account_path) = &borrowed_node.account.path {
+			name.insert_str(0, account_path);
+		} else {
+			return None;
+		}
+		borrowed_node.parent.as_ref().map(Rc::clone)
+	} {
+		current_node = parent_node;
+	}
+
+	Some(name)
+}
+
+/// Computes a map of names to reference of `AccountTreeNodes` in the hierarchy of its children.
+pub fn compute_name_map_for_account_tree_node(
+	account_tree_node: &Rc<RefCell<AccountTreeNode>>,
+) -> Result<HashMap<String, Rc<RefCell<AccountTreeNode>>>, GcliError> {
+	let mut names_to_ref_map = HashMap::<String, Rc<RefCell<AccountTreeNode>>>::new();
+
+	fn compute_recursive_name_map(
+		node: &Rc<RefCell<AccountTreeNode>>,
+		current_name: Option<String>,
+		names_to_ref_map: &mut HashMap<String, Rc<RefCell<AccountTreeNode>>>,
+	) -> Result<(), GcliError> {
+		let borrowed_node = node.borrow();
+
+		let current_name = match &borrowed_node.account.name {
+			Some(name) => Some(name.clone()),
+			None => match &borrowed_node.account.path {
+				Some(path) => current_name
+					.as_ref()
+					.map(|name| format!("{}{}", name, path)),
+				None => None,
+			},
+		};
+
+		if let Some(name) = &current_name {
+			names_to_ref_map.insert(name.clone(), Rc::clone(node));
 		}
+
+		for child in &borrowed_node.children {
+			compute_recursive_name_map(child, current_name.clone(), names_to_ref_map)?;
+		}
+
+		Ok(())
+	}
+
+	compute_recursive_name_map(account_tree_node, None, &mut names_to_ref_map)?;
+
+	Ok(names_to_ref_map)
+}
+
+/// Computes the SURI of the `AccountTreeNode` in the hierarchy; using the password to decrypt the encrypted SURI of Base account.
+pub fn compute_suri_account_tree_node(
+	account_tree_node: &Rc<RefCell<AccountTreeNode>>,
+	password: String,
+) -> Result<String, GcliError> {
+	let mut suri = String::new();
+	let mut current_node = Rc::clone(account_tree_node);
+
+	while let Some(parent_node) = {
+		let borrowed_node = current_node.borrow();
+
+		if let Some(account_path) = &borrowed_node.account.path {
+			suri.insert_str(0, account_path);
+		} else if let Some(encrypted_suri) = &borrowed_node.account.encrypted_suri {
+			let decrypted_suri = vault::decrypt(encrypted_suri, password.clone()).unwrap();
+			let secret_suri = String::from_utf8(decrypted_suri).map_err(|e| anyhow!(e))?;
+			suri.insert_str(0, &secret_suri);
+		} else {
+			return Err(GcliError::Input("No encrypted SURI found".to_string()));
+		}
+
+		borrowed_node.parent.as_ref().map(Rc::clone)
+	} {
+		current_node = parent_node;
+	}
+
+	Ok(suri)
+}
+
+/// Fetches all the `base` account tree nodes with their hierarchies
+pub async fn fetch_all_base_account_tree_node_hierarchies<C>(
+	db: &C,
+) -> Result<Vec<Rc<RefCell<AccountTreeNode>>>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	let base_accounts = find_base_accounts(db).await?;
+
+	let mut account_tree_nodes = Vec::new();
+
+	for base_account in base_accounts {
+		let base_account_tree_node =
+			fetch_children_account_tree_nodes_boxed(db, base_account, None).await?;
+		account_tree_nodes.push(base_account_tree_node);
+	}
+
+	Ok(account_tree_nodes)
+}
+
+/// Only returns the `base` accounts without their children
+///
+/// To be used in compute_vault_accounts_table to display only the base accounts
+pub async fn fetch_only_base_account_tree_nodes<C>(
+	db: &C,
+) -> Result<Vec<Rc<RefCell<AccountTreeNode>>>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	let base_accounts = find_base_accounts(db).await?;
+
+	let mut account_tree_nodes = Vec::new();
+
+	for base_account in base_accounts {
+		let current_node = Rc::new(RefCell::new(AccountTreeNode {
+			account: base_account.clone(),
+			children: Vec::new(),
+			parent: None,
+		}));
+
+		account_tree_nodes.push(current_node);
 	}
+
+	Ok(account_tree_nodes)
 }
 
-// `Related` trait has to be implemented by hand
-impl Related<super::vault_derivation::Entity> for Entity {
-	fn to() -> RelationDef {
-		Relation::Derivation.def()
+/// Fetches the `base` account tree node hierarchy for the given address
+///
+/// This one unwraps the Option and gives a proper error message in case of None
+pub async fn fetch_base_account_tree_node_hierarchy_unwrapped<C>(
+	db: &C,
+	//FIXME Ripple parameter type to &AccountId in all methods (instead of &str)
+	address: &str,
+) -> Result<Rc<RefCell<AccountTreeNode>>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	fetch_base_account_tree_node_hierarchy(db, address)
+		.await?
+		.ok_or(GcliError::Input(format!(
+			"Could not compute tree of accounts for address:'{}'",
+			address
+		)))
+}
+
+/// Fetches the `base` account tree node hierarchy for the given address using db
+pub async fn fetch_base_account_tree_node_hierarchy<C>(
+	db: &C,
+	//FIXME Ripple parameter type to &AccountId in all methods (instead of &str)
+	address: &str,
+) -> Result<Option<Rc<RefCell<AccountTreeNode>>>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	if let Some(base_parent_account) = find_base_parent_account(db, address).await? {
+		let base_account_tree_node =
+			fetch_children_account_tree_nodes_boxed(db, base_parent_account, None).await?;
+		Ok(Some(base_account_tree_node))
+	} else {
+		Ok(None)
+	}
+}
+
+/// Finds the `base` account in db for the given address
+async fn find_base_parent_account<C>(db: &C, address: &str) -> Result<Option<Model>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	let account = find_by_id(
+		db,
+		&DbAccountId::from_str(address).expect("invalid address"),
+	)
+	.await?;
+
+	if account.is_none() {
+		return Ok(None);
+	}
+
+	let mut base_parent_account = account.unwrap();
+
+	while let Some(parent_account) = base_parent_account
+		.find_linked(ParentAccountLink)
+		.one(db)
+		.await
+		.map_err(GcliError::from)?
+	{
+		base_parent_account = parent_account;
 	}
+
+	Ok(Some(base_parent_account))
 }
 
-impl ActiveModelBehavior for ActiveModel {}
+async fn find_direct_children_accounts<C>(
+	db: &C,
+	current_account: &Model,
+) -> Result<Vec<Model>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	Entity::find()
+		.filter(Column::Parent.eq(current_account.address.clone()))
+		.order_by_asc(Column::Address)
+		.all(db)
+		.await
+		.map_err(GcliError::from)
+}
+
+/// To make clippy happy... "warning: very complex type used. Consider factoring parts into `type` definitions"
+type AccountTreeNodeResult<'c> = Pin<Box<dyn Future<Output = Result<Rc<RefCell<AccountTreeNode>>, GcliError>> + 'c>>;
+
+/// This one seems necessary in order to handle async + recursion issue
+///
+/// Was suggested by AI and seems to work (might be improved)
+fn fetch_children_account_tree_nodes_boxed<'c, C>(
+	db: &'c C,
+	current_account: Model,
+	parent_node: Option<Rc<RefCell<AccountTreeNode>>>,
+) -> AccountTreeNodeResult<'c>
+where
+	C: ConnectionTrait + 'c,
+{
+	Box::pin(fetch_children_account_tree_nodes(
+		db,
+		current_account,
+		parent_node,
+	))
+}
 
-/// Creates a (root) vault account (if it doesn't exist) and returns it
-pub async fn create_vault_account<C>(
+/// Fetches the children account tree nodes for the given account and parent node
+async fn fetch_children_account_tree_nodes<C>(
+	db: &C,
+	current_account: Model,
+	parent_node: Option<Rc<RefCell<AccountTreeNode>>>,
+) -> Result<Rc<RefCell<AccountTreeNode>>, GcliError>
+where
+	C: ConnectionTrait,
+{
+	let children_accounts = find_direct_children_accounts(db, &current_account).await?;
+
+	let current_node = Rc::new(RefCell::new(AccountTreeNode {
+		account: current_account.clone(),
+		children: Vec::new(),
+		parent: parent_node,
+	}));
+
+	let mut children = Vec::new();
+
+	for child_account in children_accounts {
+		let child_node = fetch_children_account_tree_nodes_boxed(
+			db,
+			child_account,
+			Some(Rc::clone(&current_node)),
+		)
+		.await?;
+		children.push(child_node);
+	}
+
+	current_node.borrow_mut().children = children;
+
+	Ok(current_node)
+}
+
+/// Creates a `base` vault account (if it doesn't exist) and returns it
+///
+/// Typically used for `vault import|migrate` commands
+pub async fn create_base_account<C>(
 	db: &C,
 	address: &str,
-	crypto_type: CryptoType,
+	name: Option<&String>,
+	crypto_scheme: crate::keys::CryptoScheme,
 	encrypted_suri: Vec<u8>,
 ) -> Result<Model, GcliError>
 where
 	C: ConnectionTrait,
 {
-	let vault_account = Entity::find_by_id(address.to_owned()).one(db).await?;
+	let account_id = DbAccountId::from_str(address)?;
+	let vault_account = Entity::find_by_id(account_id.clone()).one(db).await?;
 
 	Ok(match vault_account {
 		Some(vault_account) => {
-			println!("Already existing vault account {vault_account}");
-
-			let overwrite_key =
-				inputs::confirm_action("Do you want to overwrite with the new encrypted key ?")?;
-			if overwrite_key {
-				let mut vault_account: ActiveModel = vault_account.into();
-				vault_account.encrypted_suri = Set(encrypted_suri);
-				let vault_account = vault_account.update(db).await?;
-				println!("Updated vault account {vault_account}");
-
-				vault_account
-			} else {
-				vault_account
-			}
+			//TODO Later possibly allow to replace the entry
+			return Err(GcliError::Input(format!(
+				"Already existing vault account {vault_account}"
+			)));
+
+			// let overwrite_key =
+			// 	inputs::confirm_action("Do you want to overwrite with the new encrypted key ?")?;
+			// if overwrite_key {
+			// 	let mut vault_account: ActiveModel = vault_account.into();
+			// 	vault_account.encrypted_suri = Set(Some(encrypted_suri));
+			// 	let vault_account = vault_account.update(db).await?;
+			// 	println!("Updated vault account {vault_account}");
+			//
+			// 	vault_account
+			// } else {
+			// 	vault_account
+			// }
+		}
+		None => {
+			let vault_account = ActiveModel {
+				address: Set(account_id),
+				name: Set(name.cloned()),
+				path: Set(None),
+				crypto_scheme: Set(Some(crypto_scheme.into())),
+				encrypted_suri: Set(Some(encrypted_suri)),
+				parent: Default::default(),
+			};
+			let vault_account = vault_account.insert(db).await?;
+			println!("Created base account {}", vault_account);
+			vault_account
+		}
+	})
+}
+
+/// Creates a `derivation` vault account (if it doesn't exist) and returns it
+///
+/// Typically used for `vault derive` command
+pub async fn create_derivation_account<C>(
+	db: &C,
+	address: &str,
+	name: Option<&String>,
+	derivation_path: &str,
+	parent_address: &str,
+) -> Result<Model, GcliError>
+where
+	C: ConnectionTrait,
+{
+	let account_id = DbAccountId::from_str(address)?;
+	let vault_account = Entity::find_by_id(account_id.clone()).one(db).await?;
+
+	Ok(match vault_account {
+		Some(vault_account) => {
+			//TODO Later possibly allow to replace the entry
+			return Err(GcliError::Input(format!(
+				"Already existing vault account {vault_account}"
+			)));
 		}
 		None => {
 			let vault_account = ActiveModel {
-				address: Set(address.to_owned()),
-				crypto_type: Set(crypto_type),
-				encrypted_suri: Set(encrypted_suri),
+				address: Set(address.to_string().into()),
+				name: Set(name.cloned()),
+				path: Set(Some(derivation_path.to_string())),
+				crypto_scheme: Set(None),
+				encrypted_suri: Set(None),
+				parent: Set(Some(parent_address.to_string().into())),
 			};
 			let vault_account = vault_account.insert(db).await?;
-			println!("Created vault account {}", vault_account);
+			println!("Created derivation account {}", vault_account);
 			vault_account
 		}
 	})
 }
+
+pub async fn update_account_name<C>(
+	db: &C,
+	account: Model,
+	new_name: Option<&String>,
+) -> Result<Model, GcliError>
+where
+	C: ConnectionTrait,
+{
+	let old_name = account.name.clone();
+	let mut account: ActiveModel = account.into();
+	account.name = Set(new_name.cloned());
+	let account = account.update(db).await?;
+	println!(
+		"Renamed address:'{}' from {:?} to {:?}",
+		&account.address, old_name, new_name
+	);
+
+	Ok(account)
+}
+
+// Unit tests
+#[cfg(test)]
+pub mod tests {
+	use super::*;
+	pub mod account_tree_node_tests {
+		use super::*;
+		use crate::commands::vault;
+		use crate::keys::SUBSTRATE_MNEMONIC;
+
+		pub fn mother_account_tree_node() -> Rc<RefCell<AccountTreeNode>> {
+			let mother_address =
+				DbAccountId::from_str("5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqRzV").unwrap();
+			let child1_address =
+				DbAccountId::from_str("5D34dL5prEUaGNQtPPZ3yN5Y6BnkfXunKXXz6fo7ZJbLwRRH").unwrap();
+			let grandchild1_address =
+				DbAccountId::from_str("5Fh5PLQNt1xuEXm71dfDtQdnwceSew4oHewWBLsWAkKspV7d").unwrap();
+			let child2_address =
+				DbAccountId::from_str("5GBNeWRhZc2jXu7D55rBimKYDk8PGk8itRYFTPfC8RJLKG5o").unwrap();
+			let grandchild2_address =
+				DbAccountId::from_str("5CvdJuB9HLXSi5FS9LW57cyHF13iCv5HDimo2C45KxnxriCT").unwrap();
+
+			let grandchild1 = Rc::new(RefCell::new(AccountTreeNode {
+				account: Model {
+					address: grandchild1_address.clone(),
+					name: Some("Grandchild 1".to_string()),
+					path: Some("//0".to_string()),
+					crypto_scheme: None,
+					encrypted_suri: None,
+					parent: Some(child1_address.clone()),
+				},
+				children: vec![],
+				parent: None,
+			}));
+
+			let grandchild2 = Rc::new(RefCell::new(AccountTreeNode {
+				account: Model {
+					address: grandchild2_address.clone(),
+					// name: Some("Grandchild 2".to_string()),
+					name: None,
+					path: Some("//1".to_string()),
+					crypto_scheme: None,
+					encrypted_suri: None,
+					parent: Some(child2_address.clone()),
+				},
+				children: vec![],
+				parent: None,
+			}));
+
+			let child1 = Rc::new(RefCell::new(AccountTreeNode {
+				account: Model {
+					address: child1_address.clone(),
+					name: Some("Child 1".to_string()),
+					path: Some("//0".to_string()),
+					crypto_scheme: None,
+					encrypted_suri: None,
+					parent: Some(mother_address.clone()),
+				},
+				children: vec![grandchild1.clone()],
+				parent: None,
+			}));
+
+			let child2 = Rc::new(RefCell::new(AccountTreeNode {
+				account: Model {
+					address: child2_address.clone(),
+					// name: Some("Child 2".to_string()),
+					name: None,
+					path: Some("//1".to_string()),
+					crypto_scheme: None,
+					encrypted_suri: None,
+					parent: Some(mother_address.clone()),
+				},
+				children: vec![grandchild2.clone()],
+				parent: None,
+			}));
+
+			let mother = Rc::new(RefCell::new(AccountTreeNode {
+				account: Model {
+					address: mother_address.clone(),
+					name: Some("Mother".to_string()),
+					path: None,
+					crypto_scheme: Some(DbCryptoScheme::Sr25519),
+					encrypted_suri: Some(
+						vault::encrypt(SUBSTRATE_MNEMONIC.as_bytes(), "".to_string()).unwrap(),
+					),
+					parent: None,
+				},
+				children: vec![child1.clone(), child2.clone()],
+				parent: None,
+			}));
+
+			// Set parent references
+			grandchild1.borrow_mut().parent = Some(child1.clone());
+			grandchild2.borrow_mut().parent = Some(child2.clone());
+			child1.borrow_mut().parent = Some(mother.clone());
+			child2.borrow_mut().parent = Some(mother.clone());
+
+			mother
+		}
+
+		#[test]
+		fn test_count_depth_account_tree_node() {
+			let mother = mother_account_tree_node();
+			assert_eq!(count_depth_account_tree_node(&mother), 0);
+
+			let child1 = mother.borrow().children[0].clone();
+			assert_eq!(count_depth_account_tree_node(&child1), 1);
+
+			let grandchild1 = child1.borrow().children[0].clone();
+			assert_eq!(count_depth_account_tree_node(&grandchild1), 2);
+		}
+
+		#[test]
+		fn test_count_accounts_in_account_tree_node_and_children() {
+			let mother = mother_account_tree_node();
+			assert_eq!(count_accounts_in_account_tree_node_and_children(&mother), 5);
+
+			let child1 = mother.borrow().children[0].clone();
+			assert_eq!(count_accounts_in_account_tree_node_and_children(&child1), 2);
+
+			let grandchild1 = child1.borrow().children[0].clone();
+			assert_eq!(
+				count_accounts_in_account_tree_node_and_children(&grandchild1),
+				1
+			);
+		}
+
+		#[test]
+		fn test_retrieve_accounts_depth_first_from_account_tree_node() {
+			let mother = mother_account_tree_node();
+			let accounts = extract_accounts_depth_first_from_account_tree_node(&mother).unwrap();
+			assert_eq!(accounts.len(), 5);
+			assert_eq!(
+				accounts[0].address.to_string(),
+				mother.borrow().children[0].borrow().children[0]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+			assert_eq!(
+				accounts[1].address.to_string(),
+				mother.borrow().children[0]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+			assert_eq!(
+				accounts[2].address.to_string(),
+				mother.borrow().children[1].borrow().children[0]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+			assert_eq!(
+				accounts[3].address.to_string(),
+				mother.borrow().children[1]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+			assert_eq!(
+				accounts[4].address.to_string(),
+				mother.borrow().account.address.to_string()
+			);
+
+			let child1 = mother.borrow().children[0].clone();
+			let accounts = extract_accounts_depth_first_from_account_tree_node(&child1).unwrap();
+			assert_eq!(accounts.len(), 2);
+			assert_eq!(
+				accounts[0].address.to_string(),
+				mother.borrow().children[0].borrow().children[0]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+			assert_eq!(
+				accounts[1].address.to_string(),
+				mother.borrow().children[0]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+
+			let grandchild1 = child1.borrow().children[0].clone();
+			let accounts =
+				extract_accounts_depth_first_from_account_tree_node(&grandchild1).unwrap();
+			assert_eq!(accounts.len(), 1);
+			assert_eq!(
+				accounts[0].address.to_string(),
+				mother.borrow().children[0].borrow().children[0]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+		}
+
+		#[test]
+		fn test_compute_name_account_tree_node() {
+			let mother = mother_account_tree_node();
+			assert_eq!(
+				compute_name_account_tree_node(&mother),
+				Some("Mother".to_string())
+			);
+
+			let child1 = mother.borrow().children[0].clone();
+			assert_eq!(
+				compute_name_account_tree_node(&child1),
+				Some("Child 1".to_string())
+			);
+
+			let grandchild1 = child1.borrow().children[0].clone();
+			assert_eq!(
+				compute_name_account_tree_node(&grandchild1),
+				Some("Grandchild 1".to_string())
+			);
+
+			let child2 = mother.borrow().children[1].clone();
+			assert_eq!(
+				compute_name_account_tree_node(&child2),
+				Some("Mother//1".to_string())
+			);
+
+			let grandchild2 = child2.borrow().children[0].clone();
+			assert_eq!(
+				compute_name_account_tree_node(&grandchild2),
+				Some("Mother//1//1".to_string())
+			);
+		}
+
+		#[test]
+		fn test_compute_name_account_tree_node_mother_without_name() {
+			let mother = mother_account_tree_node();
+			mother.borrow_mut().account.name = None;
+			assert_eq!(compute_name_account_tree_node(&mother), None);
+
+			let child1 = mother.borrow().children[0].clone();
+			assert_eq!(
+				compute_name_account_tree_node(&child1),
+				Some("Child 1".to_string())
+			);
+
+			let grandchild1 = child1.borrow().children[0].clone();
+			assert_eq!(
+				compute_name_account_tree_node(&grandchild1),
+				Some("Grandchild 1".to_string())
+			);
+
+			let child2 = mother.borrow().children[1].clone();
+			assert_eq!(compute_name_account_tree_node(&child2), None);
+
+			let grandchild2 = child2.borrow().children[0].clone();
+			assert_eq!(compute_name_account_tree_node(&grandchild2), None);
+		}
+
+		#[test]
+		fn test_compute_name_account_tree_node_grandchild1_without_name() {
+			let mother = mother_account_tree_node();
+			mother.borrow().children[0].borrow().children[0]
+				.borrow_mut()
+				.account
+				.name = None;
+
+			assert_eq!(
+				compute_name_account_tree_node(&mother),
+				Some("Mother".to_string())
+			);
+
+			let child1 = mother.borrow().children[0].clone();
+			assert_eq!(
+				compute_name_account_tree_node(&child1),
+				Some("Child 1".to_string())
+			);
+
+			let grandchild1 = child1.borrow().children[0].clone();
+			assert_eq!(
+				compute_name_account_tree_node(&grandchild1),
+				Some("Child 1//0".to_string())
+			);
+		}
+
+		#[test]
+		fn test_compute_name_map_for_account_tree_node() {
+			let mother = mother_account_tree_node();
+			let name_map = compute_name_map_for_account_tree_node(&mother).unwrap();
+			assert_eq!(name_map.len(), 5);
+			assert_eq!(
+				name_map
+					.get("Mother")
+					.unwrap()
+					.borrow()
+					.account
+					.address
+					.to_string(),
+				mother.borrow().account.address.to_string()
+			);
+			assert_eq!(
+				name_map
+					.get("Child 1")
+					.unwrap()
+					.borrow()
+					.account
+					.address
+					.to_string(),
+				mother.borrow().children[0]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+			assert_eq!(
+				name_map
+					.get("Grandchild 1")
+					.unwrap()
+					.borrow()
+					.account
+					.address
+					.to_string(),
+				mother.borrow().children[0].borrow().children[0]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+			assert_eq!(
+				name_map
+					.get("Mother//1")
+					.unwrap()
+					.borrow()
+					.account
+					.address
+					.to_string(),
+				mother.borrow().children[1]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+			assert_eq!(
+				name_map
+					.get("Mother//1//1")
+					.unwrap()
+					.borrow()
+					.account
+					.address
+					.to_string(),
+				mother.borrow().children[1].borrow().children[0]
+					.borrow()
+					.account
+					.address
+					.to_string()
+			);
+		}
+
+		#[test]
+		fn test_get_base_account_tree_node() {
+			let mother = mother_account_tree_node();
+			let child1 = mother.borrow().children[0].clone();
+			let grandchild1 = child1.borrow().children[0].clone();
+			assert_eq!(
+				get_base_account_tree_node(&grandchild1)
+					.borrow()
+					.account
+					.address
+					.to_string(),
+				mother.borrow().account.address.to_string()
+			);
+		}
+
+		#[test]
+		fn test_get_account_tree_node_for_address() {
+			let mother = mother_account_tree_node();
+			let grandchild1 = mother.borrow().children[0].borrow().children[0].clone();
+			let grandchild1_address = &grandchild1.borrow().account.address.to_string();
+			assert_eq!(
+				get_account_tree_node_for_address(&mother, grandchild1_address)
+					.borrow()
+					.account
+					.address
+					.to_string(),
+				grandchild1_address.to_string()
+			);
+		}
+
+		#[test]
+		fn test_compute_suri_account_tree_node() {
+			let mother = mother_account_tree_node();
+			let password = "".to_string();
+			assert_eq!(
+				compute_suri_account_tree_node(&mother, password.clone()).unwrap(),
+				SUBSTRATE_MNEMONIC
+			);
+
+			let child1 = mother.borrow().children[0].clone();
+			assert_eq!(
+				compute_suri_account_tree_node(&child1, password.clone()).unwrap(),
+				SUBSTRATE_MNEMONIC.to_string() + "//0"
+			);
+
+			let grandchild1 = child1.borrow().children[0].clone();
+			assert_eq!(
+				compute_suri_account_tree_node(&grandchild1, password.clone()).unwrap(),
+				SUBSTRATE_MNEMONIC.to_string() + "//0//0"
+			);
+
+			let child2 = mother.borrow().children[1].clone();
+			assert_eq!(
+				compute_suri_account_tree_node(&child2, password.clone()).unwrap(),
+				SUBSTRATE_MNEMONIC.to_string() + "//1"
+			);
+
+			let grandchild2 = child2.borrow().children[0].clone();
+			assert_eq!(
+				compute_suri_account_tree_node(&grandchild2, password.clone()).unwrap(),
+				SUBSTRATE_MNEMONIC.to_string() + "//1//1"
+			);
+		}
+	}
+}
diff --git a/src/entities/vault_derivation.rs b/src/entities/vault_derivation.rs
deleted file mode 100644
index 6851b151a13f3ae05b6ace9135bbedbe5de96697..0000000000000000000000000000000000000000
--- a/src/entities/vault_derivation.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-use crate::utils::GcliError;
-use sea_orm::sea_query::NullOrdering;
-use sea_orm::ActiveValue::Set;
-use sea_orm::PrimaryKeyTrait;
-use sea_orm::QueryFilter;
-use sea_orm::{
-	ActiveModelBehavior, DeriveEntityModel, DerivePrimaryKey, EnumIter, Related, RelationDef,
-	RelationTrait,
-};
-use sea_orm::{ActiveModelTrait, ColumnTrait};
-use sea_orm::{ConnectionTrait, EntityTrait, Order, QueryOrder};
-use std::fmt::Display;
-
-#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
-#[sea_orm(table_name = "vault_derivation")]
-pub struct Model {
-	/// SS58 Address
-	#[sea_orm(primary_key, auto_increment = false)]
-	pub address: String,
-	/// Optional name for the derivation
-	#[sea_orm(unique)]
-	pub name: Option<String>,
-	/// derivation path - None if for the root account
-	pub path: Option<String>,
-	/// ForeignKey to root vault_account SS58 Address
-	pub root_address: String,
-}
-
-impl Display for Model {
-	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-		if self.path.is_none() {
-			write!(
-				f,
-				"Account[address:\"{}\", name:{:?}]",
-				self.address, self.name
-			)
-		} else {
-			write!(
-				f,
-				"Derivation[address:\"{}\", name:{:?}, path:{:?}, account_address:\"{}\"]",
-				self.address, self.name, self.path, self.root_address
-			)
-		}
-	}
-}
-
-#[derive(Copy, Clone, Debug, EnumIter)]
-pub enum Relation {
-	RootAccount,
-}
-
-impl RelationTrait for Relation {
-	fn def(&self) -> RelationDef {
-		match self {
-			Self::RootAccount => Entity::belongs_to(super::vault_account::Entity)
-				.from(Column::RootAddress)
-				.to(super::vault_account::Column::Address)
-				.into(),
-		}
-	}
-}
-
-// `Related` trait has to be implemented by hand
-impl Related<super::vault_account::Entity> for Entity {
-	fn to() -> RelationDef {
-		Relation::RootAccount.def()
-	}
-}
-
-impl ActiveModelBehavior for ActiveModel {}
-
-/// Creates a root derivation (if it doesn't exist) and returns it
-pub async fn create_root_vault_derivation<C>(
-	db: &C,
-	root_address: &str,
-	root_name: Option<&String>,
-) -> Result<Model, GcliError>
-where
-	C: ConnectionTrait,
-{
-	create_vault_derivation(db, root_address, root_address, root_name, None).await
-}
-
-/// Creates a derivation (if it doesn't exist) and returns it
-pub async fn create_vault_derivation<C>(
-	db: &C,
-	address: &str,
-	root_address: &str,
-	name: Option<&String>,
-	path: Option<&String>,
-) -> Result<Model, GcliError>
-where
-	C: ConnectionTrait,
-{
-	let derivation = Entity::find_by_id(root_address.to_owned()).one(db).await?;
-
-	Ok(match derivation {
-		Some(derivation) => derivation,
-		None => {
-			let derivation = ActiveModel {
-				address: Set(address.to_owned()),
-				name: Set(name.cloned()),
-				path: Set(path.cloned()),
-				root_address: Set(root_address.to_owned()),
-			};
-			derivation.insert(db).await?
-		}
-	})
-}
-
-pub async fn fetch_vault_derivation<C>(db: &C, address: &str) -> Result<Option<Model>, GcliError>
-where
-	C: ConnectionTrait,
-{
-	Entity::find_by_id(address.to_owned())
-		.one(db)
-		.await
-		.map_err(GcliError::from)
-}
-
-pub async fn fetch_all_linked_derivations_in_order<C>(
-	db: &C,
-	root_address: &str,
-) -> Result<Vec<Model>, GcliError>
-where
-	C: ConnectionTrait,
-{
-	Entity::find()
-		.filter(Column::RootAddress.eq(root_address.to_owned()))
-		.order_by_with_nulls(Column::Path, Order::Asc, NullOrdering::First)
-		.all(db)
-		.await
-		.map_err(GcliError::from)
-}
-
-pub async fn list_all_derivations_in_order<C>(db: &C) -> Result<Vec<Model>, GcliError>
-where
-	C: ConnectionTrait,
-{
-	Entity::find()
-		.order_by_asc(Column::RootAddress)
-		.order_by_with_nulls(Column::Path, Order::Asc, NullOrdering::First)
-		.all(db)
-		.await
-		.map_err(GcliError::from)
-}
-
-pub async fn list_all_root_derivations_in_order<C>(db: &C) -> Result<Vec<Model>, GcliError>
-where
-	C: ConnectionTrait,
-{
-	Entity::find()
-		.filter(Column::Path.is_null())
-		.order_by_asc(Column::RootAddress)
-		.all(db)
-		.await
-		.map_err(GcliError::from)
-}
diff --git a/src/inputs.rs b/src/inputs.rs
index 31c53925bbbb21a55577eee5bc4d134265a1dd92..103ba3b293194937810b44ce9ef05ed7b506e8f2 100644
--- a/src/inputs.rs
+++ b/src/inputs.rs
@@ -1,5 +1,6 @@
+use crate::commands::vault;
 use crate::utils::GcliError;
-use inquire::validator::Validation;
+use inquire::validator::{ErrorMessage, Validation};
 
 pub fn prompt_password() -> Result<String, GcliError> {
 	prompt_password_query("Password")
@@ -75,7 +76,16 @@ pub fn prompt_vault_derivation_path() -> Result<String, GcliError> {
 					"derivation path needs to start with one or more '/'".into(),
 				))
 			} else {
-				Ok(Validation::Valid)
+				match vault::parse_prefix_and_derivation_path_from_suri(input.to_string()) {
+					Ok(_) => Ok(Validation::Valid),
+					Err(error) => {
+						if let GcliError::Input(message) = error {
+							Ok(Validation::Invalid(ErrorMessage::from(message)))
+						} else {
+							Ok(Validation::Invalid("Unknown error".into()))
+						}
+					}
+				}
 			}
 		})
 		.prompt()
diff --git a/src/keys.rs b/src/keys.rs
index 73c230d6fb9ccc6903ee74814c548bb015384e2a..63ffbbf0dec322423ef629c563dad77be4e296fd 100644
--- a/src/keys.rs
+++ b/src/keys.rs
@@ -49,10 +49,29 @@ impl From<SecretFormat> for OsStr {
 	}
 }
 
+/// The crypto scheme to use - partial copy from sc_cli::arg_enums::CryptoScheme
+///
+/// Preferred making a copy since adding the dependency to sc-cli brings more than 300 dependencies
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum CryptoScheme {
+	/// Use ed25519 - used for SecretFormat::Cesium
+	Ed25519,
+	/// Use sr25519.
+	Sr25519,
+}
+
 /// wrapper type for keys + signature
 //FIXME check if it's ok to keep large enum variant
 // Sr25519 second-largest variant contains at least 256 bytes
 // Ed25519 largest variant contains at least 480 bytes
+//FIXME
+// Replace by CryptoScheme from sc_cli?? or CryptoType from sp-core ?
+// sc_cli::arg_enums::CryptoScheme (enum)
+//   CryptoScheme::Ed25519
+//   CryptoScheme::Sr25519
+//   CryptoScheme::Ecdsa
+// sp_core::crypto::CryptoType (trait)
+// /home/valpha/.cargo/git/checkouts/duniter-polkadot-sdk-0889f590ced4a269/bcc60f3/substrate/primitives/core/src/crypto.rs:1012
 #[allow(clippy::large_enum_variant)]
 pub enum KeyPair {
 	Sr25519(Sr25519Pair),
@@ -178,9 +197,10 @@ pub fn prompt_secret_substrate() -> Sr25519Pair {
 
 pub fn prompt_secret_substrate_and_compute_keypair() -> (String, Sr25519Pair) {
 	loop {
-		let mnemonic_suri = rpassword::prompt_password("Mnemonic: ").unwrap();
-		match pair_from_sr25519_str(&mnemonic_suri) {
-			Ok(pair) => return (mnemonic_suri, pair),
+		println!("Substrate URI can be a mnemonic or a mini-secret ('0x' prefixed seed) together with optional derivation path");
+		let substrate_suri = inputs::prompt_password_query("Substrate URI: ").unwrap();
+		match pair_from_sr25519_str(&substrate_suri) {
+			Ok(pair) => return (substrate_suri, pair),
 			Err(_) => println!("Invalid secret"),
 		}
 	}
@@ -193,8 +213,8 @@ pub fn prompt_secret_cesium() -> Ed25519Pair {
 }
 
 pub fn prompt_secret_cesium_and_compute_keypair() -> (String, Ed25519Pair) {
-	let id = rpassword::prompt_password("Cesium id: ").unwrap();
-	let pwd = rpassword::prompt_password("Cesium password: ").unwrap();
+	let id = inputs::prompt_password_query("Cesium id: ").unwrap();
+	let pwd = inputs::prompt_password_query("Cesium password: ").unwrap();
 
 	let seed = seed_from_cesium(&id, &pwd);
 	let secret_suri = format!("0x{}", hex::encode(seed));
@@ -230,7 +250,7 @@ pub fn prompt_predefined() -> Sr25519Pair {
 }
 
 pub fn prompt_predefined_and_compute_keypair() -> (String, Sr25519Pair) {
-	let deriv = rpassword::prompt_password("Enter derivation path: ").unwrap();
+	let deriv = inputs::prompt_password_query("Enter derivation path: ").unwrap();
 	(
 		predefined_mnemonic(&deriv),
 		pair_from_predefined(&deriv).expect("invalid secret"),
@@ -285,6 +305,123 @@ fn catch_known(address: &str) -> Option<&str> {
 #[cfg(test)]
 mod tests {
 	use super::*;
+
+	mod subkey_like_tests {
+		use super::keys::SUBSTRATE_MNEMONIC;
+
+		use sp_core::crypto::Ss58Codec;
+		use sp_core::crypto::{Ss58AddressFormat, Ss58AddressFormatRegistry};
+		use sp_core::ByteArray;
+		use sp_runtime::traits::IdentifyAccount;
+		use sp_runtime::MultiSigner;
+
+		#[test]
+		fn test_print_from_suri() {
+			// sc_cli::CryptoSchemeFlag::augment_args()
+
+			let suri_str = SUBSTRATE_MNEMONIC.to_string();
+			print_from_suri::<sp_core::sr25519::Pair>(&suri_str, None, None);
+			print_from_suri::<sp_core::sr25519::Pair>(
+				&suri_str,
+				None,
+				Some(Ss58AddressFormatRegistry::G1Account.into()),
+			);
+
+			// bottom drive obey lake curtain smoke basket hold race lonely fit walk//Alice
+			let suri_str = SUBSTRATE_MNEMONIC.to_string() + "//Alice";
+			print_from_suri::<sp_core::sr25519::Pair>(&suri_str, None, None);
+			print_from_suri::<sp_core::sr25519::Pair>(
+				&suri_str,
+				None,
+				Some(Ss58AddressFormatRegistry::G1Account.into()),
+			);
+		}
+
+		/// print account information from suri - simplification of code from
+		/// sc_cli::commands::utils::print_from_uri
+		pub fn print_from_suri<Pair>(
+			uri: &str,
+			password: Option<&str>,
+			network_override: Option<Ss58AddressFormat>,
+		) where
+			Pair: sp_core::Pair,
+			Pair::Public: Into<MultiSigner>,
+		{
+			let network_id = String::from(unwrap_or_default_ss58_version(network_override));
+
+			if let Ok((pair, seed)) = Pair::from_string_with_seed(uri, password) {
+				let public_key = pair.public();
+				let network_override = unwrap_or_default_ss58_version(network_override);
+
+				println!(
+					"Secret Key URI `{}` is account:\n  \
+					Network ID:        {}\n  \
+					Secret seed:       {}\n  \
+					Public key (hex):  {}\n  \
+					Account ID:        {}\n  \
+					Public key (SS58): {}\n  \
+					SS58 Address:      {}",
+					uri,
+					network_id,
+					if let Some(seed) = seed {
+						// sc_cli::utils::format_seed::<Pair>(seed)
+						format!("0x{}", hex::encode(seed.as_ref()))
+					} else {
+						"n/a".into()
+					},
+					format_public_key::<Pair>(public_key.clone()),
+					format_account_id::<Pair>(public_key.clone()),
+					public_key.to_ss58check_with_version(network_override),
+					pair.public()
+						.into()
+						.into_account()
+						.to_ss58check_with_version(network_override),
+				);
+			} else {
+				println!("Invalid phrase/URI given");
+			}
+		}
+
+		/// Public key type for Runtime
+		pub type PublicFor<P> = <P as sp_core::Pair>::Public;
+
+		/// formats public key as hex
+		fn format_public_key<P: sp_core::Pair>(public_key: PublicFor<P>) -> String {
+			format!("0x{}", hex::encode((&public_key.as_ref())))
+		}
+
+		/// formats public key as accountId as hex
+		fn format_account_id<P: sp_core::Pair>(public_key: PublicFor<P>) -> String
+		where
+			PublicFor<P>: Into<MultiSigner>,
+		{
+			format!(
+				"0x{}",
+				hex::encode(&public_key.into().into_account().as_slice())
+			)
+		}
+
+		pub fn unwrap_or_default_ss58_version(
+			network: Option<Ss58AddressFormat>,
+		) -> Ss58AddressFormat {
+			network.unwrap_or_else(default_ss58_version)
+		}
+
+		pub fn default_ss58_version() -> Ss58AddressFormat {
+			DEFAULT_VERSION
+				.load(core::sync::atomic::Ordering::Relaxed)
+				.into()
+		}
+
+		static DEFAULT_VERSION: core::sync::atomic::AtomicU16 = core::sync::atomic::AtomicU16::new(
+			from_known_address_format(Ss58AddressFormatRegistry::SubstrateAccount),
+		);
+
+		pub const fn from_known_address_format(x: Ss58AddressFormatRegistry) -> u16 {
+			x as u16
+		}
+	}
+
 	mod substrate {
 		use super::*;