electrs_query/
lib.rs

1use bitcoin_address;
2use bitcoin_utils::{
3    self, get_pubkey_hash_from_p2wpkh_address, get_public_key_hash_from_address,
4    get_script_hash_from_p2sh_address,
5};
6use electrs_request::{
7    BlockchainRelayFeeCommand, BlockchainScriptHashGetBalanceCommand,
8    BlockchainScriptHashGetHistoryCommand, BlockchainScriptHashListUnspentCommand,
9    Client as ElectrsRequestClient,
10};
11use hex_utilities::convert_big_endian_hex_to_little_endian;
12
13pub struct Client {
14    electrs_request_client: ElectrsRequestClient,
15}
16impl Client {
17    pub fn new(electrum_server_address: &str) -> Self {
18        let electrs_request_client = ElectrsRequestClient::new(electrum_server_address);
19        Client {
20            electrs_request_client,
21        }
22    }
23}
24
25#[derive(Debug)]
26pub struct AddressBalance {
27    pub confirmed: u64,
28    pub unconfirmed: u64,
29}
30#[derive(Debug)]
31pub struct HistoricalTransaction {
32    pub height: i64,
33    pub tx_hash: String,
34}
35
36fn get_script_hash_for_p2pkh_address(p2pkh_address: &str) -> String {
37    let p2pkh_address = p2pkh_address.to_string();
38    let p2pkh_pk_hash = get_public_key_hash_from_address(&p2pkh_address);
39    let p2pkh_script = format!("{}{}{}", "76a914", p2pkh_pk_hash, "88ac");
40    let p2pkh_script_hash = bitcoin_utils::sha256_hex(&p2pkh_script);
41    p2pkh_script_hash
42}
43fn get_script_hash_for_p2sh_address(p2sh_address: &str) -> String {
44    let p2sh_address = p2sh_address.to_string();
45    let p2sh_script_hash = get_script_hash_from_p2sh_address(&p2sh_address);
46    let p2sh_script = format!("{}{}{}", "a914", p2sh_script_hash, "87");
47    let p2sh_script_hash_sha256 = bitcoin_utils::sha256_hex(&p2sh_script);
48    p2sh_script_hash_sha256
49}
50fn get_script_hash_for_p2wpkh_address(p2wpkh_address: &str) -> String {
51    let p2wpkh_address = p2wpkh_address.to_string();
52    let p2wpkh_pk_hash = get_public_key_hash_from_address(&p2wpkh_address);
53    let p2wpkh_script = format!("{}{}", "0014", p2wpkh_pk_hash);
54    let p2wpkh_script_hash = bitcoin_utils::sha256_hex(&p2wpkh_script);
55    p2wpkh_script_hash
56}
57
58fn get_script_hash_for_address(address: &str) -> String {
59    let address = &address.to_string();
60    if bitcoin_address::is_p2pkh(address) {
61        get_script_hash_for_p2pkh_address(address)
62    } else if bitcoin_address::is_p2sh(address) {
63        get_script_hash_for_p2sh_address(address)
64    } else if bitcoin_address::is_p2wpkh(address) {
65        get_script_hash_for_p2wpkh_address(address)
66    } else {
67        panic!("Address type not supported: {}", address);
68    }
69}
70pub fn get_relay_fee(client: &Client) -> f64 {
71    let relay_fee_response = BlockchainRelayFeeCommand::new()
72        .call(&client.electrs_request_client)
73        .unwrap();
74    relay_fee_response.0
75}
76pub fn get_balance_for_address(address: &str, client: &Client) -> AddressBalance {
77    let script_hash = get_script_hash_for_address(address);
78    let script_hash_le = convert_big_endian_hex_to_little_endian(&script_hash);
79    let balance_response = BlockchainScriptHashGetBalanceCommand::new(&script_hash_le)
80        .call(&client.electrs_request_client)
81        .unwrap();
82    AddressBalance {
83        unconfirmed: balance_response.unconfirmed,
84        confirmed: balance_response.confirmed,
85    }
86}
87pub fn get_historical_transactions_for_address(
88    address: &str,
89    client: &Client,
90) -> Vec<HistoricalTransaction> {
91    let script_hash = get_script_hash_for_address(address);
92    let script_hash_le = convert_big_endian_hex_to_little_endian(&script_hash);
93    let get_history_response = BlockchainScriptHashGetHistoryCommand::new(&script_hash_le)
94        .call(&client.electrs_request_client)
95        .unwrap();
96    let historical_transactions = get_history_response
97        .0
98        .iter()
99        .map(|historical_transaction| HistoricalTransaction {
100            height: historical_transaction.height,
101            tx_hash: historical_transaction.tx_hash.clone(),
102        })
103        .collect();
104    historical_transactions
105}
106
107#[derive(Debug)]
108pub struct Utxo {
109    pub height: u64,
110    pub tx_hash: String,
111    pub tx_pos: u64,
112    pub value: u64,
113}
114pub fn get_utxos_for_address(address: &str, client: &Client) -> Vec<Utxo> {
115    let script_hash = get_script_hash_for_address(address);
116    let script_hash_le = convert_big_endian_hex_to_little_endian(&script_hash);
117    let list_unspent_response = BlockchainScriptHashListUnspentCommand::new(&script_hash_le)
118        .call(&client.electrs_request_client)
119        .unwrap();
120    let utxos = list_unspent_response
121        .0
122        .iter()
123        .map(|unspent| Utxo {
124            height: unspent.height,
125            tx_hash: unspent.tx_hash.clone(),
126            tx_pos: unspent.tx_pos,
127            value: unspent.value,
128        })
129        .collect();
130    utxos
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136
137    #[test]
138    fn it_works() {
139        // let result = add(2, 2);
140        // assert_eq!(result, 4);
141    }
142}