Skip to main content

bittensor_wallet/
wallet.rs

1use colored::Colorize;
2use std::path::PathBuf;
3use std::{env, fmt};
4
5use crate::config::Config;
6use crate::constants::{BT_WALLET_HOTKEY, BT_WALLET_NAME, BT_WALLET_PATH};
7use crate::errors::*;
8use crate::keyfile::Keyfile;
9use crate::keypair::Keypair;
10use crate::utils::{self, is_valid_bittensor_address_or_public_key};
11
12/// Display the mnemonic and a warning message to keep the mnemonic safe.
13///
14/// ```text
15///     Arguments:
16///         mnemonic (str): The mnemonic phrase to display.
17///         key_type (str): The type of key e.g. "coldkey" or "hotkey".
18/// ```
19pub fn display_mnemonic_msg(mnemonic: String, key_type: &str) {
20    utils::print(format!("{}", "\nIMPORTANT: Store this mnemonic in a secure (preferable offline place), as anyone who has possession of this mnemonic can use it to regenerate the key and access your tokens.\n".red()));
21
22    utils::print(format!(
23        "\nThe mnemonic to the new {} is: {}",
24        key_type.blue(),
25        mnemonic.green()
26    ));
27    utils::print(format!(
28        "\nYou can use the mnemonic to recreate the key with `{}` in case it gets lost.\n",
29        "btcli".green()
30    ));
31}
32
33#[derive(Clone)]
34pub struct Wallet {
35    pub name: String,
36    pub path: String,
37    pub hotkey: String,
38
39    _path: PathBuf,
40
41    _coldkey: Option<Keypair>,
42    _coldkeypub: Option<Keypair>,
43    _hotkey: Option<Keypair>,
44    _hotkeypub: Option<Keypair>,
45}
46
47impl fmt::Display for Wallet {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        write!(
50            f,
51            "Wallet (Name: '{:}', Hotkey: '{:}', Path: '{:}')",
52            self.name, self.hotkey, self.path
53        )
54    }
55}
56
57impl fmt::Debug for Wallet {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        write!(
60            f,
61            "name: '{:?}', hotkey: '{:?}', path: '{:?}'",
62            self.name, self.hotkey, self.path
63        )
64    }
65}
66
67impl Wallet {
68    /// Initialize the bittensor wallet object containing a hot and coldkey.
69    ///
70    /// ```text
71    ///     Arguments:
72    ///         name (Optional[str]): The name of the wallet. Defaults to "default".
73    ///         hotkey (Optional[str]): The name of hotkey. Defaults to "default".
74    ///         path (Optional[str]): The path to wallets. Defaults to "~/.bittensor/wallets/".
75    ///         config (Optional[Config]): Optional configuration.
76    /// ```
77    ///
78    /// ```text
79    ///     Returns:
80    ///         `Wallet` - A new Wallet instance.
81    /// ```
82    pub fn new(
83        name: Option<String>,
84        hotkey: Option<String>,
85        path: Option<String>,
86        config: Option<Config>,
87    ) -> Self {
88        let final_name = name
89            .or(config.as_ref().map(|conf| conf.name()))
90            .unwrap_or(BT_WALLET_NAME.to_string());
91
92        let final_hotkey = hotkey
93            .or(config.as_ref().map(|conf| conf.hotkey()))
94            .unwrap_or(BT_WALLET_HOTKEY.to_string());
95
96        let final_path = path
97            .or(config.as_ref().map(|conf| conf.path()))
98            .unwrap_or(BT_WALLET_PATH.to_string());
99
100        let expanded_path = PathBuf::from(shellexpand::tilde(&final_path).to_string());
101
102        Wallet {
103            name: final_name,
104            hotkey: final_hotkey,
105            path: final_path,
106            _path: expanded_path,
107            _coldkey: None,
108            _coldkeypub: None,
109            _hotkey: None,
110            _hotkeypub: None,
111        }
112    }
113
114    /// Get default config
115    pub fn config() -> Config {
116        Config::new(None, None, None)
117    }
118
119    /// Print help information
120    pub fn help() -> Config {
121        unimplemented!()
122    }
123
124    /// Registers the wallet's `--wallet.name`, `--wallet.hotkey`, and `--wallet.path`
125    /// arguments on the given clap command, seeded from the `BT_WALLET_NAME`,
126    /// `BT_WALLET_HOTKEY`, and `BT_WALLET_PATH` environment variables when set.
127    ///
128    /// ```text
129    ///     Arguments:
130    ///         parser (clap::Command): The clap command to extend.
131    ///         _prefix (Option<&str>): Reserved for future argument-prefix support (see inline TODO).
132    ///     Returns:
133    ///         parser (clap::Command): The same command with the wallet arguments added.
134    /// ```
135    // TODO: What are the prefixes for ?
136    pub fn add_args(parser: clap::Command, _prefix: Option<&str>) -> clap::Command {
137        let default_name =
138            env::var("BT_WALLET_NAME").unwrap_or_else(|_| BT_WALLET_NAME.to_string());
139        let default_name_static: &'static str = Box::leak(default_name.into_boxed_str());
140
141        let parser = parser.arg(
142            clap::Arg::new("wallet.name")
143                .long("wallet.name")
144                .default_value(default_name_static)
145                .help("The name of the wallet to unlock for running Bittensor"),
146        );
147
148        let default_hotkey =
149            env::var("BT_WALLET_HOTKEY").unwrap_or_else(|_| BT_WALLET_HOTKEY.to_string());
150        let default_hotkey_static: &'static str = Box::leak(default_hotkey.into_boxed_str());
151
152        let parser = parser.arg(
153            clap::Arg::new("wallet.hotkey")
154                .long("wallet.hotkey")
155                .default_value(default_hotkey_static)
156                .help("The name of the wallet's hotkey"),
157        );
158
159        let default_path =
160            env::var("BT_WALLET_PATH").unwrap_or_else(|_| BT_WALLET_PATH.to_string());
161        let default_path_static: &'static str = Box::leak(default_path.into_boxed_str());
162
163        parser.arg(
164            clap::Arg::new("wallet.path")
165                .long("wallet.path")
166                .default_value(default_path_static)
167                .help("The path to your Bittensor wallets"),
168        )
169    }
170
171    /// Checks for existing coldkeypub, hotkeypub, hotkeys, and creates them if non-existent.
172    ///
173    /// ```text
174    ///     Arguments:
175    ///         coldkey_use_password (bool): Whether to use a password for coldkey. Defaults to ``True``.
176    ///         hotkey_use_password (bool): Whether to use a password for hotkey. Defaults to ``False``.
177    ///         save_coldkey_to_env (bool): Whether to save a coldkey password to local env. Defaults to ``False``.
178    ///         save_hotkey_to_env (bool): Whether to save a hotkey password to local env. Defaults to ``False``.
179    ///         coldkey_password (Optional[str]): Coldkey password for encryption. Defaults to ``None``. If `coldkey_password` is passed, then `coldkey_use_password` is automatically ``True``.
180    ///         hotkey_password (Optional[str]): Hotkey password for encryption. Defaults to ``None``. If `hotkey_password` is passed, then `hotkey_use_password` is automatically ``True``.
181    ///         overwrite (bool): Whether to overwrite an existing keys. Defaults to ``False``.
182    ///         suppress (bool): If ``True``, suppresses the display of the keys mnemonic message. Defaults to ``False``.
183    /// ```
184    ///
185    /// ```text
186    ///     Returns:
187    ///         `Wallet` - The wallet instance with created keys.
188    ///     Raises:
189    ///         WalletError: If key generation or file operations fail.
190    /// ```
191    #[allow(clippy::too_many_arguments)]
192    pub fn create_if_non_existent(
193        &mut self,
194        coldkey_use_password: bool,
195        hotkey_use_password: bool,
196        save_coldkey_to_env: bool,
197        save_hotkey_to_env: bool,
198        coldkey_password: Option<String>,
199        hotkey_password: Option<String>,
200        overwrite: bool,
201        suppress: bool,
202        coldkey_crypto_type: u8,
203        hotkey_crypto_type: u8,
204    ) -> Result<Self, WalletError> {
205        self.create(
206            coldkey_use_password,
207            hotkey_use_password,
208            save_coldkey_to_env,
209            save_hotkey_to_env,
210            coldkey_password,
211            hotkey_password,
212            overwrite,
213            suppress,
214            coldkey_crypto_type,
215            hotkey_crypto_type,
216        )
217    }
218
219    /// Checks for existing coldkeypub and hotkeys, and creates them if non-existent.
220    ///
221    /// ```text
222    ///     Arguments:
223    ///         coldkey_use_password (bool): Whether to use a password for coldkey. Defaults to ``True``.
224    ///         hotkey_use_password (bool): Whether to use a password for hotkey. Defaults to ``False``.
225    ///         save_coldkey_to_env (bool): Whether to save a coldkey password to local env. Defaults to ``False``.
226    ///         save_hotkey_to_env (bool): Whether to save a hotkey password to local env. Defaults to ``False``.
227    ///         coldkey_password (Optional[str]): Coldkey password for encryption. Defaults to ``None``. If `coldkey_password` is passed, then `coldkey_use_password` is automatically ``True``.
228    ///         hotkey_password (Optional[str]): Hotkey password for encryption. Defaults to ``None``. If `hotkey_password` is passed, then `hotkey_use_password` is automatically ``True``.
229    ///         overwrite (bool): Whether to overwrite an existing keys. Defaults to ``False``.
230    ///         suppress (bool): If ``True``, suppresses the display of the keys mnemonic message. Defaults to ``False``.
231    /// ```
232    ///
233    /// ```text
234    ///     Returns:
235    ///         Wallet instance with created keys.
236    /// ```
237    ///
238    /// ```text
239    ///     Raises:
240    ///         WalletError: If key generation or file operations fail.
241    /// ```
242    #[allow(clippy::bool_comparison, clippy::too_many_arguments)]
243    pub fn create(
244        &mut self,
245        coldkey_use_password: bool,
246        hotkey_use_password: bool,
247        save_coldkey_to_env: bool,
248        save_hotkey_to_env: bool,
249        coldkey_password: Option<String>,
250        hotkey_password: Option<String>,
251        overwrite: bool,
252        suppress: bool,
253        coldkey_crypto_type: u8,
254        hotkey_crypto_type: u8,
255    ) -> Result<Self, WalletError> {
256        if overwrite
257            || (!self.coldkey_file()?.exists_on_device()?
258                && !self.coldkeypub_file()?.exists_on_device()?)
259        {
260            self.create_new_coldkey(
261                12,
262                coldkey_use_password,
263                overwrite,
264                suppress,
265                save_coldkey_to_env,
266                coldkey_password,
267                coldkey_crypto_type,
268            )?;
269        } else {
270            println!("ColdKey for the wallet '{}' already exists.", self.name);
271        }
272
273        if overwrite
274            || !self.hotkey_file()?.exists_on_device()?
275                && !self.hotkeypub_file()?.exists_on_device()?
276        {
277            self.create_new_hotkey(
278                12,
279                hotkey_use_password,
280                overwrite,
281                suppress,
282                save_hotkey_to_env,
283                hotkey_password,
284                hotkey_crypto_type,
285            )?;
286        } else {
287            println!("HotKey for the wallet '{}' already exists.", self.name);
288        }
289
290        Ok(self.clone())
291    }
292
293    /// Checks for existing coldkeypub and hotkeys, and recreates them if non-existent.
294    ///
295    /// ```text
296    ///     Arguments:
297    ///         coldkey_use_password (bool): Whether to use a password for coldkey. Defaults to ``True``.
298    ///         hotkey_use_password (bool): Whether to use a password for hotkey. Defaults to ``False``.
299    ///         save_coldkey_to_env (bool): Whether to save a coldkey password to local env. Defaults to ``False``.
300    ///         save_hotkey_to_env (bool): Whether to save a hotkey password to local env. Defaults to ``False``.
301    ///         coldkey_password (Optional[str]): Coldkey password for encryption. Defaults to ``None``. If `coldkey_password` is passed, then `coldkey_use_password` is automatically ``True``.
302    ///         hotkey_password (Optional[str]): Hotkey password for encryption. Defaults to ``None``. If `hotkey_password` is passed, then `hotkey_use_password` is automatically ``True``.
303    ///         overwrite (bool): Whether to overwrite an existing keys. Defaults to ``False``.
304    ///         suppress (bool): If ``True``, suppresses the display of the keys mnemonic message. Defaults to ``False``.
305    /// ```
306    ///
307    /// ```text
308    ///     Returns:
309    ///         Wallet instance with created keys.
310    /// ```
311    ///
312    /// ```text
313    ///     Raises:
314    ///         WalletError: If key generation or file operations fail.
315    /// ```
316    #[allow(clippy::too_many_arguments)]
317    pub fn recreate(
318        &mut self,
319        coldkey_use_password: bool,
320        hotkey_use_password: bool,
321        save_coldkey_to_env: bool,
322        save_hotkey_to_env: bool,
323        coldkey_password: Option<String>,
324        hotkey_password: Option<String>,
325        overwrite: bool,
326        suppress: bool,
327        coldkey_crypto_type: u8,
328        hotkey_crypto_type: u8,
329    ) -> Result<Self, WalletError> {
330        self.create_new_coldkey(
331            12,
332            coldkey_use_password,
333            overwrite,
334            suppress,
335            save_coldkey_to_env,
336            coldkey_password,
337            coldkey_crypto_type,
338        )?;
339        self.create_new_hotkey(
340            12,
341            hotkey_use_password,
342            overwrite,
343            suppress,
344            save_hotkey_to_env,
345            hotkey_password,
346            hotkey_crypto_type,
347        )?;
348
349        Ok(self.clone())
350    }
351
352    /// Returns the hotkey file.
353    pub fn hotkey_file(&self) -> Result<Keyfile, KeyFileError> {
354        self.create_hotkey_file(false)
355    }
356
357    /// Creates a new hotkey file for the keypair.
358    ///
359    /// ```text
360    ///     Arguments:
361    ///         save_hotkey_to_env (bool): Whether to save hotkey password to local env. Defaults to ``False``.
362    /// ```
363    ///
364    /// ```text
365    ///     Returns:
366    ///         `Keyfile` - The created hotkey file.
367    ///     Raises:
368    ///         KeyFileError: If file creation fails.
369    /// ```
370    pub fn create_hotkey_file(&self, save_hotkey_to_env: bool) -> Result<Keyfile, KeyFileError> {
371        // concatenate wallet path
372        let wallet_path = self._path.join(&self.name);
373
374        // concatenate hotkey path
375        let hotkey_path = wallet_path.join("hotkeys").join(&self.hotkey);
376
377        Keyfile::new(
378            hotkey_path.to_string_lossy().into_owned(),
379            Some(self.hotkey.clone()),
380            save_hotkey_to_env,
381        )
382    }
383
384    /// Returns the coldkey file.
385    pub fn coldkey_file(&self) -> Result<Keyfile, KeyFileError> {
386        self.create_coldkey_file(false)
387    }
388
389    /// Creates a new coldkey file for the keypair.
390    ///
391    /// ```text
392    ///     Arguments:
393    ///         save_coldkey_to_env (bool): Whether to save coldkey password to local env. Defaults to ``False``.
394    /// ```
395    ///
396    /// ```text
397    ///     Returns:
398    ///         `Keyfile` - The created coldkey file.
399    ///     Raises:
400    ///         KeyFileError: If file creation fails.
401    /// ```
402    pub fn create_coldkey_file(&self, save_coldkey_to_env: bool) -> Result<Keyfile, KeyFileError> {
403        // concatenate wallet path
404        let wallet_path = PathBuf::from(&self._path).join(&self.name);
405
406        // concatenate coldkey path
407        let coldkey_path = wallet_path.join("coldkey");
408        Keyfile::new(
409            coldkey_path.to_string_lossy().into_owned(),
410            Some("coldkey".to_string()),
411            save_coldkey_to_env,
412        )
413    }
414
415    /// Returns the hotkeypub file.
416    pub fn hotkeypub_file(&self) -> Result<Keyfile, KeyFileError> {
417        // concatenate wallet path
418        let wallet_path = self._path.join(&self.name);
419        let hotkeypub_name = format!("{}pub.txt", self.hotkey);
420        // concatenate hotkeypub path
421        let hotkeypub_path = wallet_path.join("hotkeys").join(&hotkeypub_name);
422
423        Keyfile::new(
424            hotkeypub_path.to_string_lossy().into_owned(),
425            Some(hotkeypub_name),
426            false,
427        )
428    }
429
430    /// Returns the coldkeypub file.
431    pub fn coldkeypub_file(&self) -> Result<Keyfile, KeyFileError> {
432        // concatenate wallet path
433        let wallet_path = self._path.join(&self.name);
434
435        // concatenate hotkey path
436        let coldkeypub_path = wallet_path.join("coldkeypub.txt");
437
438        Keyfile::new(
439            coldkeypub_path.to_string_lossy().into_owned(),
440            Some("coldkeypub.txt".to_string()),
441            false,
442        )
443    }
444
445    /// Returns the coldkey from wallet.path/wallet.name/coldkey or raises an error.
446    pub fn coldkey_property(&self) -> Result<Keypair, KeyFileError> {
447        if let Some(coldkey) = &self._coldkey {
448            Ok(coldkey.clone())
449        } else {
450            let coldkey_file = self.coldkey_file()?;
451            coldkey_file.get_keypair(None)
452        }
453    }
454
455    /// Returns the coldkeypub from wallet.path/wallet.name/coldkeypub.txt or raises an error.
456    pub fn coldkeypub_property(&self) -> Result<Keypair, KeyFileError> {
457        let coldkeypub_file = self.coldkeypub_file()?;
458        coldkeypub_file.get_keypair(None)
459    }
460
461    /// Returns the hotkey from wallet.path/wallet.name/hotkeys/wallet.hotkey or raises an error.
462    pub fn hotkey_property(&self) -> Result<Keypair, KeyFileError> {
463        if let Some(hotkey) = &self._hotkey {
464            Ok(hotkey.clone())
465        } else {
466            let hotkey_file = self.hotkey_file()?;
467            hotkey_file.get_keypair(None)
468        }
469    }
470
471    /// Returns the hotkeypub from wallet.path/wallet.name/hotkeypub.txt or raises an error.
472    pub fn hotkeypub_property(&self) -> Result<Keypair, KeyFileError> {
473        let hotkeypub_file = self.hotkeypub_file()?;
474        hotkeypub_file.get_keypair(None)
475    }
476
477    /// Returns the name of the wallet
478    pub fn get_name(&self) -> String {
479        self.name.clone()
480    }
481
482    /// Returns the path of the wallet
483    pub fn get_path(&self) -> String {
484        self.path.clone()
485    }
486
487    /// Returns the hotkey name
488    pub fn get_hotkey_str(&self) -> String {
489        self.hotkey.clone()
490    }
491
492    /// Sets the coldkey for the wallet.
493    ///
494    /// ```text
495    ///     Arguments:
496    ///         keypair (Keypair): The keypair to set as coldkey.
497    ///         encrypt (bool): Whether to encrypt the key. Defaults to ``True``.
498    ///         overwrite (bool): Whether to overwrite if key exists. Defaults to ``False``.
499    ///         save_coldkey_to_env (bool): Whether to save coldkey password to local env. Defaults to ``False``.
500    ///         coldkey_password (Optional[str]): Coldkey password for encryption. Defaults to ``None``.
501    /// ```
502    ///
503    /// ```text
504    ///     Raises:
505    ///         KeyFileError: If file operations fail.
506    /// ```
507    pub fn set_coldkey(
508        &mut self,
509        keypair: Keypair,
510        encrypt: bool,
511        overwrite: bool,
512        save_coldkey_to_env: bool,
513        coldkey_password: Option<String>,
514    ) -> Result<(), KeyFileError> {
515        self._coldkey = Some(keypair.clone());
516        match self.create_coldkey_file(save_coldkey_to_env) {
517            Ok(keyfile) => keyfile
518                .set_keypair(keypair, encrypt, overwrite, coldkey_password)
519                .map_err(|e| KeyFileError::Generic(e.to_string())),
520            Err(e) => Err(KeyFileError::Generic(e.to_string())),
521        }
522    }
523
524    /// Sets the coldkeypub for the wallet.
525    ///
526    /// ```text
527    ///     Arguments:
528    ///         keypair (Keypair): The keypair to set as coldkeypub.
529    ///         encrypt (bool): Whether to encrypt the key. Defaults to ``False``.
530    ///         overwrite (bool): Whether to overwrite if key exists. Defaults to ``False``.
531    /// ```
532    ///
533    /// ```text
534    ///     Raises:
535    ///         KeyFileError: If file operations fail.
536    /// ```
537    pub fn set_coldkeypub(
538        &mut self,
539        keypair: Keypair,
540        encrypt: bool,
541        overwrite: bool,
542    ) -> Result<(), KeyFileError> {
543        let ss58_address = keypair
544            .ss58_address()
545            .ok_or_else(|| KeyFileError::Generic("Failed to get ss58_address".to_string()))?;
546        let coldkeypub_keypair = Keypair::new(
547            Some(ss58_address),
548            None,
549            None,
550            42,
551            None,
552            keypair.crypto_type(),
553        )
554        .map_err(|e| KeyFileError::Generic(e.to_string()))?;
555
556        self._coldkeypub = Some(coldkeypub_keypair.clone());
557        self.coldkeypub_file()
558            .map_err(|e| KeyFileError::Generic(e.to_string()))?
559            .set_keypair(coldkeypub_keypair, encrypt, overwrite, None)
560            .map_err(|e| KeyFileError::Generic(e.to_string()))?;
561        Ok(())
562    }
563
564    /// Sets the hotkey for the wallet.
565    ///
566    /// ```text
567    ///     Arguments:
568    ///         keypair (Keypair): The keypair to set as hotkey.
569    ///         encrypt (bool): Whether to encrypt the key. Defaults to ``False``.
570    ///         overwrite (bool): Whether to overwrite if key exists. Defaults to ``False``.
571    ///         save_hotkey_to_env (bool): Whether to save hotkey password to local env. Defaults to ``False``.
572    ///         hotkey_password (Optional[str]): Hotkey password for encryption. Defaults to ``None``.
573    /// ```
574    ///
575    /// ```text
576    ///     Raises:
577    ///         KeyFileError: If file operations fail.
578    /// ```
579    pub fn set_hotkey(
580        &mut self,
581        keypair: Keypair,
582        encrypt: bool,
583        overwrite: bool,
584        save_hotkey_to_env: bool,
585        hotkey_password: Option<String>,
586    ) -> Result<(), KeyFileError> {
587        self._hotkey = Some(keypair.clone());
588        self.create_hotkey_file(save_hotkey_to_env)
589            .map_err(|e| KeyFileError::Generic(e.to_string()))?
590            .set_keypair(keypair, encrypt, overwrite, hotkey_password)
591    }
592
593    /// Sets the hotkeypub for the wallet.
594    ///
595    /// ```text
596    ///     Arguments:
597    ///         keypair (Keypair): The keypair to set as hotkeypub.
598    ///         encrypt (bool): Whether to encrypt the key. Defaults to ``False``.
599    ///         overwrite (bool): Whether to overwrite if key exists. Defaults to ``False``.
600    /// ```
601    ///
602    /// ```text
603    ///     Raises:
604    ///         KeyFileError: If file operations fail.
605    /// ```
606    pub fn set_hotkeypub(
607        &mut self,
608        keypair: Keypair,
609        encrypt: bool,
610        overwrite: bool,
611    ) -> Result<(), KeyFileError> {
612        let ss58_address = keypair
613            .ss58_address()
614            .ok_or_else(|| KeyFileError::Generic("Failed to get ss58_address".to_string()))?;
615        let hotkeypub_keypair = Keypair::new(
616            Some(ss58_address),
617            None,
618            None,
619            42,
620            None,
621            keypair.crypto_type(),
622        )
623        .map_err(|e| KeyFileError::Generic(e.to_string()))?;
624
625        self._hotkeypub = Some(hotkeypub_keypair.clone());
626        self.hotkeypub_file()
627            .map_err(|e| KeyFileError::Generic(e.to_string()))?
628            .set_keypair(hotkeypub_keypair, encrypt, overwrite, None)
629            .map_err(|e| KeyFileError::Generic(e.to_string()))?;
630        Ok(())
631    }
632
633    /// Gets the coldkey from the wallet.
634    ///
635    /// ```text
636    ///     Arguments:
637    ///         password (Optional[str]): Password for decryption. Defaults to ``None``. If not provided, asks for user input.
638    /// ```
639    ///
640    /// ```text
641    ///     Returns:
642    ///         `Keypair` - The coldkey keypair.
643    ///     Raises:
644    ///         KeyFileError: If the coldkey file doesn't exist or decryption fails.
645    /// ```
646    pub fn get_coldkey(&self, password: Option<String>) -> Result<Keypair, KeyFileError> {
647        self.coldkey_file()?.get_keypair(password)
648    }
649
650    /// Gets the coldkeypub from the wallet.
651    ///
652    /// ```text
653    ///     Arguments:
654    ///         password (Optional[str]): Password for decryption. Defaults to ``None``. If not provided, asks for user input.
655    /// ```
656    ///
657    /// ```text
658    ///     Returns:
659    ///         `Keypair` - The coldkeypub keypair.
660    ///     Raises:
661    ///         KeyFileError: If the coldkeypub file doesn't exist or decryption fails.
662    /// ```
663    pub fn get_coldkeypub(&self, password: Option<String>) -> Result<Keypair, KeyFileError> {
664        self.coldkeypub_file()?.get_keypair(password)
665    }
666
667    /// Gets the hotkey from the wallet.
668    ///
669    /// ```text
670    ///     Arguments:
671    ///         password (Optional[str]): Password for decryption. Defaults to ``None``. If not provided, asks for user input.
672    /// ```
673    ///
674    /// ```text
675    ///     Returns:
676    ///         `Keypair` - The hotkey keypair.
677    ///     Raises:
678    ///         KeyFileError: If the hotkey file doesn't exist or decryption fails.
679    /// ```
680    pub fn get_hotkey(&self, password: Option<String>) -> Result<Keypair, KeyFileError> {
681        self.hotkey_file()?.get_keypair(password)
682    }
683
684    /// Gets the hotkeypub from the wallet.
685    ///
686    /// ```text
687    ///     Arguments:
688    ///         password (Optional[str]): Password for decryption. Defaults to ``None``. If not provided, asks for user input.
689    /// ```
690    ///
691    /// ```text
692    ///     Returns:
693    ///         `Keypair` - The hotkeypub keypair.
694    ///     Raises:
695    ///         KeyFileError: If the hotkeypub file doesn't exist or decryption fails.
696    /// ```
697    pub fn get_hotkeypub(&self, password: Option<String>) -> Result<Keypair, KeyFileError> {
698        self.hotkeypub_file()?.get_keypair(password)
699    }
700
701    /// Creates coldkey from uri string, optionally encrypts it with the user-provided password.
702    ///
703    /// ```text
704    ///     Arguments:
705    ///         uri (str): The URI string to create the coldkey from.
706    ///         use_password (bool): Whether to use a password for coldkey. Defaults to ``False``.
707    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
708    ///         suppress (bool): Whether to suppress mnemonic display. Defaults to ``True``.
709    ///         save_coldkey_to_env (bool): Whether to save coldkey password to local env. Defaults to ``False``.
710    ///         coldkey_password (Optional[str]): Coldkey password for encryption. Defaults to ``None``.
711    /// ```
712    ///
713    /// ```text
714    ///     Returns:
715    ///         `Wallet` - The wallet instance with created coldkey.
716    ///     Raises:
717    ///         KeyFileError: If key creation or file operations fail.
718    /// ```
719    #[allow(clippy::too_many_arguments)]
720    pub fn create_coldkey_from_uri(
721        &mut self,
722        uri: String,
723        use_password: bool,
724        overwrite: bool,
725        suppress: bool,
726        save_coldkey_to_env: bool,
727        coldkey_password: Option<String>,
728        crypto_type: u8,
729    ) -> Result<Wallet, KeyFileError> {
730        let keypair = Keypair::create_from_uri(uri.as_str(), crypto_type)
731            .map_err(|e| KeyFileError::Generic(e.to_string()))?;
732
733        if !suppress {
734            if let Some(m) = keypair.mnemonic() {
735                display_mnemonic_msg(m, "coldkey");
736            }
737        }
738
739        self.set_coldkey(
740            keypair.clone(),
741            use_password,
742            overwrite,
743            save_coldkey_to_env,
744            coldkey_password,
745        )?;
746        self.set_coldkeypub(keypair, false, overwrite)?;
747        Ok(self.clone())
748    }
749
750    /// Creates hotkey from uri string, optionally encrypts it with the user-provided password.
751    ///
752    /// ```text
753    ///     Arguments:
754    ///         uri (str): The URI string to create the hotkey from.
755    ///         use_password (bool): Whether to use a password for hotkey. Defaults to ``False``.
756    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
757    ///         suppress (bool): Whether to suppress mnemonic display. Defaults to ``True``.
758    ///         save_hotkey_to_env (bool): Whether to save hotkey password to local env. Defaults to ``False``.
759    ///         hotkey_password (Optional[str]): Hotkey password for encryption. Defaults to ``None``.
760    /// ```
761    ///
762    /// ```text
763    ///     Returns:
764    ///         `Wallet` - The wallet instance with created hotkey.
765    ///     Raises:
766    ///         KeyFileError: If key creation or file operations fail.
767    /// ```
768    #[allow(clippy::too_many_arguments)]
769    pub fn create_hotkey_from_uri(
770        &mut self,
771        uri: String,
772        use_password: bool,
773        overwrite: bool,
774        suppress: bool,
775        save_hotkey_to_env: bool,
776        hotkey_password: Option<String>,
777        crypto_type: u8,
778    ) -> Result<Wallet, KeyFileError> {
779        let keypair = Keypair::create_from_uri(uri.as_str(), crypto_type)
780            .map_err(|e| KeyFileError::Generic(e.to_string()))?;
781
782        if !suppress {
783            if let Some(m) = keypair.mnemonic() {
784                display_mnemonic_msg(m, "hotkey");
785            }
786        }
787
788        self.set_hotkey(
789            keypair.clone(),
790            use_password,
791            overwrite,
792            save_hotkey_to_env,
793            hotkey_password,
794        )?;
795        self.set_hotkeypub(keypair, false, overwrite)?;
796        Ok(self.clone())
797    }
798
799    /// Unlocks the coldkey.
800    pub fn unlock_coldkey(&mut self) -> Result<Keypair, KeyFileError> {
801        if self._coldkey.is_none() {
802            let coldkey_file = self.coldkey_file()?;
803            self._coldkey = Some(coldkey_file.get_keypair(None)?);
804        }
805        let _coldkey = self
806            ._coldkey
807            .clone()
808            .ok_or_else(|| KeyFileError::Generic("Coldkey file doesn't exist.".to_string()))?;
809        Ok(_coldkey)
810    }
811
812    /// Unlocks the coldkeypub.
813    pub fn unlock_coldkeypub(&mut self) -> Result<Keypair, KeyFileError> {
814        if self._coldkeypub.is_none() {
815            let coldkeypub_file = self.coldkeypub_file()?;
816            self._coldkeypub = Some(coldkeypub_file.get_keypair(None)?);
817        }
818        let _coldkeypub = self
819            ._coldkeypub
820            .clone()
821            .ok_or_else(|| KeyFileError::Generic("Coldkey file doesn't exist.".to_string()))?;
822        Ok(_coldkeypub)
823    }
824
825    /// Unlocks the hotkey.
826    pub fn unlock_hotkey(&mut self) -> Result<Keypair, KeyFileError> {
827        if self._hotkey.is_none() {
828            let hotkey_file = self.hotkey_file()?;
829            self._hotkey = Some(hotkey_file.get_keypair(None)?);
830        }
831        let _hotkey = self
832            ._hotkey
833            .clone()
834            .ok_or_else(|| KeyFileError::Generic("Hotkey doesn't exist.".to_string()))?;
835        Ok(_hotkey)
836    }
837
838    /// Unlocks the hotkeypub.
839    pub fn unlock_hotkeypub(&mut self) -> Result<Keypair, KeyFileError> {
840        if self._hotkeypub.is_none() {
841            let hotkeypub_file = self.hotkeypub_file()?;
842            self._hotkeypub = Some(hotkeypub_file.get_keypair(None)?);
843        }
844        let _hotkeypub = self
845            ._hotkeypub
846            .clone()
847            .ok_or_else(|| KeyFileError::Generic("Hotkeypub file doesn't exist.".to_string()))?;
848        Ok(_hotkeypub)
849    }
850
851    /// Creates a new coldkey, optionally encrypts it with the user-provided password and saves to disk.
852    ///
853    /// ```text
854    ///     Arguments:
855    ///         n_words (int): The number of words in the mnemonic. Defaults to 12.
856    ///         use_password (bool): Whether to use a password for coldkey. Defaults to ``True``.
857    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
858    ///         suppress (bool): Whether to suppress mnemonic display. Defaults to ``False``.
859    ///         save_coldkey_to_env (bool): Whether to save coldkey password to local env. Defaults to ``False``.
860    ///         coldkey_password (Optional[str]): Coldkey password for encryption. Defaults to ``None``. If `coldkey_password` is passed, then `use_password` is automatically ``True``.
861    /// ```
862    ///
863    /// ```text
864    ///     Returns:
865    ///         `Wallet` - The wallet instance with created coldkey.
866    ///     Raises:
867    ///         WalletError: If key generation or file operations fail.
868    /// ```
869    #[allow(clippy::too_many_arguments)]
870    pub fn new_coldkey(
871        &mut self,
872        n_words: usize,
873        use_password: bool,
874        overwrite: bool,
875        suppress: bool,
876        save_coldkey_to_env: bool,
877        coldkey_password: Option<String>,
878        crypto_type: u8,
879    ) -> Result<Wallet, WalletError> {
880        self.create_new_coldkey(
881            n_words,
882            use_password,
883            overwrite,
884            suppress,
885            save_coldkey_to_env,
886            coldkey_password,
887            crypto_type,
888        )
889    }
890
891    /// Creates a new coldkey, optionally encrypts it with the user-provided password and saves to disk.
892    ///
893    /// ```text
894    ///     Arguments:
895    ///         n_words (int): The number of words in the mnemonic. Defaults to 12.
896    ///         use_password (bool): Whether to use a password for coldkey. Defaults to ``True``.
897    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
898    ///         suppress (bool): Whether to suppress mnemonic display. Defaults to ``False``.
899    ///         save_coldkey_to_env (bool): Whether to save coldkey password to local env. Defaults to ``False``.
900    ///         coldkey_password (Optional[str]): Coldkey password for encryption. Defaults to ``None``. If `coldkey_password` is passed, then `use_password` is automatically ``True``.
901    /// ```
902    ///
903    /// ```text
904    ///     Returns:
905    ///         `Wallet` - The wallet instance with created coldkey.
906    ///     Raises:
907    ///         WalletError: If key generation or file operations fail.
908    /// ```
909    #[allow(clippy::too_many_arguments)]
910    fn create_new_coldkey(
911        &mut self,
912        n_words: usize,
913        mut use_password: bool,
914        overwrite: bool,
915        suppress: bool,
916        save_coldkey_to_env: bool,
917        coldkey_password: Option<String>,
918        crypto_type: u8,
919    ) -> Result<Self, WalletError> {
920        let mnemonic = Keypair::generate_mnemonic(n_words)
921            .map_err(|e| WalletError::KeyGeneration(e.to_string()))?;
922
923        let keypair = Keypair::create_from_mnemonic(&mnemonic, crypto_type)
924            .map_err(|e| WalletError::KeyGeneration(e.to_string()))?;
925
926        if !suppress {
927            display_mnemonic_msg(mnemonic, "coldkey");
928        }
929
930        // If password is provided, force password usage
931        if coldkey_password.is_some() {
932            use_password = true;
933        }
934
935        self.set_coldkey(
936            keypair.clone(),
937            use_password,
938            overwrite,
939            save_coldkey_to_env,
940            coldkey_password,
941        )?;
942
943        self.set_coldkeypub(keypair.clone(), false, overwrite)?;
944
945        Ok(self.clone())
946    }
947
948    /// Creates a new hotkey, optionally encrypts it with the user-provided password and saves to disk.
949    ///
950    /// ```text
951    ///     Arguments:
952    ///         n_words (int): The number of words in the mnemonic. Defaults to 12.
953    ///         use_password (bool): Whether to use a password for hotkey. Defaults to ``True``.
954    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
955    ///         suppress (bool): Whether to suppress mnemonic display. Defaults to ``False``.
956    ///         save_hotkey_to_env (bool): Whether to save hotkey password to local env. Defaults to ``False``.
957    ///         hotkey_password (Optional[str]): Hotkey password for encryption. Defaults to ``None``. If `hotkey_password` is passed, then `use_password` is automatically ``True``.
958    /// ```
959    ///
960    /// ```text
961    ///     Returns:
962    ///         `Wallet` - The wallet instance with created hotkey.
963    ///     Raises:
964    ///         WalletError: If key generation or file operations fail.
965    /// ```
966    #[allow(clippy::too_many_arguments)]
967    pub fn new_hotkey(
968        &mut self,
969        n_words: usize,
970        use_password: bool,
971        overwrite: bool,
972        suppress: bool,
973        save_hotkey_to_env: bool,
974        hotkey_password: Option<String>,
975        crypto_type: u8,
976    ) -> Result<Self, WalletError> {
977        self.create_new_hotkey(
978            n_words,
979            use_password,
980            overwrite,
981            suppress,
982            save_hotkey_to_env,
983            hotkey_password,
984            crypto_type,
985        )
986    }
987
988    /// Creates a new hotkey, optionally encrypts it with the user-provided password and saves to disk.
989    ///
990    /// ```text
991    ///     Arguments:
992    ///         n_words (int): The number of words in the mnemonic. Defaults to 12.
993    ///         use_password (bool): Whether to use a password for hotkey. Defaults to ``False``.
994    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
995    ///         suppress (bool): Whether to suppress mnemonic display. Defaults to ``False``.
996    ///         save_hotkey_to_env (bool): Whether to save hotkey password to local env. Defaults to ``False``.
997    ///         hotkey_password (Optional[str]): Hotkey password for encryption. Defaults to ``None``. If `hotkey_password` is passed, then `use_password` is automatically ``True``.
998    /// ```
999    ///
1000    /// ```text
1001    ///     Returns:
1002    ///         `Wallet` - The wallet instance with created hotkey.
1003    ///     Raises:
1004    ///         WalletError: If key generation or file operations fail.
1005    /// ```
1006    #[allow(clippy::too_many_arguments)]
1007    pub fn create_new_hotkey(
1008        &mut self,
1009        n_words: usize,
1010        mut use_password: bool,
1011        overwrite: bool,
1012        suppress: bool,
1013        save_hotkey_to_env: bool,
1014        hotkey_password: Option<String>,
1015        crypto_type: u8,
1016    ) -> Result<Wallet, WalletError> {
1017        let mnemonic = Keypair::generate_mnemonic(n_words)
1018            .map_err(|e| WalletError::KeyGeneration(e.to_string()))?;
1019        let keypair = Keypair::create_from_mnemonic(&mnemonic, crypto_type)
1020            .map_err(|e| WalletError::KeyGeneration(e.to_string()))?;
1021
1022        if !suppress {
1023            display_mnemonic_msg(mnemonic, "hotkey");
1024        }
1025
1026        // if hotkey_password is passed then hotkey_use_password always is true
1027        use_password = hotkey_password.is_some() || use_password;
1028
1029        self.set_hotkey(
1030            keypair.clone(),
1031            use_password,
1032            overwrite,
1033            save_hotkey_to_env,
1034            hotkey_password,
1035        )?;
1036
1037        self.set_hotkeypub(keypair.clone(), false, overwrite)?;
1038
1039        Ok(self.clone())
1040    }
1041
1042    /// Regenerates the coldkey from the passed mnemonic or seed, or JSON encrypts it with the user's password and saves the file.
1043    ///
1044    /// ```text
1045    ///     Arguments:
1046    ///         mnemonic (Optional[str]): Mnemonic phrase to regenerate the coldkey from. Defaults to ``None``.
1047    ///         seed (Optional[str]): Seed hex to regenerate the coldkey from. Defaults to ``None``.
1048    ///         json (Optional[tuple]): Tuple of (JSON data, passphrase) to regenerate the coldkey from. Defaults to ``None``.
1049    ///         use_password (bool): Whether to use a password for coldkey. Defaults to ``True``.
1050    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
1051    ///         suppress (bool): Whether to suppress mnemonic display. Defaults to ``False``.
1052    ///         save_coldkey_to_env (bool): Whether to save coldkey password to local env. Defaults to ``False``.
1053    ///         coldkey_password (Optional[str]): Coldkey password for encryption. Defaults to ``None``.
1054    /// ```
1055    ///
1056    /// ```text
1057    ///     Returns:
1058    ///         `Wallet` - The wallet instance with regenerated coldkey.
1059    ///     Raises:
1060    ///         WalletError: If key generation or file operations fail.
1061    /// ```
1062    #[allow(clippy::too_many_arguments)]
1063    pub fn regenerate_coldkey(
1064        &mut self,
1065        mnemonic: Option<String>,
1066        seed: Option<String>,
1067        json: Option<(String, String)>,
1068        use_password: bool,
1069        overwrite: bool,
1070        suppress: bool,
1071        save_coldkey_to_env: bool,
1072        coldkey_password: Option<String>,
1073        crypto_type: u8,
1074    ) -> Result<Self, WalletError> {
1075        let keypair = if let Some(mnemonic) = mnemonic {
1076            let keypair = Keypair::create_from_mnemonic(&mnemonic, crypto_type)
1077                .map_err(|e| WalletError::KeyGeneration(e.to_string()))?;
1078            if !suppress {
1079                display_mnemonic_msg(mnemonic, "coldkey");
1080            }
1081            keypair
1082        } else if let Some(seed) = seed {
1083            Keypair::create_from_seed(
1084                hex::decode(seed.trim_start_matches("0x")).unwrap(),
1085                crypto_type,
1086            )
1087            .map_err(|e| KeyFileError::Generic(e.to_string()))?
1088        } else if let Some((json_data, passphrase)) = json {
1089            // json_data + passphrase
1090            Keypair::create_from_encrypted_json(&json_data, &passphrase)
1091                .map_err(|e| KeyFileError::Generic(e.to_string()))?
1092        } else {
1093            return Err(WalletError::InvalidInput(
1094                "Must pass either mnemonic, seed, or json.".to_string(),
1095            ));
1096        };
1097
1098        self.set_coldkey(
1099            keypair.clone(),
1100            use_password,
1101            overwrite,
1102            save_coldkey_to_env,
1103            coldkey_password,
1104        )?;
1105        self.set_coldkeypub(keypair.clone(), false, overwrite)?;
1106        Ok(self.clone())
1107    }
1108
1109    /// Regenerates the coldkeypub from the passed ss58_address or public_key and saves the file.
1110    /// Requires either ss58_address or public_key to be passed.
1111    ///
1112    /// ```text
1113    ///     Arguments:
1114    ///         ss58_address (Optional[str]): SS58 address to regenerate the coldkeypub from. Defaults to ``None``.
1115    ///         public_key (Optional[str]): Public key hex to regenerate the coldkeypub from. Defaults to ``None``.
1116    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
1117    /// ```
1118    ///
1119    /// ```text
1120    ///     Returns:
1121    ///         `Wallet` - The wallet instance with regenerated coldkeypub.
1122    ///     Raises:
1123    ///         WalletError: If key generation or file operations fail.
1124    /// ```
1125    pub fn regenerate_coldkeypub(
1126        &mut self,
1127        ss58_address: Option<String>,
1128        public_key: Option<String>,
1129        overwrite: bool,
1130        crypto_type: u8,
1131    ) -> Result<Self, WalletError> {
1132        if ss58_address.is_none() && public_key.is_none() {
1133            return Err(WalletError::InvalidInput(
1134                "Either ss58_address or public_key must be passed.".to_string(),
1135            ));
1136        }
1137
1138        let address_to_string = ss58_address
1139            .as_ref()
1140            .or(public_key.as_ref())
1141            .ok_or_else(|| WalletError::InvalidInput("No address provided".to_string()))?;
1142
1143        if !is_valid_bittensor_address_or_public_key(address_to_string) {
1144            return Err(WalletError::InvalidInput(format!(
1145                "Invalid {}.",
1146                if ss58_address.is_some() {
1147                    "ss58_address"
1148                } else {
1149                    "public_key"
1150                }
1151            )));
1152        }
1153
1154        let keypair = Keypair::new(ss58_address, public_key, None, 42, None, crypto_type)
1155            .map_err(|e| WalletError::KeyGeneration(e.to_string()))?;
1156
1157        self.set_coldkeypub(keypair, false, overwrite)?;
1158        Ok(self.clone())
1159    }
1160
1161    /// Regenerates the hotkey from passed mnemonic or seed, encrypts it with the user's password and saves the file.
1162    ///
1163    /// ```text
1164    ///     Arguments:
1165    ///         mnemonic (Optional[str]): Mnemonic phrase to regenerate the hotkey from. Defaults to ``None``.
1166    ///         seed (Optional[str]): Seed hex to regenerate the hotkey from. Defaults to ``None``.
1167    ///         json (Optional[tuple]): Tuple of (JSON data, passphrase) to regenerate the hotkey from. Defaults to ``None``.
1168    ///         use_password (bool): Whether to use a password for hotkey. Defaults to ``False``.
1169    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
1170    ///         suppress (bool): Whether to suppress mnemonic display. Defaults to ``False``.
1171    ///         save_hotkey_to_env (bool): Whether to save hotkey password to local env. Defaults to ``False``.
1172    ///         hotkey_password (Optional[str]): Hotkey password for encryption. Defaults to ``None``.
1173    /// ```
1174    ///
1175    /// ```text
1176    ///     Returns:
1177    ///         `Wallet` - The wallet instance with regenerated hotkey.
1178    ///     Raises:
1179    ///         KeyFileError: If key generation or file operations fail.
1180    /// ```
1181    #[allow(clippy::too_many_arguments)]
1182    pub fn regenerate_hotkey(
1183        &mut self,
1184        mnemonic: Option<String>,
1185        seed: Option<String>,
1186        json: Option<(String, String)>,
1187        use_password: bool,
1188        overwrite: bool,
1189        suppress: bool,
1190        save_hotkey_to_env: bool,
1191        hotkey_password: Option<String>,
1192        crypto_type: u8,
1193    ) -> Result<Self, KeyFileError> {
1194        let keypair = if let Some(mnemonic) = mnemonic {
1195            let keypair = Keypair::create_from_mnemonic(&mnemonic, crypto_type)
1196                .map_err(|e| KeyFileError::Generic(e.to_string()))?;
1197            if !suppress {
1198                display_mnemonic_msg(mnemonic, "hotkey");
1199            }
1200            keypair
1201        } else if let Some(seed) = seed {
1202            Keypair::create_from_seed(
1203                hex::decode(seed.trim_start_matches("0x")).unwrap(),
1204                crypto_type,
1205            )
1206            .map_err(|e| KeyFileError::Generic(e.to_string()))?
1207        } else if let Some((json_data, passphrase)) = json {
1208            // json_data + passphrase
1209            Keypair::create_from_encrypted_json(&json_data, &passphrase)
1210                .map_err(|e| KeyFileError::Generic(e.to_string()))?
1211        } else {
1212            return Err(KeyFileError::Generic(
1213                "Must pass either mnemonic, seed, or json.".to_string(),
1214            ));
1215        };
1216
1217        self.set_hotkey(
1218            keypair.clone(),
1219            use_password,
1220            overwrite,
1221            save_hotkey_to_env,
1222            hotkey_password,
1223        )?;
1224        self.set_hotkeypub(keypair.clone(), false, overwrite)?;
1225        Ok(self.clone())
1226    }
1227
1228    /// Regenerates the hotkeypub from the passed ss58_address or public_key and saves the file.
1229    /// Requires either ss58_address or public_key to be passed.
1230    ///
1231    /// ```text
1232    ///     Arguments:
1233    ///         ss58_address (Optional[str]): SS58 address to regenerate the hotkeypub from. Defaults to ``None``.
1234    ///         public_key (Optional[str]): Public key hex to regenerate the hotkeypub from. Defaults to ``None``.
1235    ///         overwrite (bool): Whether to overwrite existing keys. Defaults to ``False``.
1236    /// ```
1237    ///
1238    /// ```text
1239    ///     Returns:
1240    ///         `Wallet` - The wallet instance with regenerated hotkeypub.
1241    ///     Raises:
1242    ///         WalletError: If key generation or file operations fail.
1243    /// ```
1244    pub fn regenerate_hotkeypub(
1245        &mut self,
1246        ss58_address: Option<String>,
1247        public_key: Option<String>,
1248        overwrite: bool,
1249        crypto_type: u8,
1250    ) -> Result<Self, WalletError> {
1251        if ss58_address.is_none() && public_key.is_none() {
1252            return Err(WalletError::InvalidInput(
1253                "Either ss58_address or public_key must be passed.".to_string(),
1254            ));
1255        }
1256
1257        let address_to_string = ss58_address
1258            .as_ref()
1259            .or(public_key.as_ref())
1260            .ok_or_else(|| WalletError::InvalidInput("No address provided".to_string()))?;
1261
1262        if !is_valid_bittensor_address_or_public_key(address_to_string) {
1263            return Err(WalletError::InvalidInput(format!(
1264                "Invalid {}.",
1265                if ss58_address.is_some() {
1266                    "ss58_address"
1267                } else {
1268                    "public_key"
1269                }
1270            )));
1271        }
1272
1273        let keypair = Keypair::new(ss58_address, public_key, None, 42, None, crypto_type)
1274            .map_err(|e| WalletError::KeyGeneration(e.to_string()))?;
1275
1276        self.set_hotkeypub(keypair, false, overwrite)?;
1277        Ok(self.clone())
1278    }
1279}