web3_rust_wrapper/
lib.rs

1extern crate alloc;
2
3pub mod ethereum_mainnet;
4pub mod rinkeby_testnet;
5pub mod traits;
6
7use alloc::boxed::Box;
8use alloc::string::{String, ToString};
9use alloc::vec;
10use alloc::vec::Vec;
11use futures::StreamExt;
12use secp256k1::rand::rngs::StdRng;
13use secp256k1::rand::Rng;
14use secp256k1::{
15    rand::{rngs, SeedableRng},
16    PublicKey, SecretKey,
17};
18use serde::{Deserialize, Serialize};
19use std::any::{Any, TypeId};
20use std::collections::HashMap;
21use std::convert::{From, TryFrom};
22use std::str::FromStr;
23use std::time::SystemTime;
24use web3::api::SubscriptionStream;
25use web3::contract::tokens::{Detokenize, Tokenize};
26use web3::contract::{Contract, Options};
27use web3::ethabi::ethereum_types::H256;
28use web3::ethabi::{Int, Uint};
29use web3::helpers as w3h;
30use web3::signing::keccak256;
31use web3::transports::{Http, WebSocket};
32use web3::types::{
33    Address, BlockNumber, Bytes, FilterBuilder, Log, SignedTransaction, TransactionId,
34    TransactionParameters, H160, U256, U64,
35};
36use web3::Web3;
37
38// use hex_literal::hex;
39
40/// Emulates a `switch` statement.
41///
42/// The syntax is similar to `match` except that every left-side expression is
43/// interpreted as an expression rather than a pattern. The expression to
44/// compare against must be at the beginning with a semicolon. A default case
45/// is required at the end with a `_`, similar to `match`.
46///
47/// Example:
48///
49/// ```
50/// use switch_statement::switch;
51/// use web3_rust_wrapper::switch;
52///
53/// const A: u32 = 1 << 0;
54/// const B: u32 = 1 << 1;
55///
56/// let n = 3;
57/// let val = switch! { n;
58///     A => false,
59///     // this is a bitwise OR
60///     A | B => true,
61///     _ => false,
62/// };
63/// assert!(val);
64/// ```
65#[macro_export]
66macro_rules! switch {
67    ($v:expr; $($a:expr => $b:expr,)* _ => $e:expr $(,)?) => {
68        match $v {
69            $(v if v == $a => $b,)*
70            _ => $e,
71        }
72    };
73}
74
75#[cfg(test)]
76mod tests {
77    const A: u32 = 1 << 0;
78    const B: u32 = 1 << 1;
79    const C: u32 = 1 << 2;
80    const D: u32 = 1 << 3;
81
82    #[test]
83    fn it_works() {
84        assert!(switch! { 1; _ => true });
85
86        let v = switch! { A | B;
87            A => false,
88            B | C => false,
89            A | B => true,
90            C | D => {
91                unreachable!();
92            },
93            _ => false,
94        };
95        assert!(v);
96    }
97
98    #[test]
99    fn no_trailing_comma() {
100        let v = switch! { 1;
101            1 => true,
102            _ => false
103        };
104        assert!(v);
105    }
106}
107
108// use chainlink_interface::EthereumFeeds;
109trait InstanceOf
110where
111    Self: Any,
112{
113    fn instance_of<U: ?Sized + Any>(&self) -> bool {
114        TypeId::of::<Self>() == TypeId::of::<U>()
115    }
116}
117
118// implement this trait for every type that implements `Any` (which is most types)
119impl<T: ?Sized + Any> InstanceOf for T {}
120
121#[derive(Clone, Debug, Serialize, Deserialize)]
122pub struct KeyPair {
123    pub secret_key: String,
124    pub public_key: String,
125}
126
127#[derive(Clone, Debug)]
128pub struct EVMNetwork {
129    pub http_url: String,
130    pub ws_url: String,
131    pub chain_id: Option<u64>,
132}
133
134pub enum Network {
135    EthereumMainnet = 1,
136    EthereumGoerli = 5,
137    EthereumSepolia = 11155111,
138    BSCMainnet = 56,
139    BSCTestnet = 97,
140    AvalancheMainnet = 99,
141    AvalancheTestnet = 100,
142}
143
144impl EVMNetwork {
145    pub fn new(network_id: Network) -> EVMNetwork {
146        let mut _http_url = "";
147        let mut _socket_url = "";
148
149        match network_id {
150            Network::EthereumMainnet => {
151                _http_url = "https://mainnet.infura.io/v3/d39a866f4f6d49b9916f9269bf880110";
152                _socket_url = "https://goerli.infura.io/v3/d39a866f4f6d49b9916f9269bf880110";
153            }
154            Network::EthereumGoerli => {
155                _http_url = "https://goerli.infura.io/v3/d39a866f4f6d49b9916f9269bf880110";
156                _socket_url = "wss://goerli.infura.io/ws/v3/d39a866f4f6d49b9916f9269bf880110";
157            }
158            Network::EthereumSepolia => {
159                _http_url = "https://sepolia.infura.io/v3/d39a866f4f6d49b9916f9269bf880110";
160                _socket_url = "wss://sepolia.infura.io/ws/v3/d39a866f4f6d49b9916f9269bf880110";
161            }
162            Network::BSCMainnet => {
163                _http_url = "https://bsc-mainnet.nodereal.io/v1/35714f2a92134c78b61e57d04a9e82b0";
164                _socket_url =
165                    "wss://bsc-mainnet.nodereal.io/ws/v1/35714f2a92134c78b61e57d04a9e82b0";
166            }
167            Network::BSCTestnet => {
168                //_http_url = "https://rpc.ankr.com/bsc_testnet_chapel";
169                //_socket_url = "wss://bsc-testnet.nodereal.io/ws/v1/d4224d2458594df5830eb45cdef8b45b";
170
171                _http_url = "https://rpc.ankr.com/bsc_testnet_chapel/8bb975b26860eb14a52028cf0094617967e250459efe5360f1029369b445e6c0";
172                _socket_url =
173                    "wss://rpc.ankr.com/bsc_testnet_chapel/ws/8bb975b26860eb14a52028cf0094617967e250459efe5360f1029369b445e6c0";
174            }
175            Network::AvalancheMainnet => {
176                _http_url = "https://speedy-nodes-nyc.moralis.io/84a2745d907034e6d388f8d6/avalanche/mainnet";
177                _socket_url = "wss://speedy-nodes-nyc.moralis.io/84a2745d907034e6d388f8d6/avalanche/mainnet/ws";
178            }
179            Network::AvalancheTestnet => {
180                _http_url = "https://speedy-nodes-nyc.moralis.io/84a2745d907034e6d388f8d6/avalanche/mainnet";
181                _socket_url = "wss://speedy-nodes-nyc.moralis.io/84a2745d907034e6d388f8d6/avalanche/mainnet/ws";
182            }
183            _ => {
184                _http_url = "https://speedy-nodes-nyc.moralis.io/84a2745d907034e6d388f8d6/avalanche/mainnet";
185                _socket_url = "wss://speedy-nodes-nyc.moralis.io/84a2745d907034e6d388f8d6/avalanche/mainnet/ws";
186            }
187        }
188
189        let u64chain_id: u64 = network_id as u64;
190        println!("chain id: {}", u64chain_id);
191        let chain_id: Option<u64> = Option::Some(u64::try_from(u64chain_id).unwrap());
192
193        EVMNetwork {
194            http_url: String::from(_http_url),
195            ws_url: String::from(_socket_url),
196            chain_id,
197        }
198    }
199}
200
201#[test]
202fn new_network() {
203    let network = EVMNetwork::new(Network::BSCMainnet);
204    println!("{:?}", network);
205}
206
207// 0x9Ac64Cc6e4415144C455BD8E4837Fea55603e5c3
208#[derive(Clone, Debug)]
209pub struct Router {
210    pub address: String,
211    pub factory: String,
212}
213
214impl Router {
215    pub async fn new(network_id: Network) {}
216}
217
218#[derive(Clone, Debug)]
219pub struct Web3Manager {
220    // all the accounts
221    pub accounts: Vec<H160>,
222    // public addresses
223    pub web3http: Web3<Http>,
224    // web3 https instance (for use call or write contract functions)
225    pub web3web_socket: Web3<WebSocket>,
226    // web3 websocket instance (for listen contracts events)
227    accounts_map: HashMap<H160, String>,
228    current_nonce: U256,
229    // hashmap (like mapping on solidity) for store public and private keys
230    chain_id: Option<u64>,
231}
232
233impl Web3Manager {
234    //-------------------------------------------------------------------------
235    //                        getters
236    //-------------------------------------------------------------------------
237    pub fn get_current_nonce(&self) -> U256 {
238        self.current_nonce
239    }
240
241    //-------------------------------------------------------------------------
242    //                        setters
243    //-------------------------------------------------------------------------
244    pub fn set_current_nonce(&mut self, new_nonce: U256) {
245        self.current_nonce = new_nonce;
246    }
247
248    /*
249    pub async fn call_write_function<P: Clone>(
250        &mut self,
251        account: H160,
252        contract_instance: Contract<Http>,
253        contract_function: String,
254        params: P,
255    ) -> Result<H256, web3::Error>
256    where
257        P: Tokenize,
258    {
259        let tx_result = self
260            .sign_and_send_tx(
261                account,
262                &contract_instance,
263                &contract_function.to_string(),
264                &params,
265                U256::from_dec_str("0").unwrap(),
266            )
267            .await;
268        tx_result
269    }
270    */
271
272    pub async fn instance_contract(
273        &self,
274        plain_contract_address: &str,
275        abi_path: &[u8],
276    ) -> Result<Contract<Http>, Box<dyn std::error::Error>> {
277        Ok(Contract::from_json(
278            self.web3http.eth(),
279            Address::from_str(plain_contract_address)?,
280            abi_path,
281        )?)
282    }
283
284    pub fn generate_keypair() -> (SecretKey, PublicKey) {
285        let secp = secp256k1::Secp256k1::new();
286
287        let n2: u64 = 1;
288        println!("first random u64 is: {}", n2);
289        let mut rng: StdRng = rngs::StdRng::seed_from_u64(n2);
290        let random_number: u64 = rng.gen::<u64>();
291        println!(
292            "With seed {}, the first random u64 is: {}",
293            n2, random_number
294        );
295
296        secp.generate_keypair(&mut rng)
297    }
298
299    pub fn public_key_address(public_key: &PublicKey) -> Address {
300        let public_key = public_key.serialize_uncompressed();
301        debug_assert_eq!(public_key[0], 0x04);
302        let hash = keccak256(&public_key[1..]);
303        Address::from_slice(&hash[12..])
304    }
305
306    pub fn generate_keypairs(n: u8) -> Vec<(SecretKey, PublicKey)> {
307        let mut keypairs: Vec<(SecretKey, PublicKey)> = Vec::new();
308        for _ in 0..n {
309            keypairs.push(Web3Manager::generate_keypair());
310        }
311        keypairs
312    }
313
314    pub async fn get_token_balance(&self, token_address: &str, account: H160) -> U256 {
315        let token_abi = include_bytes!("../abi/TokenAbi.json");
316        let token_instance: Contract<Http> = self
317            .instance_contract(token_address, token_abi)
318            .await
319            .unwrap();
320
321        let token_decimals: U256 = self
322            .query_contract(&token_instance, "decimals", ())
323            .await
324            .unwrap();
325
326        let token_balance: U256 = self
327            .query_contract(&token_instance, "balanceOf", account)
328            .await
329            .unwrap();
330
331        //let a = token_balance * token_decimals;
332
333        //println!("a: {:?}", token_decimals);
334        println!("token_decimals: {:?}", token_decimals);
335        println!("token_balance: {:?}", token_balance);
336
337        token_balance
338    }
339
340    pub fn generate_deadline(&self) -> U256 {
341        U256::from(
342            SystemTime::now()
343                .duration_since(SystemTime::UNIX_EPOCH)
344                .unwrap()
345                .as_secs(),
346        ) + 1000usize
347    }
348
349    // TODO(elsuizo:2022-03-03): documentation here
350    pub async fn swap_tokens_for_exact_tokens(
351        &mut self,
352        account: H160,
353        router_address: &str,
354        token_amount: U256,
355        pairs: &[&str],
356        slippage: usize,
357    ) -> Result<H256, web3::Error> {
358        let contract_function = "swapTokensForExactTokens";
359
360        let router_abi = include_bytes!("../abi/PancakeRouterAbi.json");
361        let router_instance: Contract<Http> = self
362            .instance_contract(router_address, router_abi)
363            .await
364            .expect("error creating the router instance");
365
366        let mut addresses = Vec::new();
367        for pair in pairs {
368            addresses.push(Address::from_str(pair).unwrap());
369        }
370
371        let parameter_out = (token_amount, addresses.clone());
372        let amount_out_min: Vec<Uint> = self
373            .query_contract(&router_instance, "getAmountsOut", parameter_out)
374            .await
375            .unwrap();
376
377        let min_amount = U256::from(amount_out_min[1].as_u128());
378        let min_amount_less_slippage = min_amount - ((min_amount * slippage) / 100usize);
379
380        let parameters2 = (
381            token_amount,
382            min_amount_less_slippage,
383            addresses,
384            self.first_loaded_account(),
385            self.generate_deadline(),
386        );
387
388        println!("amount_out: {:?}", token_amount);
389        println!("min_amount_less_slippage: {:?}", min_amount_less_slippage);
390
391        let send_tx_result = self
392            .sign_and_send_tx(
393                account,
394                &router_instance,
395                contract_function,
396                &parameters2,
397                token_amount,
398            )
399            .await;
400
401        send_tx_result
402    }
403
404    pub async fn get_token_price(&mut self, router_address: &str, pairs: Vec<H160>) -> U256 {
405        let router_abi = include_bytes!("../abi/PancakeRouterAbi.json");
406        let router_instance: Contract<Http> = self
407            .instance_contract(router_address, router_abi)
408            .await
409            .expect("error creating the router instance");
410
411        let amount_out: U256 = U256::from_dec_str("1000000000000000000").unwrap();
412        let parameter_out = (amount_out, pairs.clone());
413        let amount_out_min: Vec<Uint> = self
414            .query_contract(&router_instance, "getAmountsOut", parameter_out)
415            .await
416            .unwrap();
417        let min_amount = U256::from(amount_out_min[1].as_u128());
418
419        min_amount
420    }
421
422    pub async fn swap_exact_tokens_for_tokens_supporting_fee_on_transfer_tokens(
423        &mut self,
424        account: H160,
425        router_address: &str,
426        token_amount: U256,
427        pairs: &[&str],
428    ) -> Result<H256, web3::Error> {
429        let contract_function: &str = "swapExactTokensForTokensSupportingFeeOnTransferTokens";
430
431        let router_abi = include_bytes!("../abi/PancakeRouterAbi.json");
432        let router_instance: Contract<Http> = self
433            .instance_contract(router_address, router_abi)
434            .await
435            .expect("error creating the router instance");
436
437        let mut addresses = Vec::new();
438        for pair in pairs {
439            addresses.push(Address::from_str(pair).unwrap());
440        }
441
442        let parameters = (
443            token_amount,
444            U256::from_dec_str("0").unwrap(),
445            addresses,
446            self.first_loaded_account(),
447            self.generate_deadline(),
448        );
449
450        let send_tx_result = self
451            .sign_and_send_tx(
452                account,
453                &router_instance,
454                contract_function,
455                &parameters,
456                U256::from_dec_str("0").unwrap(),
457            )
458            .await;
459
460        send_tx_result
461    }
462    pub async fn swap_eth_for_exact_tokens(
463        &mut self,
464        account: H160,
465        router_address: &str,
466        token_address: &str,
467        eth_amount: U256,
468        slippage: usize,
469    ) -> Result<H256, web3::Error> {
470        let mut router_abi_path = "../abi/PancakeRouterAbi.json";
471        let mut contract_function: &str = "swapExactETHForTokens";
472        let mut wbnb_address = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c";
473        let mut wbnb_address_testnet = "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd";
474
475        // 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c
476
477        let mut path_address: Vec<&str> = vec![];
478
479        switch! { router_address;
480            "0x9Ac64Cc6e4415144C455BD8E4837Fea55603e5c3" => {
481
482                router_abi_path = "../abi/PancakeRouterAbi.json";
483                contract_function = "swapExactETHForTokens";
484
485                path_address.push(wbnb_address_testnet);
486                path_address.push(token_address);
487
488            },
489                "0x10ed43c718714eb63d5aa57b78b54704e256024e" => {
490
491                router_abi_path = "../abi/PancakeRouterAbi.json";
492                contract_function = "swapExactETHForTokens";
493
494                path_address.push(wbnb_address);
495                path_address.push(token_address);
496
497            },
498            _ => {
499
500                router_abi_path = "../abi/PancakeRouterAbi.json";
501                contract_function = "swapExactETHForTokens";
502
503                path_address.push(wbnb_address_testnet);
504                path_address.push(token_address);
505            },
506        }
507
508        let router_abi = include_bytes!("../abi/PancakeRouterAbi.json");
509        let router_instance: Contract<Http> = self
510            .instance_contract(router_address, router_abi)
511            .await
512            .unwrap();
513
514        let mut addresses = Vec::new();
515        for pair in path_address {
516            addresses.push(Address::from_str(pair).unwrap());
517        }
518
519        let parameter_out = (eth_amount, addresses.clone());
520        let amount_out_min: Vec<Uint> = self
521            .query_contract(&router_instance, "getAmountsOut", parameter_out)
522            .await
523            .unwrap();
524
525        let min_amount = U256::from(amount_out_min[1].as_u128());
526        let min_amount_less_slippage = min_amount - ((min_amount * slippage) / 100usize);
527
528        let parameters = (
529            min_amount_less_slippage,
530            addresses,
531            account,
532            self.generate_deadline(),
533        );
534
535        println!("slippage {}", slippage);
536        println!("eth_amount {}", eth_amount);
537        println!("amount_out_min[0] {}", amount_out_min[0]);
538        println!("amount_out_min[1] {}", amount_out_min[1]);
539        println!("min_amount_less_slippage {}", min_amount_less_slippage);
540
541        let send_tx_result = self
542            .sign_and_send_tx(
543                account,
544                &router_instance,
545                contract_function,
546                &parameters,
547                eth_amount,
548            )
549            .await;
550
551        send_tx_result
552    }
553
554    pub async fn get_out_estimated_tokens_for_tokens(
555        &mut self,
556        contract_instance: &Contract<Http>,
557        pair_a: &str,
558        pair_b: &str,
559        amount: &str,
560    ) -> Result<U256, web3::contract::Error> {
561        self.query_contract(
562            contract_instance,
563            "getAmountsOut",
564            (
565                amount.to_string(),
566                vec![pair_a.to_string(), pair_b.to_string()],
567            ),
568        )
569        .await
570    }
571
572    pub async fn get_eth_balance(&mut self, account: H160) -> U256 {
573        let balance = self.web3http.eth().balance(account, None).await.unwrap();
574        return balance;
575    }
576
577    // Counts the number of exececuted transactions by the loaded wallet to set the 'nonce' param for current transacction
578    // Cuenta el número de transacciones se han ejecutado con la wallet cargada para establecer el parámetro 'nonce' en la transacción actual
579    pub async fn last_nonce(&self, account: H160) -> U256 {
580        let block_number: Option<BlockNumber> = Option::Some(BlockNumber::Pending);
581
582        let nonce = self
583            .web3http
584            .eth()
585            .transaction_count(account, block_number)
586            .await
587            .unwrap();
588
589        return nonce;
590
591        /*
592        self.web3http
593        .eth()
594        .transaction_count(self.first_loaded_account(), None)
595        .await
596        */
597    }
598
599    pub async fn load_account(
600        &mut self,
601        plain_address: &str,
602        plain_private_key: &str,
603    ) -> &mut Web3Manager {
604        // cast plain pk to sk type
605
606        let wallet: H160 = H160::from_str(plain_address).unwrap();
607
608        // push on account list
609        self.accounts_map
610            .insert(wallet, plain_private_key.to_string());
611        self.accounts.push(wallet);
612
613        // get last nonce from loaded account
614        let nonce: U256 = self.last_nonce(wallet).await;
615
616        self.set_current_nonce(nonce);
617
618        self
619    }
620
621    pub async fn new_from_rpc_url(
622        http_url: &str,
623        websocket_url: &str,
624        u64chain_id: u64,
625    ) -> Web3Manager {
626        // init web3 http connection
627        let web3http: Web3<Http> = web3::Web3::new(web3::transports::Http::new(http_url).unwrap());
628
629        // init web3 ws connection
630        let web3web_socket: Web3<WebSocket> = web3::Web3::new(
631            web3::transports::WebSocket::new(websocket_url)
632                .await
633                .unwrap(),
634        );
635
636        // create empty vector for store accounts
637        let accounts: Vec<Address> = vec![];
638        let accounts_map: HashMap<H160, String> = HashMap::new();
639        let current_nonce: U256 = U256::from_dec_str("0").unwrap();
640
641        //let chain_id: Option<u64> = Option::Some(u64::try_from(web3http.eth().chain_id().await.unwrap()).unwrap());
642        let chain_id: Option<u64> = Option::Some(u64::try_from(u64chain_id).unwrap());
643
644        Web3Manager {
645            accounts,
646            web3http,
647            web3web_socket,
648            accounts_map,
649            current_nonce,
650            chain_id,
651        }
652    }
653
654    pub async fn new(network_id: Network) -> Web3Manager {
655        // http_url: &str, websocket_url: &str, u64chain_id: u64
656        let network = EVMNetwork::new(network_id);
657
658        // init web3 http connection
659        let web3http: Web3<Http> =
660            web3::Web3::new(web3::transports::Http::new(network.http_url.as_str()).unwrap());
661
662        // init web3 ws connection
663        let web3web_socket: Web3<WebSocket> = web3::Web3::new(
664            web3::transports::WebSocket::new(network.ws_url.as_str())
665                .await
666                .unwrap(),
667        );
668
669        // create empty vector for store accounts
670        let accounts: Vec<Address> = vec![];
671        let accounts_map: HashMap<H160, String> = HashMap::new();
672        let current_nonce: U256 = U256::from_dec_str("0").unwrap();
673        let chain_id: Option<u64> = Option::Some(u64::try_from(network.chain_id.unwrap()).unwrap());
674
675        Web3Manager {
676            accounts,
677            web3http,
678            web3web_socket,
679            accounts_map,
680            current_nonce,
681            chain_id,
682        }
683    }
684
685    // Get a estimation on medium gas price in network
686    // Obtiene un precio del gas  estimado en la red
687    pub async fn gas_price(&self) -> Result<U256, web3::Error> {
688        self.web3http.eth().gas_price().await
689    }
690
691    // Get the current block in the network
692    // Obtiene el número del bloque actual en la red
693    pub async fn get_block(&self) -> Result<U64, web3::Error> {
694        self.web3http.eth().block_number().await
695    }
696
697    pub async fn query_contract<P, T>(
698        &self,
699        contract_instance: &Contract<Http>,
700        func: &str,
701        params: P,
702    ) -> Result<T, web3::contract::Error>
703    where
704        P: Tokenize,
705        T: Detokenize,
706    {
707        // query contract
708        contract_instance
709            .query(func, params, None, Default::default(), None)
710            .await
711    }
712
713    // The transactions must be signed with the private key of the wallet that executes it
714    // Las transacciones han de ser firmadas con la clave privada de la cartera que la ejecuta
715    pub async fn sign_transaction(
716        &mut self,
717        account: H160,
718        transact_obj: TransactionParameters,
719    ) -> SignedTransaction {
720        let plain_pk = self.accounts_map.get(&account).unwrap();
721        let private_key = SecretKey::from_str(plain_pk).unwrap();
722
723        self.web3http
724            .accounts()
725            .sign_transaction(transact_obj, &private_key)
726            .await
727            .unwrap()
728    }
729
730    pub fn encode_tx_parameters(
731        &mut self,
732        nonce: U256,
733        to: Address,
734        value: U256,
735        gas: U256,
736        gas_price: U256,
737        data: Bytes,
738    ) -> TransactionParameters {
739        TransactionParameters {
740            nonce: Some(nonce),
741            to: Some(to),
742            value,
743            gas_price: Some(gas_price),
744            gas,
745            data,
746            chain_id: self.chain_id,
747            ..Default::default()
748        }
749    }
750
751    // TODO(elsuizo:2022-03-03): add a `Result` here
752    pub fn encode_tx_data<P>(&mut self, contract: &Contract<Http>, func: &str, params: P) -> Bytes
753    where
754        P: Tokenize,
755    {
756        contract
757            .abi()
758            .function(func)
759            .unwrap()
760            .encode_input(&params.into_tokens())
761            .unwrap()
762            .into()
763    }
764
765    pub async fn estimate_tx_gasV1<P>(
766        &mut self,
767        contract: &Contract<Http>,
768        func: &str,
769        params: P,
770        value: &str,
771    ) -> U256
772    where
773        P: Tokenize,
774    {
775        let mut gas_estimation = U256::from_dec_str("0").unwrap();
776        let gas_estimation_result = contract
777            .estimate_gas(
778                func,
779                params,
780                self.accounts[0],
781                Options {
782                    value: Some(U256::from_dec_str(value).unwrap()),
783                    ..Default::default()
784                },
785            )
786            .await;
787        if gas_estimation_result.is_ok() {
788            gas_estimation = gas_estimation_result.unwrap();
789        }
790        gas_estimation
791    }
792
793    pub fn first_loaded_account(&self) -> H160 {
794        self.accounts[0]
795    }
796
797    pub async fn approve_erc20_token(
798        &mut self,
799        account: H160,
800        token_address: &str,
801        spender: &str,
802        value: &str,
803    ) -> Result<H256, web3::Error> {
804        let token_abi = include_bytes!("../abi/TokenAbi.json");
805        let token_instance: Contract<Http> = self
806            .instance_contract(token_address, token_abi)
807            .await
808            .unwrap();
809
810        let spender_address: Address = Address::from_str(spender).unwrap();
811        let contract_function = "approve";
812        let contract_function_parameters = (spender_address, U256::from_dec_str(value).unwrap());
813
814        let send_tx_result = self
815            .sign_and_send_tx(
816                account,
817                &token_instance,
818                &contract_function.to_string(),
819                &contract_function_parameters,
820                U256::from_dec_str("0").unwrap(),
821            )
822            .await;
823
824        Ok(send_tx_result.unwrap())
825    }
826
827    pub async fn sign_and_send_tx<P: Clone>(
828        &mut self,
829        account: H160,
830        contract_instance: &Contract<Http>,
831        func: &str,
832        params: &P,
833        value: U256,
834    ) -> Result<H256, web3::Error>
835    where
836        P: Tokenize,
837    {
838        // estimate gas for call this function with this parameters
839        // increase 200ms execution time, we use high gas available
840        // gas not used goes back to contract
841        //let estimated_tx_gas: U256 = U256::from_dec_str("5000000").unwrap();
842        let mut gas_estimation_result = contract_instance
843            .estimate_gas(
844                func,
845                params.clone(),
846                account,
847                Options {
848                    value: Some(value),
849                    ..Default::default()
850                },
851            )
852            .await;
853
854        let estimated_tx_gas = gas_estimation_result.unwrap();
855
856        /*
857         let estimated_tx_gas = U256::from_dec_str("5000000").unwrap();
858        // todo return err
859        let mut used_gas = U256::from_dec_str("0").unwrap();
860        if gas_estimation_result.is_err() {
861        } else {
862            used_gas = gas_estimation_result.unwrap();
863        }
864        */
865
866        // 2. encode_tx_data
867        let tx_data: Bytes = self.encode_tx_data(contract_instance, func, params.clone());
868        let gas_price: U256 = self.web3http.eth().gas_price().await.unwrap();
869        let mut nonce: U256 = self.get_current_nonce();
870
871        // 3. build tx parameters
872        let tx_parameters: TransactionParameters = self.encode_tx_parameters(
873            nonce,
874            contract_instance.address(),
875            value,
876            estimated_tx_gas,
877            gas_price,
878            tx_data,
879        );
880
881        // 4. sign tx and send tx
882        let tx_result = self.sign_and_send_transaction(account, tx_parameters).await;
883
884        self.update_nonce();
885        nonce = self.get_current_nonce();
886        println!("current_nonce after: {:?}", nonce);
887
888        return tx_result;
889    }
890
891    async fn sign_and_send_transaction(
892        &mut self,
893        account: H160,
894        tx_parameters: TransactionParameters,
895    ) -> Result<H256, web3::Error> {
896        let signed_transaction: SignedTransaction =
897            self.sign_transaction(account, tx_parameters).await;
898
899        // send tx
900        let tx_result = self
901            .web3http
902            .eth()
903            .send_raw_transaction(signed_transaction.raw_transaction)
904            .await;
905        tx_result
906    }
907
908    fn update_nonce(&mut self) {
909        self.set_current_nonce(self.get_current_nonce() + 1)
910    }
911
912    pub async fn sent_eth(&mut self, account: H160, to: H160, amount: &str) {
913        let amount_out: U256 = U256::from_dec_str(amount).unwrap();
914
915        // Build the tx object
916        let tx_object = TransactionParameters {
917            to: Some(to),
918            value: amount_out, //0.1 eth
919            ..Default::default()
920        };
921
922        let plain_pk = self.accounts_map.get(&account).unwrap();
923        let private_key = SecretKey::from_str(plain_pk).unwrap();
924
925        // Sign the tx (can be done offline)
926        let signed = self
927            .web3http
928            .accounts()
929            .sign_transaction(tx_object, &private_key)
930            .await
931            .unwrap();
932
933        // Send the tx to infura
934        let result = self
935            .web3http
936            .eth()
937            .send_raw_transaction(signed.raw_transaction)
938            .await
939            .unwrap();
940
941        println!("Tx succeeded with hash: {}", result);
942    }
943
944    pub async fn sent_erc20_token(
945        &mut self,
946        account: H160,
947        contract_instance: Contract<Http>,
948        to: &str,
949        token_amount: &str,
950    ) -> H256 {
951        let contract_function = "transfer";
952
953        let recipient_address: Address = Address::from_str(to).unwrap();
954        let contract_function_parameters =
955            (recipient_address, U256::from_dec_str(token_amount).unwrap());
956
957        let send_tx_result = self
958            .sign_and_send_tx(
959                account,
960                &contract_instance,
961                contract_function,
962                &contract_function_parameters,
963                U256::from_dec_str("0").unwrap(),
964            )
965            .await;
966
967        send_tx_result.unwrap()
968    }
969
970    //-------------------------------------------------------------------------
971    //                        chainlink inplementations
972    //-------------------------------------------------------------------------
973
974    pub async fn get_latest_price(
975        &mut self,
976        network: impl crate::traits::GetAddress,
977        pair_address: &str,
978    ) -> Int {
979        let proxy_abi = include_bytes!("../abi/EACAggregatorProxy.json");
980        let proxy_instance: Contract<Http> = self
981            .instance_contract(&network.get_address(pair_address).unwrap(), proxy_abi)
982            .await
983            .unwrap();
984
985        let res: (Uint, Int, Uint, Uint, Uint) = self
986            .query_contract(&proxy_instance, "latestRoundData", ())
987            .await
988            .unwrap();
989        res.1
990    }
991
992    /*
993        pub async fn access_controller(
994        &mut self,
995        feed: impl crate::traits::GetAddress,
996        pair: &str,
997    ) -> Result<Address, web3::contract::Error> {
998        let proxy_abi = include_bytes!("../abi/EACAggregatorProxy.json");
999        let proxy_instance: Contract<Http> = self
1000            .instance_contract(&feed.get_address(pair).unwrap(), proxy_abi)
1001            .await
1002            .expect("error creating the proxy instance");
1003        self.query_contract(&proxy_instance, "accessController", ())
1004            .await
1005    }
1006     */
1007
1008    pub async fn listen_contract_events(&mut self, contract_address: &str) {
1009        /*
1010        let filter = FilterBuilder::default()
1011        .address(vec![contract.address()])
1012        .topics(
1013            Some(vec![hex!(
1014                "d282f389399565f3671145f5916e51652b60eee8e5c759293a2f5771b8ddfd2e"
1015            )
1016            .into()]),
1017            None,
1018            None,
1019            None,
1020        )
1021        .build();
1022         */
1023
1024        let filter = FilterBuilder::default()
1025            .address(vec![Address::from_str(contract_address).unwrap()])
1026            .topics(None, None, None, None)
1027            .build();
1028
1029        let sub: SubscriptionStream<WebSocket, Log> = self
1030            .web3web_socket
1031            .eth_subscribe()
1032            .subscribe_logs(filter)
1033            .await
1034            .unwrap();
1035        sub.for_each(|log| async {
1036            let l: Log = log.unwrap();
1037            println!("Address: {:?}", l.transaction_hash.unwrap());
1038            println!("Data: {:?}", l.data);
1039            println!("Data0: {:?}", l.data.0);
1040            println!("{}", std::str::from_utf8(&l.data.0).unwrap());
1041            println!("topics: {:?}", l.topics);
1042            println!("log_type: {:?}", l.log_type);
1043
1044            let tx = self
1045                .web3http
1046                .eth()
1047                .transaction(TransactionId::Hash(l.transaction_hash.unwrap()))
1048                .await
1049                .unwrap()
1050                .unwrap();
1051
1052            let from_addr = tx.from.unwrap_or(H160::zero());
1053            let to_addr = tx.to.unwrap_or(H160::zero());
1054            let value = tx.value;
1055            let input = tx.input;
1056
1057            println!("from_addr: {:?}", from_addr);
1058            println!("to_addr: {:?}", to_addr);
1059            println!("value: {:?}", value);
1060            println!("input: {:?}", input);
1061        })
1062        .await;
1063    }
1064
1065    pub async fn build_contract_events(
1066        &mut self,
1067        contract_address: &str,
1068    ) -> SubscriptionStream<WebSocket, Log> {
1069        let filter = FilterBuilder::default()
1070            .address(vec![Address::from_str(contract_address).unwrap()])
1071            .topics(None, None, None, None)
1072            .build();
1073
1074        let sub: SubscriptionStream<WebSocket, Log> = self
1075            .web3web_socket
1076            .eth_subscribe()
1077            .subscribe_logs(filter)
1078            .await
1079            .unwrap();
1080        return sub;
1081    }
1082
1083    pub async fn init_pair(&self, lp_address: &str) -> Contract<Http> {
1084        let lp_pair_abi = include_bytes!("../abi/PancakeLPTokenAbi.json");
1085        let lp_pair_instance_address = lp_address;
1086        let lp_pair_instance: Contract<Http> = self
1087            .instance_contract(lp_pair_instance_address, lp_pair_abi)
1088            .await
1089            .expect("error creating the contract instance");
1090        lp_pair_instance
1091    }
1092
1093    pub async fn init_router_factory(&mut self, factory_address: &str) -> Contract<Http> {
1094        let factory_abi = include_bytes!("../abi/PancakeFactoryAbi.json");
1095        let factory_instance: Contract<Http> = self
1096            .instance_contract(factory_address, factory_abi)
1097            .await
1098            .expect("error creating the contract instance");
1099        factory_instance
1100    }
1101
1102    pub async fn init_router(&mut self, router_address: &str) -> Contract<Http> {
1103        //let abi: Abi = load_abi_from_json("factoryabi.json");
1104        let router_abi = include_bytes!("../abi/PancakeRouterAbi.json");
1105
1106        let router_instance: Contract<Http> = self
1107            .instance_contract(router_address, router_abi)
1108            .await
1109            .expect("error creating the contract instance");
1110        router_instance
1111    }
1112
1113    pub async fn token_has_liquidity(&self, lp_pair_factory_instance: Contract<Http>) -> bool {
1114        let lp_pair_reserves: (Uint, Uint, Uint) = self
1115            .query_contract(&lp_pair_factory_instance, "getReserves", ())
1116            .await
1117            .unwrap();
1118        lp_pair_reserves.0 > U256::from(0) && lp_pair_reserves.1 > U256::from(0)
1119    }
1120
1121    pub async fn find_lp_pair(&mut self, factory_address: &str, token_address: &str) -> String {
1122        let factory_instance = self.init_router_factory(factory_address).await;
1123
1124        let weth = "0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd";
1125        //let busd = "0x78867BbEeF44f2326bF8DDd1941a4439382EF2A7";
1126        let initial_lp_address = "0x0000000000000000000000000000000000000000";
1127        //let mut lp_token_address;
1128
1129        let lp_pair_address: H160 = self
1130            .query_contract(
1131                &factory_instance,
1132                "getPair",
1133                (
1134                    H160::from_str(weth).unwrap(),
1135                    H160::from_str(token_address).unwrap(),
1136                ),
1137            )
1138            .await
1139            .unwrap();
1140
1141        let mut lp_token_address = w3h::to_string(&lp_pair_address).replace("\"", "");
1142        if lp_token_address == initial_lp_address {
1143            let lp_pair_address: H160 = self
1144                .query_contract(
1145                    &factory_instance,
1146                    "getPair",
1147                    (
1148                        H160::from_str(weth).unwrap(),
1149                        H160::from_str(token_address).unwrap(),
1150                    ),
1151                )
1152                .await
1153                .unwrap();
1154            lp_token_address = w3h::to_string(&lp_pair_address).replace("\"", "")
1155        }
1156        lp_token_address
1157    }
1158
1159    pub async fn get_token_reserves(
1160        &mut self,
1161        lp_pair_factory_instance: Contract<Http>,
1162    ) -> (U256, U256, U256) {
1163        let lp_pair_reserves: (Uint, Uint, Uint) = self
1164            .query_contract(&lp_pair_factory_instance, "getReserves", ())
1165            .await
1166            .unwrap();
1167        println!("lp_pair_reserves: {:?}", lp_pair_reserves);
1168        lp_pair_reserves
1169    }
1170}