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 }
142}