1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use crate::error::CosmosClientError;
use cosmrs::bip32::{Language, Mnemonic, XPrv};
use cosmrs::crypto::secp256k1::SigningKey;
use cosmrs::crypto::PublicKey;
use cosmrs::AccountId;
use hex::decode;
use rand_core::OsRng;

pub struct Signer {
    pub mnemonic: Option<String>,
    pub denom: String,
    pub public_address: AccountId,
    pub private_key: SigningKey,
    pub public_key: PublicKey,
    pub gas_adjustment_percent: u8,
    pub gas_price: u128,
}

impl Signer {
    fn load_from_mnemonic(
        phrase: &str,
        prefix: &str,
        derivation: Option<&str>,
    ) -> Result<(SigningKey, PublicKey, AccountId), CosmosClientError> {
        let derivation = if let Some(derivation) = derivation {
            derivation
        } else {
            "m/44'/118'/0'/0/0"
        };

        let mnemonic = Mnemonic::new(phrase, Language::English)?;
        let pri = XPrv::derive_from_path(&mnemonic.to_seed(""), &derivation.parse()?)?;
        let private_key = SigningKey::from(pri);
        let public_key = private_key.public_key();
        let public_address = public_key.account_id(prefix)?;

        Ok((private_key, public_key, public_address))
    }

    pub fn generate(
        prefix: &str,
        denom: &str,
        derivation: Option<&str>,
        gas_adjustment_percent: u8,
        gas_price: u128,
    ) -> Result<Self, CosmosClientError> {
        let mnemonic = Mnemonic::random(OsRng, Language::English);
        let (private_key, public_key, public_address) =
            Signer::load_from_mnemonic(mnemonic.phrase(), prefix, derivation)?;

        Ok(Signer {
            mnemonic: Some(mnemonic.phrase().to_string()),
            public_address,
            gas_adjustment_percent,
            gas_price,
            denom: denom.to_string(),
            private_key,
            public_key,
        })
    }

    pub fn from_pkey(
        private_key: &str,
        prefix: &str,
        denom: &str,
        gas_adjustment_percent: u8,
        gas_price: u128,
    ) -> Result<Self, CosmosClientError> {
        let private_key = SigningKey::from_slice(decode(private_key)?.as_slice())?;
        let public_key = private_key.public_key();
        let public_address = public_key.account_id(prefix)?;

        Ok(Signer {
            mnemonic: None,
            public_address,
            gas_adjustment_percent,
            gas_price,
            denom: denom.to_string(),
            private_key,
            public_key,
        })
    }

    pub fn from_mnemonic(
        phrase: &str,
        prefix: &str,
        denom: &str,
        derivation: Option<&str>,
        gas_adjustment_percent: u8,
        gas_price: u128,
    ) -> Result<Self, CosmosClientError> {
        let (private_key, public_key, public_address) =
            Signer::load_from_mnemonic(phrase, prefix, derivation)?;

        Ok(Signer {
            mnemonic: Some(phrase.to_string()),
            public_address,
            gas_adjustment_percent,
            gas_price,
            denom: denom.to_string(),
            private_key,
            public_key,
        })
    }
}