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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
pub mod builder;

use crate::builder::settings::wallet::WalletType;
pub use builder::WalletTemplateBuilder;
use chain_addr::{AddressReadable, Discrimination};
use chain_impl_mockchain::value::Value;
use jormungandr_automation::jormungandr::NodeAlias;
use jormungandr_lib::interfaces::{DiscriminationDef, TokenIdentifier, ValueDef};
use serde::Deserialize;
use std::{
    collections::HashMap,
    hash::{Hash, Hasher},
};
use thor::{DiscriminationExtension, WalletAlias};

#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
#[serde(untagged)]
pub enum WalletTemplate {
    /// Generated wallet when we want let hersir generate new wallet from scratch
    Generated {
        alias: WalletAlias,
        #[serde(with = "ValueDef")]
        value: Value,
        #[serde(default = "default_wallet_type")]
        wallet_type: WalletType,
        delegate: Option<NodeAlias>,
        #[serde(with = "DiscriminationDef")]
        #[serde(default = "default_discrimination")]
        discrimination: Discrimination,
        #[serde(default = "HashMap::new")]
        tokens: HashMap<TokenIdentifier, u64>,
    },
    /// Wallet which was given in configuration by address, thus hersir does not control it, which
    /// implies that some operations like delegation in block0 are not available
    External {
        address: String,
        #[serde(with = "ValueDef")]
        value: Value,
        #[serde(default = "HashMap::new")]
        tokens: HashMap<TokenIdentifier, u64>,
    },
}

pub fn default_wallet_type() -> WalletType {
    WalletType::Account
}

pub fn default_discrimination() -> Discrimination {
    Discrimination::Test
}

impl WalletTemplate {
    pub fn is_generated(&self) -> bool {
        matches!(self, Self::Generated { .. })
    }

    pub fn has_alias(&self, other_alias: &WalletAlias) -> bool {
        if let Some(alias) = &self.alias() {
            alias == other_alias
        } else {
            false
        }
    }
}

#[allow(clippy::derived_hash_with_manual_eq)]
impl Hash for WalletTemplate {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            WalletTemplate::Generated { alias, .. } => alias.hash(state),
            WalletTemplate::External { address, .. } => address.hash(state),
        }
    }
}

impl WalletTemplate {
    pub fn new_account<S: Into<WalletAlias>>(
        alias: S,
        value: Value,
        discrimination: Discrimination,
        tokens: HashMap<TokenIdentifier, u64>,
    ) -> Self {
        Self::Generated {
            alias: alias.into(),
            value,
            discrimination,
            tokens,
            wallet_type: WalletType::Account,
            delegate: None,
        }
    }
    pub fn new_utxo<S: Into<WalletAlias>>(
        alias: S,
        value: Value,
        discrimination: Discrimination,
        tokens: HashMap<TokenIdentifier, u64>,
    ) -> Self {
        Self::Generated {
            alias: alias.into(),
            value,
            discrimination,
            tokens,
            wallet_type: WalletType::UTxO,
            delegate: None,
        }
    }

    pub fn new_external<S: Into<String>>(
        address: S,
        value: Value,
        tokens: HashMap<TokenIdentifier, u64>,
    ) -> Self {
        Self::External {
            value,
            tokens,
            address: address.into(),
        }
    }

    pub fn id(&self) -> String {
        if let Some(alias) = self.alias() {
            alias
        } else if let Some(address) = self.address() {
            address
        } else {
            unreachable!()
        }
    }

    pub fn alias(&self) -> Option<WalletAlias> {
        match self {
            Self::External { .. } => None,
            Self::Generated { alias, .. } => Some(alias.clone()),
        }
    }

    pub fn address(&self) -> Option<String> {
        match self {
            Self::External { address, .. } => Some(address.clone()),
            Self::Generated { .. } => None,
        }
    }

    pub fn discrimination(&self) -> Discrimination {
        match self {
            Self::External { address, .. } => Discrimination::from_prefix(
                &AddressReadable::from_string_anyprefix(address)
                    .unwrap()
                    .get_prefix(),
            ),
            Self::Generated { discrimination, .. } => *discrimination,
        }
    }

    pub fn wallet_type(&self) -> Option<WalletType> {
        match self {
            Self::External { .. } => None,
            Self::Generated { wallet_type, .. } => Some(wallet_type.clone()),
        }
    }

    pub fn value(&self) -> &Value {
        match self {
            Self::External { value, .. } => value,
            Self::Generated { value, .. } => value,
        }
    }

    pub fn delegate(&self) -> &Option<NodeAlias> {
        match self {
            Self::External { .. } => &None,
            Self::Generated { delegate, .. } => delegate,
        }
    }

    pub fn delegate_mut(&mut self) -> &mut Option<NodeAlias> {
        match self {
            Self::External { .. } => unimplemented!(),
            Self::Generated { delegate, .. } => delegate,
        }
    }

    pub fn tokens(&self) -> &HashMap<TokenIdentifier, u64> {
        match self {
            Self::External { tokens, .. } => tokens,
            Self::Generated { tokens, .. } => tokens,
        }
    }
}