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