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}