terra_rust_api/
client.rs

1// use crate::errors::{ErrorKind, Result};
2use crate::client::tx_types::{TXResultAsync, TXResultSync, TxFeeResult};
3use crate::core_types::{Coin, StdFee, StdSignMsg, StdSignature};
4use reqwest::header::{HeaderMap, CONTENT_TYPE, USER_AGENT};
5use reqwest::{Client, RequestBuilder};
6use serde::{Deserialize, Serialize};
7
8pub mod auth;
9/// Structures used in account authentication
10pub mod auth_types;
11/// APIs around bank module (get balances)
12pub mod bank;
13/// JSON Serializer/Deserializer helpers
14pub mod client_types;
15/// Common Structures throughout the library
16pub mod core_types;
17pub mod fcd;
18pub mod lcd_types;
19/// APIs around market operations (swap)
20pub mod market;
21/// APIs to perform oracle related things
22pub mod oracle;
23/// Structures used for Oracle APIs
24pub mod oracle_types;
25/// tendermint RPC
26pub mod rpc;
27pub mod rpc_types;
28/// staking routines
29pub mod staking;
30/// Structures used for Staking APIs
31pub mod staking_types;
32/// tendermint level APIs
33pub mod tendermint;
34/// Structures used for Tendermint / Misc APIs
35pub mod tendermint_types;
36/// operations around the transaction itself
37pub mod tx;
38/// Structures used for sending transactions to LCD
39pub mod tx_types;
40/// wasm module/contract related apis
41pub mod wasm;
42pub mod wasm_types;
43
44use crate::auth_types::AuthAccount;
45use crate::errors::TerraRustAPIError;
46use crate::errors::TerraRustAPIError::{GasPriceError, TxResultError};
47use crate::messages::Message;
48use crate::PrivateKey;
49use crate::{AddressBook, LCDResult};
50
51use rust_decimal_macros::dec;
52use secp256k1::Secp256k1;
53use secp256k1::Signing;
54use std::fs::File;
55
56/// Version # of package sent out on requests to help with debugging
57const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
58/// name of package
59const NAME: Option<&'static str> = option_env!("CARGO_PKG_NAME");
60
61const NETWORK_PROD_ADDRESS_BOOK: &str = "https://network.terra.dev/addrbook.json";
62const NETWORK_TEST_ADDRESS_BOOK: &str =
63    "https://raw.githubusercontent.com/terra-money/testnet/master/bombay-12/addrbook.json";
64
65/// When Submitting transactions you need to either submit gas or a fee to the validator
66/// This structure is used to determine what your preferences are by default
67/// Higher fees may be given preference by the validator to include the transaction in their block
68#[derive(Clone, Debug)]
69pub struct GasOptions {
70    /// If specified the TX will use the fee specified
71    pub fees: Option<Coin>,
72    /// if true, the server will call the 'estimate_transaction' to get an estimate.
73    /// This estimate is then multiplied by the gas_adjustment field
74    pub estimate_gas: bool,
75    /// your estimate of the gas to use.
76    pub gas: Option<u64>,
77    /// used to calculate the fee .. gas * gas_price
78    pub gas_price: Option<Coin>,
79    /// used to adjust the estimate
80    pub gas_adjustment: Option<f64>,
81}
82impl GasOptions {
83    /// for hard-coding of fees
84    pub fn create_with_fees(fees: &str, gas: u64) -> Result<GasOptions, TerraRustAPIError> {
85        Ok(GasOptions {
86            fees: Coin::parse(fees)?,
87            estimate_gas: false,
88            gas: Some(gas),
89            gas_price: None,
90            gas_adjustment: None,
91        })
92    }
93    /// for when you want the validator to give you an estimate on the amounts
94
95    pub fn create_with_gas_estimate(
96        gas_price: &str,
97        gas_adjustment: f64,
98    ) -> Result<GasOptions, TerraRustAPIError> {
99        Ok(GasOptions {
100            fees: None,
101            estimate_gas: true,
102            gas: None,
103            gas_price: Coin::parse(gas_price)?,
104            gas_adjustment: Some(gas_adjustment),
105        })
106    }
107    pub async fn create_with_fcd(
108        client: &reqwest::Client,
109        fcd_url: &str,
110        gas_denom: &str,
111        gas_adjustment: f64,
112    ) -> Result<GasOptions, TerraRustAPIError> {
113        let prices = fcd::FCD::fetch_gas_prices(client, fcd_url).await?;
114        if let Some(price) = prices.get(gas_denom) {
115            let gas_coin = Coin::create(gas_denom, *price);
116            let gas_price = Some(gas_coin);
117            Ok(GasOptions {
118                fees: None,
119                estimate_gas: true,
120                gas: None,
121                gas_price,
122                gas_adjustment: Some(gas_adjustment),
123            })
124        } else {
125            Err(GasPriceError(gas_denom.into()))
126        }
127    }
128}
129
130/// The main structure that all API calls are generated from
131#[derive(Clone)]
132pub struct Terra {
133    /// reqwest Client
134    client: Client,
135    /// The URL of the LCD
136    url: String,
137
138    /// The Chain of the network
139    pub chain_id: String,
140    /// Gas Options used to help with gas/fee generation of transactions
141    pub gas_options: Option<GasOptions>,
142    pub debug: bool,
143}
144impl Terra {
145    /// Create a LCD client interface
146    pub fn lcd_client<S: Into<String>>(
147        url: S,
148        chain_id: S,
149        gas_options: &GasOptions,
150        debug: Option<bool>,
151    ) -> Terra {
152        let client = reqwest::Client::new();
153        Terra {
154            client,
155            url: url.into(),
156            chain_id: chain_id.into(),
157            gas_options: Some(gas_options.clone()),
158            debug: debug.unwrap_or(false),
159        }
160    }
161
162    /// Create a read-only / query client interface
163    pub fn lcd_client_no_tx<S: Into<String>>(url: S, chain_id: S) -> Terra {
164        let client = reqwest::Client::new();
165        Terra {
166            client,
167            url: url.into(),
168            chain_id: chain_id.into(),
169            gas_options: None,
170            debug: false,
171        }
172    }
173
174    /// Auth API functions
175    pub fn auth(&self) -> auth::Auth {
176        auth::Auth::create(self)
177    }
178    /// Bank  API functions
179    pub fn bank(&self) -> bank::Bank {
180        bank::Bank::create(self)
181    }
182    /// Staking API functions
183    pub fn staking(&self) -> staking::Staking {
184        staking::Staking::create(self)
185    }
186    /// Market API functions
187    pub fn market(&self) -> market::Market {
188        market::Market::create(self)
189    }
190    /// Oracle API functions
191    pub fn oracle(&self) -> oracle::Oracle {
192        oracle::Oracle::create(self)
193    }
194    /// Tendermint (MISC) API Functions
195    pub fn tendermint(&self) -> tendermint::Tendermint {
196        tendermint::Tendermint::create(self)
197    }
198    /// TXS API Functions
199    pub fn tx(&self) -> tx::TX {
200        tx::TX::create(self)
201    }
202    /// RPC Api Functions
203    pub fn rpc<'a>(&'a self, tendermint_url: &'a str) -> rpc::RPC {
204        rpc::RPC::create(self, tendermint_url)
205    }
206    /// FCD Api Functions
207    pub fn fcd<'a>(&'a self, fcd_url: &'a str) -> fcd::FCD {
208        fcd::FCD::create(self, fcd_url)
209    }
210    /// WASM module / smart contract API Functions
211    pub fn wasm(&self) -> wasm::Wasm {
212        wasm::Wasm::create(self)
213    }
214
215    pub fn construct_headers() -> HeaderMap {
216        let mut headers = HeaderMap::new();
217
218        headers.insert(
219            USER_AGENT,
220            format!(
221                "PFC-{}/{}",
222                NAME.unwrap_or("terra-rust-api"),
223                VERSION.unwrap_or("-?-")
224            )
225            .parse()
226            .unwrap(),
227        );
228        headers.insert(CONTENT_TYPE, "application/json".parse().unwrap());
229        headers
230    }
231
232    /// used to send a GET command to the LCD
233    pub async fn send_cmd<T: for<'de> Deserialize<'de>>(
234        &self,
235        path: &str,
236        args: Option<&str>,
237    ) -> Result<T, TerraRustAPIError> {
238        self.send_cmd_url(&self.url, path, args).await
239    }
240
241    /// used to send a GET command to any URL
242    pub async fn send_cmd_url<T: for<'de> Deserialize<'de>>(
243        &self,
244        url: &str,
245        path: &str,
246        args: Option<&str>,
247    ) -> Result<T, TerraRustAPIError> {
248        let request_url = match args {
249            Some(a) => format!("{}{}{}", url.to_owned(), path, a),
250            None => format!("{}{}", url.to_owned(), path),
251        };
252
253        if self.debug {
254            log::debug!("URL={}", &request_url);
255        }
256        let req = self
257            .client
258            .get(&request_url)
259            .headers(Terra::construct_headers());
260
261        Terra::resp::<T>(&request_url, req).await
262    }
263
264    pub async fn fetch_url<T: for<'de> Deserialize<'de>>(
265        client: &reqwest::Client,
266        url: &str,
267        path: &str,
268        args: Option<&str>,
269    ) -> Result<T, TerraRustAPIError> {
270        let request_url = match args {
271            Some(a) => format!("{}{}{}", url.to_owned(), path, a),
272            None => format!("{}{}", url.to_owned(), path),
273        };
274
275        let req = client.get(&request_url).headers(Terra::construct_headers());
276
277        Terra::resp::<T>(&request_url, req).await
278    }
279
280    /// used to send a POST with a JSON body to the LCD
281    pub async fn post_cmd<R: for<'de> Serialize, T: for<'de> Deserialize<'de>>(
282        &self,
283        path: &str,
284        args: &R,
285    ) -> Result<T, TerraRustAPIError> {
286        let request_url = format!("{}{}", self.url.to_owned(), path);
287
288        if self.debug {
289            log::debug!("URL={}", &request_url);
290        }
291
292        let req = self
293            .client
294            .post(&request_url)
295            .headers(Terra::construct_headers())
296            .json::<R>(args);
297
298        Terra::resp::<T>(&request_url, req).await
299    }
300
301    async fn resp<T: for<'de> Deserialize<'de>>(
302        request_url: &str,
303        req: RequestBuilder,
304    ) -> Result<T, TerraRustAPIError> {
305        let response = req.send().await?;
306        let status = response.status();
307        if !&status.is_success() {
308            let status_text = response.text().await?;
309            //  eprintln!("{}", &request_url);
310            log::debug!("URL={} - {}", &request_url, &status_text);
311            Err(TerraRustAPIError::TerraLCDResponse(status, status_text))
312        } else {
313            let struct_response: T = response.json::<T>().await?;
314            Ok(struct_response)
315        }
316    }
317
318    /// Generate Fee structure, either by estimation method or hardcoded
319    ///
320
321    pub async fn calc_fees(
322        &self,
323        auth_account: &AuthAccount,
324        messages: &[Message],
325    ) -> Result<StdFee, TerraRustAPIError> {
326        match &self.gas_options {
327            None => Err(TerraRustAPIError::NoGasOpts),
328
329            Some(gas) => {
330                match &gas.fees {
331                    Some(f) => {
332                        let fee_coin: Coin = Coin::create(&f.denom, f.amount);
333                        Ok(StdFee::create(vec![fee_coin], gas.gas.unwrap_or(0)))
334                    }
335
336                    None => {
337                        let fee: StdFee = match &gas.estimate_gas {
338                            true => {
339                                let default_gas_coin = Coin::create("ukrw", dec!(1.0));
340                                let gas_coin = match &gas.gas_price {
341                                    Some(c) => c,
342                                    None => &default_gas_coin,
343                                };
344                                let res: LCDResult<TxFeeResult> = self
345                                    .tx()
346                                    .estimate_fee(
347                                        &auth_account.address,
348                                        messages,
349                                        gas.gas_adjustment.unwrap_or(1.0),
350                                        &[gas_coin],
351                                    )
352                                    .await?;
353                                //  let gas_amount = gas.gas_adjustment.unwrap_or(1.0) * res.result.gas as f64;
354                                let mut fees: Vec<Coin> = vec![];
355                                for fee in res.result.fee.amount {
356                                    fees.push(Coin::create(&fee.denom, fee.amount))
357                                }
358                                StdFee::create(fees, res.result.fee.gas as u64)
359                            }
360                            false => {
361                                let mut fees: Vec<Coin> = vec![];
362                                match &gas.fees {
363                                    Some(fee) => {
364                                        fees.push(Coin::create(&fee.denom, fee.amount));
365                                    }
366                                    None => {}
367                                }
368
369                                StdFee::create(fees, gas.gas.unwrap_or(0))
370                            }
371                        };
372                        Ok(fee)
373                    }
374                }
375            }
376        }
377    }
378
379    /// helper function to generate a 'StdSignMsg' & 'Signature' blocks to be used to broadcast a transaction
380    #[allow(clippy::too_many_arguments)]
381    fn generate_transaction_to_broadcast_fees<C: Signing + secp256k1::Context>(
382        chain_id: &str,
383        auth_account: &AuthAccount,
384        fee: StdFee,
385        secp: &Secp256k1<C>,
386        from: &PrivateKey,
387        messages: Vec<Message>,
388        memo: Option<String>,
389    ) -> Result<(StdSignMsg, Vec<StdSignature>), TerraRustAPIError> {
390        let account_number = auth_account.account_number;
391        let sequence = auth_account.sequence.unwrap_or(0);
392        let messages_len = messages.len();
393        let std_sign_msg = StdSignMsg {
394            chain_id: chain_id.to_string(), //: String::from(self.chain_id),
395            account_number,
396            sequence,
397            fee,
398            msgs: messages,
399            memo: memo.unwrap_or(format!(
400                "PFC-{}/{}",
401                NAME.unwrap_or("TERRA-RUST"),
402                VERSION.unwrap_or("dev")
403            )),
404        };
405        let js = serde_json::to_string(&std_sign_msg)?;
406        if js.len() > 1000 {
407            log::debug!(
408                "TO SIGN - {} {} {} #messages {}",
409                chain_id,
410                account_number,
411                sequence,
412                messages_len
413            );
414        } else {
415            log::debug!("TO SIGN - {}", js);
416        }
417
418        // eprintln!("Client.rs:311\n{}", js);
419        let sig = from.sign(secp, &js)?;
420        let sigs: Vec<StdSignature> = vec![sig];
421
422        Ok((std_sign_msg, sigs))
423    }
424
425    /// helper function to generate a 'StdSignMsg' & 'Signature' blocks to be used to broadcast a transaction
426    /// This version calculates fees, and obtains account# and sequence# as well
427    pub async fn generate_transaction_to_broadcast<C: secp256k1::Signing + secp256k1::Context>(
428        &self,
429        secp: &Secp256k1<C>,
430        from: &PrivateKey,
431        messages: Vec<Message>,
432        memo: Option<String>,
433    ) -> Result<(StdSignMsg, Vec<StdSignature>), TerraRustAPIError> {
434        let from_public = from.public_key(secp);
435        let from_account = from_public.account()?;
436        let auth = self.auth().account(&from_account).await?;
437        let fees = self.calc_fees(&auth.result.value, &messages).await?;
438        Terra::generate_transaction_to_broadcast_fees(
439            &self.chain_id,
440            &auth.result.value,
441            fees,
442            secp,
443            from,
444            messages,
445            memo,
446        )
447    }
448    /// helper: sign & submit the transaction sync
449    pub async fn submit_transaction_sync<C: Signing + secp256k1::Context>(
450        &self,
451        secp: &Secp256k1<C>,
452        from: &PrivateKey,
453        messages: Vec<Message>,
454        memo: Option<String>,
455    ) -> Result<TXResultSync, TerraRustAPIError> {
456        let (std_sign_msg, sigs) = self
457            .generate_transaction_to_broadcast(secp, from, messages, memo)
458            .await?;
459        let resp = self.tx().broadcast_sync(&std_sign_msg, &sigs).await?;
460
461        match resp.code {
462            Some(code) => Err(TxResultError(code, resp.txhash, resp.raw_log)),
463            None => Ok(resp),
464        }
465    }
466    /// helper: sign & submit the transaction async
467    pub async fn submit_transaction_async<C: Signing + secp256k1::Context>(
468        &self,
469        secp: &Secp256k1<C>,
470        from: &PrivateKey,
471        messages: Vec<Message>,
472        memo: Option<String>,
473    ) -> Result<TXResultAsync, TerraRustAPIError> {
474        let (std_sign_msg, sigs) = self
475            .generate_transaction_to_broadcast(secp, from, messages, memo)
476            .await?;
477        let resp = self.tx().broadcast_async(&std_sign_msg, &sigs).await?;
478        Ok(resp)
479    }
480
481    /// fetch the address book for the production network
482    pub async fn production_address_book() -> Result<AddressBook, TerraRustAPIError> {
483        Self::address_book(NETWORK_PROD_ADDRESS_BOOK).await
484    }
485    /// fetch the address book for the testnet network
486    pub async fn testnet_address_book() -> Result<AddressBook, TerraRustAPIError> {
487        Self::address_book(NETWORK_TEST_ADDRESS_BOOK).await
488    }
489    /// fetch a address book json structure
490    pub async fn address_book(addr_url: &str) -> Result<AddressBook, TerraRustAPIError> {
491        if let Some(file_name) = addr_url.strip_prefix("file://") {
492            let file = File::open(file_name).unwrap();
493            let add: AddressBook = serde_json::from_reader(file)?;
494            Ok(add)
495        } else {
496            let client = reqwest::Client::new();
497
498            let req = client.get(addr_url).headers(Self::construct_headers());
499            Ok(Self::resp::<AddressBook>(addr_url, req).await?)
500        }
501    }
502}
503#[cfg(test)]
504mod tst {
505    use super::*;
506    //use crate::client::auth::Auth;
507    use crate::core_types::{Coin, StdTx};
508    use crate::messages::MsgSend;
509    use crate::{PrivateKey, Terra};
510    use bitcoin::secp256k1::Secp256k1;
511
512    #[test]
513    pub fn test_send() -> Result<(), TerraRustAPIError> {
514        let str_1 = "island relax shop such yellow opinion find know caught erode blue dolphin behind coach tattoo light focus snake common size analyst imitate employ walnut";
515        let secp = Secp256k1::new();
516        let pk = PrivateKey::from_words(&secp, str_1, 0, 0)?;
517        let pub_k = pk.public_key(&secp);
518        let from_address = pub_k.account()?;
519        assert_eq!(from_address, "terra1n3g37dsdlv7ryqftlkef8mhgqj4ny7p8v78lg7");
520
521        let send = MsgSend::create_single(
522            from_address,
523            "terra1usws7c2c6cs7nuc8vma9qzaky5pkgvm2uag6rh".into(),
524            Coin::parse("100000uluna")?.unwrap(),
525        )?;
526        let json = serde_json::to_string(&send)?;
527        let json_eq = r#"{"type":"bank/MsgSend","value":{"amount":[{"amount":"100000","denom":"uluna"}],"from_address":"terra1n3g37dsdlv7ryqftlkef8mhgqj4ny7p8v78lg7","to_address":"terra1usws7c2c6cs7nuc8vma9qzaky5pkgvm2uag6rh"}}"#;
528
529        assert_eq!(json, json_eq);
530        let std_fee = StdFee::create_single(Coin::parse("50000uluna")?.unwrap(), 90000);
531
532        let messages: Vec<Message> = vec![send];
533        let auth_account = AuthAccount {
534            address: "terra1n3g37dsdlv7ryqftlkef8mhgqj4ny7p8v78lg7".to_string(),
535            public_key: None,
536            account_number: 43045,
537            sequence: Some(3),
538        };
539        let (sign_message, signatures) = Terra::generate_transaction_to_broadcast_fees(
540            "tequila-0004".into(),
541            &auth_account,
542            std_fee,
543            &secp,
544            &pk,
545            messages,
546            Some("PFC-terra-rust/0.1.5".into()),
547        )?;
548        let json_sign_message = serde_json::to_string(&sign_message)?;
549        let json_sign_message_eq = r#"{"account_number":"43045","chain_id":"tequila-0004","fee":{"amount":[{"amount":"50000","denom":"uluna"}],"gas":"90000"},"memo":"PFC-terra-rust/0.1.5","msgs":[{"type":"bank/MsgSend","value":{"amount":[{"amount":"100000","denom":"uluna"}],"from_address":"terra1n3g37dsdlv7ryqftlkef8mhgqj4ny7p8v78lg7","to_address":"terra1usws7c2c6cs7nuc8vma9qzaky5pkgvm2uag6rh"}}],"sequence":"3"}"#;
550        assert_eq!(json_sign_message, json_sign_message_eq);
551        let json_sig = serde_json::to_string(&signatures)?;
552        let json_sig_eq = r#"[{"signature":"f1wYTzbSyAYqN2tGR0A4PGmfyNYBUExpuoU7UOiBDpNoRlChF/BMtE7h6pdgbpu/V7jNzitu1Eb0fO35dxVkWA==","pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AiMzHaA2bvnDXfHzkjMM+vkSE/p0ymBtAFKUnUtQAeXe"}}]"#;
553        assert_eq!(json_sig, json_sig_eq);
554        let std_tx: StdTx = StdTx::from_StdSignMsg(&sign_message, &signatures, "sync");
555        let js_sig = serde_json::to_string(&std_tx)?;
556        let js_sig_eq = r#"{"tx":{"msg":[{"type":"bank/MsgSend","value":{"amount":[{"amount":"100000","denom":"uluna"}],"from_address":"terra1n3g37dsdlv7ryqftlkef8mhgqj4ny7p8v78lg7","to_address":"terra1usws7c2c6cs7nuc8vma9qzaky5pkgvm2uag6rh"}}],"fee":{"amount":[{"amount":"50000","denom":"uluna"}],"gas":"90000"},"signatures":[{"signature":"f1wYTzbSyAYqN2tGR0A4PGmfyNYBUExpuoU7UOiBDpNoRlChF/BMtE7h6pdgbpu/V7jNzitu1Eb0fO35dxVkWA==","pub_key":{"type":"tendermint/PubKeySecp256k1","value":"AiMzHaA2bvnDXfHzkjMM+vkSE/p0ymBtAFKUnUtQAeXe"}}],"memo":"PFC-terra-rust/0.1.5"},"mode":"sync"}"#;
557        assert_eq!(js_sig, js_sig_eq);
558        Ok(())
559    }
560
561    #[test]
562    pub fn test_wasm() -> Result<(), TerraRustAPIError> {
563        let key_words = "sell raven long age tooth still predict idea quit march gasp bamboo hurdle problem voyage east tiger divide machine brain hole tiger find smooth";
564        let secp = Secp256k1::new();
565        let private = PrivateKey::from_words(&secp, key_words, 0, 0)?;
566        let public_key = private.public_key(&secp);
567        let account = public_key.account()?;
568        assert_eq!(account, "terra1vr0e7kylhu9am44v0s3gwkccmz7k3naxysrwew");
569        //  let gas = GasOptions::create_with_fees("70000uluna", 200000)?;
570        //   let terra =
571        //       Terra::lcd_client("https://tequila-lcd.terra.dev", "tequila-0004", &gas, None).await?;
572        /*
573        TODO upgrade test to new version/bombay
574        let msg = MsgExecuteContract::create_from_b64(
575            &account,
576            "terra16ckeuu7c6ggu52a8se005mg5c0kd2kmuun63cu",
577            "eyJjYXN0X3ZvdGUiOnsicG9sbF9pZCI6NDQsInZvdGUiOiJ5ZXMiLCJhbW91bnQiOiIxMDAwMDAwIn19",
578            &vec![],
579        );
580
581        let json = serde_json::to_string(&msg)?;
582        let json_eq = r#"{"type":"wasm/MsgExecuteContract","value":{"coins":[],"contract":"terra16ckeuu7c6ggu52a8se005mg5c0kd2kmuun63cu","execute_msg":"eyJjYXN0X3ZvdGUiOnsicG9sbF9pZCI6NDQsInZvdGUiOiJ5ZXMiLCJhbW91bnQiOiIxMDAwMDAwIn19","sender":"terra1vr0e7kylhu9am44v0s3gwkccmz7k3naxysrwew"}}"#;
583
584        assert_eq!(json, json_eq);
585        let std_fee = StdFee::create_single(Coin::parse("70000uluna")?.unwrap(), 200000);
586        let auth_account = AuthAccount {
587            address: "terra1vr0e7kylhu9am44v0s3gwkccmz7k3naxysrwew".to_string(),
588            public_key: None,
589            account_number: 49411,
590            sequence: Some(0),
591        };
592        let messages: Vec<Message> = vec![msg];
593        let (sign_message, signatures) = Terra::generate_transaction_to_broadcast_fees(
594            "tequila-0004".into(),
595            &auth_account,
596            std_fee,
597            &secp,
598            &private,
599            &messages,
600            Some("PFC-terra-rust-anchor/0.1.1".into()),
601        )?;
602        let json_sign_message = serde_json::to_string(&sign_message)?;
603        let json_sign_message_eq = r#"{"account_number":"49411","chain_id":"tequila-0004","fee":{"amount":[{"amount":"70000","denom":"uluna"}],"gas":"200000"},"memo":"PFC-terra-rust-anchor/0.1.1","msgs":[{"type":"wasm/MsgExecuteContract","value":{"coins":[],"contract":"terra16ckeuu7c6ggu52a8se005mg5c0kd2kmuun63cu","execute_msg":"eyJjYXN0X3ZvdGUiOnsicG9sbF9pZCI6NDQsInZvdGUiOiJ5ZXMiLCJhbW91bnQiOiIxMDAwMDAwIn19","sender":"terra1vr0e7kylhu9am44v0s3gwkccmz7k3naxysrwew"}}],"sequence":"0"}"#;
604        assert_eq!(json_sign_message, json_sign_message_eq);
605        let json_sig = serde_json::to_string(&signatures)?;
606        let json_sig_eq = r#"[{"signature":"pCkd+nBaz1U3DYw0oY2Arxqc+3jI8QRdaXtYbIle9uh60POxvcUHVk2aN7VklgvnPKF7XGIF04U0sxpq/05Vqg==","pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3K4ruHQP1yY4dkCp41Djnx6z7KfMjDcvkIB93L3Po9C"}}]"#;
607        assert_eq!(json_sig, json_sig_eq);
608        let std_tx: StdTx = StdTx::from_StdSignMsg(&sign_message, &signatures, "sync");
609        let js_sig = serde_json::to_string(&std_tx)?;
610        let js_sig_eq = r#"{"tx":{"msg":[{"type":"wasm/MsgExecuteContract","value":{"coins":[],"contract":"terra16ckeuu7c6ggu52a8se005mg5c0kd2kmuun63cu","execute_msg":"eyJjYXN0X3ZvdGUiOnsicG9sbF9pZCI6NDQsInZvdGUiOiJ5ZXMiLCJhbW91bnQiOiIxMDAwMDAwIn19","sender":"terra1vr0e7kylhu9am44v0s3gwkccmz7k3naxysrwew"}}],"fee":{"amount":[{"amount":"70000","denom":"uluna"}],"gas":"200000"},"signatures":[{"signature":"pCkd+nBaz1U3DYw0oY2Arxqc+3jI8QRdaXtYbIle9uh60POxvcUHVk2aN7VklgvnPKF7XGIF04U0sxpq/05Vqg==","pub_key":{"type":"tendermint/PubKeySecp256k1","value":"A3K4ruHQP1yY4dkCp41Djnx6z7KfMjDcvkIB93L3Po9C"}}],"memo":"PFC-terra-rust-anchor/0.1.1"},"mode":"sync"}"#;
611        assert_eq!(js_sig, js_sig_eq);
612
613         */
614        Ok(())
615    }
616
617    #[tokio::test]
618    pub async fn test_address_book() -> Result<(), TerraRustAPIError> {
619        let prod = Terra::production_address_book().await?;
620        assert!(prod.addrs.len() > 0);
621        let test = Terra::testnet_address_book().await?;
622        assert!(test.addrs.len() > 0);
623        let file_version = Terra::address_book("file://resources/addressbook.json").await?;
624        assert_eq!(file_version.key, "775cf30a073ca5e97fb07a00");
625        assert!(file_version.addrs.len() > 1);
626        assert_eq!(
627            file_version.addrs[0].addr.id,
628            "ebca6b5d3cc2da9dfdfe4b1c045043fce686f143"
629        );
630
631        Ok(())
632    }
633}