whisky_provider/blockfrost/
fetcher.rs1use 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}