1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
use bitcoin_address;
use bitcoin_utils::{
    self, get_pubkey_hash_from_p2wpkh_address, get_public_key_hash_from_address,
    get_script_hash_from_p2sh_address,
};
use electrs_request::{
    BlockchainRelayFeeCommand, BlockchainScriptHashGetBalanceCommand,
    BlockchainScriptHashListUnspentCommand, Client as ElectrsRequestClient,
};
use hex_utilities::convert_big_endian_hex_to_little_endian;

pub struct Client {
    electrs_request_client: ElectrsRequestClient,
}
impl Client {
    pub fn new(electrum_server_address: &str) -> Self {
        let electrs_request_client = ElectrsRequestClient::new(electrum_server_address);
        Client {
            electrs_request_client,
        }
    }
}

#[derive(Debug)]
pub struct AddressBalance {
    pub confirmed: u64,
    pub unconfirmed: u64,
}

fn get_script_hash_for_p2pkh_address(p2pkh_address: &str) -> String {
    let p2pkh_address = p2pkh_address.to_string();
    let p2pkh_pk_hash = get_public_key_hash_from_address(&p2pkh_address);
    let p2pkh_script = format!("{}{}{}", "76a914", p2pkh_pk_hash, "88ac");
    let p2pkh_script_hash = bitcoin_utils::sha256_hex(&p2pkh_script);
    p2pkh_script_hash
}
fn get_script_hash_for_p2sh_address(p2sh_address: &str) -> String {
    let p2sh_address = p2sh_address.to_string();
    let p2sh_script_hash = get_script_hash_from_p2sh_address(&p2sh_address);
    let p2sh_script = format!("{}{}{}", "a914", p2sh_script_hash, "87");
    let p2sh_script_hash_sha256 = bitcoin_utils::sha256_hex(&p2sh_script);
    p2sh_script_hash_sha256
}
fn get_script_hash_for_p2wpkh_address(p2wpkh_address: &str) -> String {
    let p2wpkh_address = p2wpkh_address.to_string();
    let p2wpkh_pk_hash = get_public_key_hash_from_address(&p2wpkh_address);
    let p2wpkh_script = format!("{}{}", "0014", p2wpkh_pk_hash);
    let p2wpkh_script_hash = bitcoin_utils::sha256_hex(&p2wpkh_script);
    p2wpkh_script_hash
}

fn get_script_hash_for_address(address: &str) -> String {
    let address = &address.to_string();
    if bitcoin_address::is_p2pkh(address) {
        get_script_hash_for_p2pkh_address(address)
    } else if bitcoin_address::is_p2sh(address) {
        get_script_hash_for_p2sh_address(address)
    } else if bitcoin_address::is_p2wpkh(address) {
        get_script_hash_for_p2wpkh_address(address)
    } else {
        panic!("Address type not supported: {}", address);
    }
}
pub fn get_relay_fee(client: &Client) -> f64 {
    let relay_fee_response = BlockchainRelayFeeCommand::new()
        .call(&client.electrs_request_client)
        .unwrap();
    relay_fee_response.0
}
pub fn get_balance_for_address(address: &str, client: &Client) -> AddressBalance {
    let script_hash = get_script_hash_for_address(address);
    let script_hash_le = convert_big_endian_hex_to_little_endian(&script_hash);
    let balance_response = BlockchainScriptHashGetBalanceCommand::new(&script_hash_le)
        .call(&client.electrs_request_client)
        .unwrap();
    AddressBalance {
        unconfirmed: balance_response.unconfirmed,
        confirmed: balance_response.confirmed,
    }
}

#[derive(Debug)]
pub struct Utxo {
    pub height: u64,
    pub tx_hash: String,
    pub tx_pos: u64,
    pub value: u64,
}
pub fn get_utxos_for_address(address: &str, client: &Client) -> Vec<Utxo> {
    let script_hash = get_script_hash_for_address(address);
    let script_hash_le = convert_big_endian_hex_to_little_endian(&script_hash);
    let list_unspent_response = BlockchainScriptHashListUnspentCommand::new(&script_hash_le)
        .call(&client.electrs_request_client)
        .unwrap();
    let utxos = list_unspent_response
        .0
        .iter()
        .map(|unspent| Utxo {
            height: unspent.height,
            tx_hash: unspent.tx_hash.clone(),
            tx_pos: unspent.tx_pos,
            value: unspent.value,
        })
        .collect();
    utxos
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        // let result = add(2, 2);
        // assert_eq!(result, 4);
    }
}