wallet_rs_cli/metamask/
mod.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5use clap::Parser;
6use eth_keystore::encrypt_key;
7use ethers_signers::{coins_bip39::English, MnemonicBuilder};
8use tracing::{debug, error, info};
9use wallet_metamask::{
10    interactive::{extract_all_vaults, get_password},
11    vault::decrypt_vault,
12};
13
14/// Start the metamask command
15#[derive(Debug, Parser)]
16pub struct Command {
17    /// Output the decrypted mnemonic to stdout
18    #[arg(short, long)]
19    output: bool,
20
21    /// The path to the keystore file, if you want to export the encrypted keystore
22    #[arg(short, long)]
23    keystore: Option<String>,
24
25    /// Flag to test running the command
26    #[arg(short, long)]
27    test: bool,
28}
29
30impl Command {
31    pub async fn run(&self) -> eyre::Result<()> {
32        // Get the vaults and the password
33        let vaults = extract_all_vaults();
34
35        // Print the error and exit if there was an error
36        if let Err(e) = vaults {
37            error!("Failed to extract vaults: {:?}", e);
38            return Ok(());
39        }
40
41        // Exit if this is a test run
42        if self.test {
43            info!("Cargo test, exiting");
44            return Ok(());
45        }
46
47        // Unwrap the vaults
48        let vaults = vaults.unwrap();
49
50        // Print the number of vaults
51        info!("Found {} vaults", vaults.len());
52
53        // Exit if there are no vaults
54        if vaults.is_empty() {
55            error!("No vaults found");
56            return Ok(());
57        }
58
59        // Get the first vault and the password
60        let vault = vaults[0].clone();
61        let pwd = get_password().unwrap();
62
63        // Attempt to decrypt the vault
64        let res = decrypt_vault(&vault, &pwd);
65
66        // Print the result
67        if res.is_ok() {
68            debug!("Decrypted vault");
69
70            // Print the mnemonic
71            if self.output {
72                print!("{}", &res.unwrap().data.mnemonic);
73                return Ok(());
74            }
75
76            if self.keystore.is_some() {
77                // Get the mnemonic and index
78                let index = 0u32;
79                let phrase = &res.unwrap().data.mnemonic.to_string();
80
81                // Build the wallet with the mnemonic
82                let wallet = MnemonicBuilder::<English>::default()
83                    .phrase(phrase.as_str())
84                    .index(index)
85                    .unwrap()
86                    .build()
87                    .unwrap();
88
89                // Encrypt the wallet
90                let pk = wallet.signer();
91                let mut rng = rand::thread_rng();
92                let _ =
93                    encrypt_key(self.keystore.clone().unwrap(), &mut rng, pk.to_bytes(), pwd, None);
94            }
95        } else {
96            error!("Failed to decrypt vault: {:?}", res);
97        }
98        Ok(())
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105    use tracing_test::traced_test;
106
107    #[traced_test]
108    #[tokio::test]
109    async fn test_metamask_run() {
110        // Set up test input
111        let command = Command { output: false, keystore: None, test: true };
112
113        // Run the command
114        let res = command.run().await;
115
116        // Check that the command ran successfully
117        assert!(res.is_ok());
118
119        // Check that the logs contain the word "vault" (logs on found)
120        assert!(logs_contain("Failed to extract") || logs_contain("Cargo test, exiting"));
121    }
122}