whisky_provider/blockfrost/
fetcher.rs

1use super::models::account::BlockfrostAccountInfo;
2use super::models::asset::{AssetAddresses, AssetPolicy, BlockfrostAsset};
3use super::models::block::BlockContent;
4use super::models::epoch::EpochParam;
5use super::models::transaction::{BlockfrostTxInfo, BlockfrostTxUtxo};
6use super::models::utxo::BlockfrostUtxo;
7use super::utils::*;
8use super::BlockfrostProvider;
9use async_trait::async_trait;
10
11use futures::future;
12use std::collections::HashMap;
13use whisky_common::models::{AccountInfo, BlockInfo, Protocol, TransactionInfo, UTxO};
14
15use whisky_common::*;
16
17#[async_trait]
18impl Fetcher for BlockfrostProvider {
19    async fn fetch_account_info(&self, address: &str) -> Result<AccountInfo, WError> {
20        let reward_address = if address.starts_with("addr") {
21            resolve_reward_address(address).map_err(WError::from_err("resolve_reward_address"))?
22        } else {
23            address.to_string()
24        };
25
26        let url = format!("/accounts/{}", reward_address);
27
28        let resp = self
29            .blockfrost_client
30            .get(&url)
31            .await
32            .map_err(WError::from_err("blockfrost::fetch_account_info get"))?;
33
34        let blockfrost_account_info: BlockfrostAccountInfo = serde_json::from_str(&resp).map_err(
35            WError::from_err("blockfrost::fetch_account_info type error"),
36        )?;
37
38        let account_info: AccountInfo =
39            blockfrost_account_info_to_account_info(blockfrost_account_info);
40        Ok(account_info)
41    }
42
43    async fn fetch_address_utxos(
44        &self,
45        address: &str,
46        asset: Option<&str>,
47    ) -> Result<Vec<UTxO>, WError> {
48        let mut page = 1;
49        let mut added_utxos: Vec<UTxO> = Vec::new();
50
51        loop {
52            let append_asset_string = asset.map_or_else(String::new, |a| a.to_string());
53            let append_page_string = format!("?page={}", page);
54
55            let url = format!(
56                "/addresses/{}/utxos/{}{}",
57                address, append_asset_string, append_page_string
58            );
59
60            let resp = self
61                .blockfrost_client
62                .get(&url)
63                .await
64                .map_err(WError::from_err("blockfrost::fetch_address_utxos get"))?;
65
66            let blockfrost_utxos: Vec<BlockfrostUtxo> = serde_json::from_str(&resp).map_err(
67                WError::from_err("blockfrost::fetch_address_utxos type error"),
68            )?;
69
70            let uxtos: Vec<UTxO> =
71                future::join_all(blockfrost_utxos.iter().map(|utxo| self.to_utxo(utxo)))
72                    .await
73                    .into_iter()
74                    .collect::<Result<Vec<_>, _>>()?;
75
76            added_utxos.extend(uxtos);
77
78            if blockfrost_utxos.len() < 100 {
79                break;
80            }
81
82            page += 1;
83        }
84
85        Ok(added_utxos)
86    }
87
88    async fn fetch_asset_addresses(&self, asset: &str) -> Result<Vec<(String, String)>, WError> {
89        let mut page = 1;
90        let mut added_assets: Vec<(String, String)> = Vec::new();
91
92        loop {
93            let (policy_id, asset_name) = Asset::unit_to_tuple(asset);
94            let append_page_string = format!("?page={}", page);
95
96            let url = format!(
97                "/assets/{}{}/addresses{}",
98                policy_id, asset_name, append_page_string
99            );
100
101            let resp = self
102                .blockfrost_client
103                .get(&url)
104                .await
105                .map_err(WError::from_err("blockfrost::fetch_asset_addresses get"))?;
106
107            let blockfrost_assets: Vec<AssetAddresses> = serde_json::from_str(&resp).map_err(
108                WError::from_err("blockfrost::fetch_asset_addresses type error"),
109            )?;
110
111            let assets: Vec<(String, String)> = blockfrost_assets
112                .iter()
113                .map(|asset| (asset.address.clone(), asset.quantity.to_string()))
114                .collect();
115
116            added_assets.extend(assets);
117
118            if blockfrost_assets.len() < 100 {
119                break;
120            }
121
122            page += 1;
123        }
124        Ok(added_assets)
125    }
126
127    async fn fetch_asset_metadata(
128        &self,
129        asset: &str,
130    ) -> Result<Option<HashMap<String, serde_json::Value>>, WError> {
131        let (policy_id, asset_name) = Asset::unit_to_tuple(asset);
132        let url = format!("/assets/{}{}", &policy_id, &asset_name);
133        let resp = self
134            .blockfrost_client
135            .get(&url)
136            .await
137            .map_err(WError::from_err("blockfrost::fetch_asset_metadata get"))?;
138
139        let blockfrost_asset: BlockfrostAsset = serde_json::from_str(&resp).map_err(
140            WError::from_err("blockfrost::fetch_asset_metadata type error"),
141        )?;
142
143        let asset_metadata: HashMap<String, serde_json::Value> =
144            serde_json::to_value(&blockfrost_asset)
145                .expect("Failed to convert object to JSON")
146                .as_object()
147                .map(|obj| {
148                    obj.iter()
149                        .map(|(k, v)| (k.to_string(), v.clone()))
150                        .collect()
151                })
152                .unwrap_or_default();
153
154        Ok(Some(asset_metadata))
155    }
156
157    async fn fetch_block_info(&self, hash: &str) -> Result<BlockInfo, WError> {
158        let url = format!("/blocks/{}", hash);
159
160        let resp = self
161            .blockfrost_client
162            .get(&url)
163            .await
164            .map_err(WError::from_err("blockfrost::fetch_block_info get"))?;
165        let block_content: BlockContent = serde_json::from_str(&resp)
166            .map_err(WError::from_err("blockfrost::fetch_block_info type error"))?;
167
168        let block_info: BlockInfo = block_content_to_block_info(block_content)
169            .map_err(WError::from_err("blockfrost::fetch_block_info"))?;
170
171        Ok(block_info)
172    }
173
174    async fn fetch_collection_assets(
175        &self,
176        policy_id: &str,
177        cursor: Option<String>,
178    ) -> Result<(Vec<(String, String)>, Option<String>), WError> {
179        let cursor = cursor.unwrap_or("1".to_string());
180
181        let append_page_string = format!("?page={}", cursor);
182
183        let url = format!("/assets/policy/{}{}", policy_id, append_page_string);
184
185        let resp = self
186            .blockfrost_client
187            .get(&url)
188            .await
189            .map_err(WError::from_err("blockfrost::fetch_collection_assets get"))?;
190
191        let asset_policies: Vec<AssetPolicy> = serde_json::from_str(&resp).map_err(
192            WError::from_err("blockfrost::fetch_collection_assets type error"),
193        )?;
194
195        let assets: Vec<(String, String)> = asset_policies
196            .iter()
197            .map(|asset| (asset.asset.clone(), asset.quantity.clone()))
198            .collect();
199
200        let updated_cursor: Option<String> = if asset_policies.len() == 100 {
201            Some((cursor.parse::<i32>().unwrap_or(1) + 1).to_string())
202        } else {
203            None
204        };
205
206        Ok((assets, updated_cursor))
207    }
208
209    async fn fetch_protocol_parameters(&self, epoch: Option<u32>) -> Result<Protocol, WError> {
210        let append_epoch_string = match epoch {
211            Some(c) => format!("{}", c),
212            None => "latest".to_string(),
213        };
214
215        let url = format!("/epochs/{}/parameters", append_epoch_string);
216
217        let resp = self
218            .blockfrost_client
219            .get(&url)
220            .await
221            .map_err(WError::from_err(
222                "blockfrost::fetch_protocol_parameters get",
223            ))?;
224
225        let epoch_param: EpochParam = serde_json::from_str(&resp).map_err(WError::from_err(
226            "blockfrost::fetch_protocol_parameters type error",
227        ))?;
228
229        let protocol: Protocol = epoch_param_to_protocol(epoch_param)
230            .map_err(WError::from_err("blockfrost::fetch_protocol_parameters"))?;
231
232        Ok(protocol)
233    }
234
235    async fn fetch_tx_info(&self, hash: &str) -> Result<TransactionInfo, WError> {
236        let tx_url = format!("/txs/{}", hash);
237
238        let tx_resp = self
239            .blockfrost_client
240            .get(&tx_url)
241            .await
242            .map_err(WError::from_err("blockfrost::fetch_tx_info get"))?;
243
244        let blockfrost_tx_info: BlockfrostTxInfo = serde_json::from_str(&tx_resp)
245            .map_err(WError::from_err("blockfrost::fetch_tx_info type error"))?;
246
247        let utxo_url = format!("/txs/{}/utxos", hash);
248
249        let utxo_resp = self
250            .blockfrost_client
251            .get(&utxo_url)
252            .await
253            .map_err(WError::from_err("blockfrost_::fetch_utxos get"))?;
254
255        let blockfrost_tx_utxo: BlockfrostTxUtxo = serde_json::from_str(&utxo_resp)
256            .map_err(WError::from_err("blockfrost_::fetch_utxos type error"))?;
257
258        let blockfrost_inputs: Vec<BlockfrostUtxo> = blockfrost_tx_utxo
259            .outputs
260            .iter()
261            .map(|utxo| {
262                blockfrost_tx_output_utxo_to_blockfrost_utxo(utxo, &blockfrost_tx_utxo.hash)
263            })
264            .collect();
265
266        let inputs: Vec<UTxO> =
267            future::join_all(blockfrost_inputs.iter().map(|utxo| self.to_utxo(utxo)))
268                .await
269                .into_iter()
270                .collect::<Result<Vec<_>, _>>()?;
271
272        let blockfrost_outputs: Vec<BlockfrostUtxo> = blockfrost_tx_utxo
273            .outputs
274            .iter()
275            .map(|utxo| {
276                blockfrost_tx_output_utxo_to_blockfrost_utxo(utxo, &blockfrost_tx_utxo.hash)
277            })
278            .collect();
279
280        let outputs: Vec<UTxO> =
281            future::join_all(blockfrost_outputs.iter().map(|utxo| self.to_utxo(utxo)))
282                .await
283                .into_iter()
284                .collect::<Result<Vec<_>, _>>()?;
285
286        let transaction_info: TransactionInfo =
287            blockfrost_txinfo_to_txinfo(blockfrost_tx_info, inputs, outputs);
288
289        Ok(transaction_info)
290    }
291
292    async fn fetch_utxos(&self, hash: &str, index: Option<u32>) -> Result<Vec<UTxO>, WError> {
293        let url = format!("/txs/{}/utxos", hash);
294
295        let resp = self
296            .blockfrost_client
297            .get(&url)
298            .await
299            .map_err(WError::from_err("blockfrost_::fetch_utxos get"))?;
300
301        let blockfrost_tx_utxo: BlockfrostTxUtxo = serde_json::from_str(&resp)
302            .map_err(WError::from_err("blockfrost_::fetch_utxos type error"))?;
303
304        let blockfrost_utxos: Vec<BlockfrostUtxo> = blockfrost_tx_utxo
305            .outputs
306            .iter()
307            .map(|utxo| {
308                blockfrost_tx_output_utxo_to_blockfrost_utxo(utxo, &blockfrost_tx_utxo.hash)
309            })
310            .collect();
311
312        let outputs: Vec<UTxO> =
313            future::join_all(blockfrost_utxos.iter().map(|utxo| self.to_utxo(utxo)))
314                .await
315                .into_iter()
316                .collect::<Result<Vec<_>, _>>()?;
317
318        let utxos = match index {
319            Some(i) => outputs
320                .iter()
321                .filter(|output| output.input.output_index == i)
322                .cloned()
323                .collect(),
324            None => outputs,
325        };
326
327        Ok(utxos)
328    }
329
330    async fn get(&self, url: &str) -> Result<serde_json::Value, WError> {
331        let resp = self
332            .blockfrost_client
333            .get(url)
334            .await
335            .map_err(WError::from_err("blockfrost::get"))?;
336        let any =
337            serde_json::from_str(&resp).map_err(WError::from_err("blockfrost::get error type"))?;
338        Ok(any)
339    }
340}