contact/client/
mod.rs

1use crate::jsonrpc::client::HTTPClient;
2use std::sync::Arc;
3use std::time::Duration;
4
5mod get;
6mod send;
7
8/// An instance of Contact Cosmos RPC Client.
9#[derive(Clone)]
10pub struct Contact {
11    pub jsonrpc_client: Arc<Box<HTTPClient>>,
12    pub timeout: Duration,
13}
14
15impl Contact {
16    pub fn new(url: &str, timeout: Duration) -> Self {
17        let mut url = url;
18        if !url.ends_with('/') {
19            url = url.trim_end_matches('/');
20        }
21        Self {
22            jsonrpc_client: Arc::new(Box::new(HTTPClient::new(&url))),
23            timeout,
24        }
25    }
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31    use crate::jsonrpc::error::JsonRpcError;
32    use actix::Arbiter;
33    use actix::System;
34    use deep_space::address::Address;
35    use deep_space::coin::Coin;
36    use deep_space::private_key::PrivateKey;
37    use rand::Rng;
38
39    /// If you run the start-chains.sh script in the peggy repo it will pass
40    /// port 1317 on localhost through to the peggycli rest-server which can
41    /// then be used to run this test and debug things quickly. You will need
42    /// to run the following command and copy a phrase so that you actually
43    /// have some coins to send funds
44    /// docker exec -it peggy_test_instance cat /validator-phrases
45    #[test]
46    #[ignore]
47    fn test_endpoints() {
48        env_logger::init();
49        let key = PrivateKey::from_phrase("destroy lock crane champion nest hurt chicken leopard field album describe glimpse chimney sort kind peanut worry dilemma anchor dismiss fox there judge arm", "").unwrap();
50        let token_name = "footoken".to_string();
51
52        let res = System::run(move || {
53            let contact = Contact::new(
54                "http://testnet1-rpc.althea.net:1317",
55                Duration::from_secs(30),
56            );
57            Arbiter::spawn(async move {
58                let res = contact.get_syncing_status().await;
59                if res.is_err() {
60                    println!("{:?}", res);
61                    System::current().stop_with_code(1);
62                }
63
64                let res = test_rpc_calls(contact, key, token_name).await;
65                if res.is_err() {
66                    println!("{:?}", res);
67                    System::current().stop_with_code(1);
68                }
69
70                System::current().stop();
71            });
72        });
73
74        if let Err(e) = res {
75            panic!(format!("{:?}", e))
76        }
77    }
78
79    pub async fn test_rpc_calls(
80        contact: Contact,
81        key: PrivateKey,
82        test_token_name: String,
83    ) -> Result<(), String> {
84        let fee = Coin {
85            denom: test_token_name.clone(),
86            amount: 1u32.into(),
87        };
88        let address = key
89            .to_public_key()
90            .expect("Failed to convert to pubkey!")
91            .to_address();
92
93        test_basic_calls(&contact, key, test_token_name, fee.clone(), address).await?;
94
95        Ok(())
96    }
97
98    async fn test_basic_calls(
99        contact: &Contact,
100        key: PrivateKey,
101        test_token_name: String,
102        fee: Coin,
103        address: Address,
104    ) -> Result<(), String> {
105        // start by validating the basics
106        //
107        // get the latest block
108        // get our account info
109        // send a base transaction
110
111        let res = contact.get_latest_block().await;
112        if res.is_err() {
113            return Err(format!("Failed to get latest block {:?}", res));
114        }
115
116        let res = contact.get_account_info(address).await;
117        match res {
118            Ok(_) => {}
119            Err(JsonRpcError::NoToken) => {}
120            Err(e) => return Err(format!("Failed to get account info {:?}", e)),
121        }
122
123        let res = contact.get_balances(address).await;
124        if res.is_err() {
125            return Err(format!("Failed to get balances {:?}", res));
126        }
127
128        let mut rng = rand::thread_rng();
129        let secret: [u8; 32] = rng.gen();
130        let cosmos_key = PrivateKey::from_secret(&secret);
131        let cosmos_address = cosmos_key.to_public_key().unwrap().to_address();
132
133        let res = contact
134            .create_and_send_transaction(
135                Coin {
136                    denom: test_token_name.clone(),
137                    amount: 5u32.into(),
138                },
139                fee.clone(),
140                cosmos_address,
141                key,
142                None,
143                None,
144                None,
145            )
146            .await;
147        if res.is_err() {
148            return Err(format!("Failed to send tx {:?}", res));
149        }
150
151        let new_balances = contact.get_balances(cosmos_address).await.unwrap();
152        assert!(!new_balances.result.is_empty());
153        info!("new balances are {:?}", new_balances);
154
155        Ok(())
156    }
157}