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}