hopr_chain_connector/connector/
values.rs

1use std::time::Duration;
2
3use blokli_client::api::BlokliQueryClient;
4use futures::TryFutureExt;
5use hopr_api::chain::{ChainInfo, DomainSeparators};
6use hopr_internal_types::prelude::WinningProbability;
7use hopr_primitive_types::prelude::*;
8
9use crate::{
10    HoprBlockchainReader,
11    connector::HoprBlockchainConnector,
12    errors::ConnectorError,
13    utils::{ParsedChainInfo, model_to_chain_info},
14};
15
16pub(crate) const CHAIN_INFO_CACHE_KEY: u32 = 0;
17
18impl<B, C, P, R> HoprBlockchainConnector<C, R, B, P>
19where
20    C: BlokliQueryClient + Send + Sync + 'static,
21{
22    pub(crate) async fn query_cached_chain_info(&self) -> Result<ParsedChainInfo, ConnectorError> {
23        Ok(self
24            .values
25            .try_get_with(
26                CHAIN_INFO_CACHE_KEY,
27                self.client
28                    .query_chain_info()
29                    .map_err(ConnectorError::from)
30                    .and_then(|model| futures::future::ready(model_to_chain_info(model))),
31            )
32            .await?)
33    }
34}
35
36#[async_trait::async_trait]
37impl<B, R, C, P> hopr_api::chain::ChainValues for HoprBlockchainConnector<C, B, P, R>
38where
39    B: Send + Sync,
40    C: BlokliQueryClient + Send + Sync + 'static,
41    P: Send + Sync,
42    R: Send + Sync,
43{
44    type Error = ConnectorError;
45
46    // NOTE: these APIs can be called without calling `connect` first
47
48    #[inline]
49    async fn balance<Cy: Currency, A: Into<Address> + Send>(&self, address: A) -> Result<Balance<Cy>, Self::Error> {
50        HoprBlockchainReader(self.client.clone()).balance(address).await
51    }
52
53    async fn domain_separators(&self) -> Result<DomainSeparators, Self::Error> {
54        Ok(self.query_cached_chain_info().await?.domain_separators)
55    }
56
57    async fn minimum_incoming_ticket_win_prob(&self) -> Result<WinningProbability, Self::Error> {
58        Ok(self.query_cached_chain_info().await?.ticket_win_prob)
59    }
60
61    async fn minimum_ticket_price(&self) -> Result<HoprBalance, Self::Error> {
62        Ok(self.query_cached_chain_info().await?.ticket_price)
63    }
64
65    async fn key_binding_fee(&self) -> Result<HoprBalance, Self::Error> {
66        Ok(self.query_cached_chain_info().await?.key_binding_fee)
67    }
68
69    async fn channel_closure_notice_period(&self) -> Result<Duration, Self::Error> {
70        Ok(self.query_cached_chain_info().await?.channel_closure_grace_period)
71    }
72
73    async fn chain_info(&self) -> Result<ChainInfo, Self::Error> {
74        Ok(self.query_cached_chain_info().await?.info)
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use std::str::FromStr;
81
82    use hopr_api::chain::ChainValues;
83    use hopr_crypto_types::prelude::*;
84    use hopr_internal_types::account::{AccountEntry, AccountType};
85
86    use super::*;
87    use crate::{connector::tests::create_connector, testing::BlokliTestStateBuilder};
88
89    #[tokio::test]
90    async fn connector_should_get_balance() -> anyhow::Result<()> {
91        let account = AccountEntry {
92            public_key: *OffchainKeypair::random().public(),
93            chain_addr: [1u8; Address::SIZE].into(),
94            entry_type: AccountType::NotAnnounced,
95            safe_address: Some([2u8; Address::SIZE].into()),
96            key_id: 1.into(),
97        };
98
99        let blokli_client = BlokliTestStateBuilder::default()
100            .with_accounts([(account.clone(), HoprBalance::new_base(100), XDaiBalance::new_base(1))])
101            .with_safe_allowances([(account.safe_address.unwrap(), HoprBalance::new_base(10000))])
102            .build_static_client();
103
104        let mut connector = create_connector(blokli_client)?;
105        connector.connect().await?;
106
107        assert_eq!(
108            connector.balance(account.safe_address.unwrap()).await?,
109            HoprBalance::new_base(100)
110        );
111        assert_eq!(connector.balance(account.chain_addr).await?, XDaiBalance::new_base(1));
112
113        Ok(())
114    }
115
116    #[tokio::test]
117    async fn connector_should_query_chain_info() -> anyhow::Result<()> {
118        let blokli_client = BlokliTestStateBuilder::default()
119            .with_hopr_network_chain_info("rotsee")
120            .build_static_client();
121
122        let mut connector = create_connector(blokli_client)?;
123        connector.connect().await?;
124
125        let chain_info = connector.chain_info().await?;
126
127        assert_eq!(100, chain_info.chain_id);
128        assert_eq!("rotsee", &chain_info.hopr_network_name);
129
130        assert_eq!(Duration::from_mins(5), connector.channel_closure_notice_period().await?);
131        assert_eq!(HoprBalance::new_base(1), connector.minimum_ticket_price().await?);
132        assert!(WinningProbability::ALWAYS.approx_eq(&connector.minimum_incoming_ticket_win_prob().await?));
133        assert_eq!(Hash::default(), connector.domain_separators().await?.channel);
134        assert_eq!(
135            HoprBalance::from_str("0.01 wxHOPR")?,
136            connector.key_binding_fee().await?
137        );
138
139        Ok(())
140    }
141
142    #[tokio::test]
143    async fn connector_should_query_chain_info_without_calling_connect_first() -> anyhow::Result<()> {
144        let blokli_client = BlokliTestStateBuilder::default()
145            .with_hopr_network_chain_info("rotsee")
146            .build_static_client();
147
148        let connector = create_connector(blokli_client)?;
149
150        let chain_info = connector.chain_info().await?;
151
152        assert_eq!(100, chain_info.chain_id);
153        assert_eq!("rotsee", &chain_info.hopr_network_name);
154
155        assert_eq!(Duration::from_mins(5), connector.channel_closure_notice_period().await?);
156        assert_eq!(HoprBalance::new_base(1), connector.minimum_ticket_price().await?);
157        assert!(WinningProbability::ALWAYS.approx_eq(&connector.minimum_incoming_ticket_win_prob().await?));
158        assert_eq!(Hash::default(), connector.domain_separators().await?.channel);
159        assert_eq!(
160            HoprBalance::from_str("0.01 wxHOPR")?,
161            connector.key_binding_fee().await?
162        );
163
164        Ok(())
165    }
166}