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
use super::{
    base_currency::{BaseCurrency, CurrencyLike},
    currency::CurrencyTrait,
    token::Token,
    weth9::WETH9,
};
use alloy_primitives::Address;
use lazy_static::lazy_static;
use std::{collections::HashMap, sync::Mutex};

lazy_static! {
    static ref ETHER_CACHE: Mutex<HashMap<u32, Ether>> = Mutex::new(HashMap::new());
}

/// Ether is the main usage of a 'native' currency, i.e. for Ethereum mainnet and all testnets
pub type Ether = CurrencyLike<()>;

impl CurrencyTrait for Ether {
    fn is_native(&self) -> bool {
        true
    }

    fn address(&self) -> Address {
        self.wrapped().address()
    }

    fn equals(&self, other: &impl CurrencyTrait) -> bool {
        match other.is_native() {
            true => self.chain_id() == other.chain_id(),
            _ => false,
        }
    }

    fn wrapped(&self) -> Token {
        match WETH9::default().get(self.chain_id()) {
            Some(weth9) => weth9.clone(),
            None => panic!("WRAPPED"),
        }
    }
}

impl Ether {
    pub fn new(chain_id: u32) -> Self {
        Self {
            chain_id,
            decimals: 18,
            symbol: Some("ETH".to_string()),
            name: Some("Ether".to_string()),
            meta: (),
        }
    }

    pub fn on_chain(chain_id: u32) -> Self {
        let mut cache = ETHER_CACHE.lock().unwrap();
        match cache.get(&chain_id) {
            Some(ether) => ether.clone(),
            None => {
                let ether = Ether::new(chain_id);
                cache.insert(chain_id, ether.clone());
                ether
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::entities::currency::Currency;

    #[test]
    fn test_static_constructor_uses_cache() {
        assert!(Ether::on_chain(1) == Ether::on_chain(1));
    }

    #[test]
    fn test_caches_once_per_chain_id() {
        assert!(Ether::on_chain(1) != Ether::on_chain(2));
    }

    #[test]
    fn test_equals_returns_false_for_different_chains() {
        assert!(!Ether::on_chain(1).equals(&Currency::NativeCurrency(Ether::on_chain(2))));
    }

    #[test]
    fn test_equals_returns_true_for_same_chains() {
        assert!(Ether::on_chain(1).equals(&Currency::NativeCurrency(Ether::on_chain(1))));
    }
}