whisky_provider/blockfrost/utils/
utxo_utils.rs

1use whisky_common::{
2    models::{Asset, UTxO, UtxoInput, UtxoOutput},
3    WError,
4};
5use whisky_csl::{
6    apply_double_cbor_encoding,
7    csl::{self, NativeScript, PlutusScript, ScriptRef},
8};
9
10use crate::blockfrost::{
11    models::{utxo::BlockfrostUtxo, Script, Type},
12    BlockfrostProvider,
13};
14
15#[derive(Debug, Clone)]
16pub enum ScriptType {
17    Plutus(PlutusScript),
18    Native(NativeScript),
19}
20
21impl BlockfrostProvider {
22    pub async fn to_utxo(&self, utxo: &BlockfrostUtxo) -> Result<UTxO, WError> {
23        let utxo = UTxO {
24            input: UtxoInput {
25                output_index: utxo.output_index as u32,
26                tx_hash: utxo.tx_hash.clone(),
27            },
28            output: UtxoOutput {
29                address: utxo.address.clone(),
30                amount: utxo
31                    .amount
32                    .iter()
33                    .map(|asset| Asset::new(asset.unit.clone(), asset.quantity.to_string()))
34                    .collect(),
35                data_hash: utxo.data_hash.clone(),
36                plutus_data: utxo.inline_datum.clone(),
37                script_ref: match &utxo.reference_script_hash {
38                    Some(s) => self
39                        .resolve_script_ref(s)
40                        .await
41                        .map_err(WError::from_err("resolve_script_ref"))?,
42                    None => None,
43                },
44                script_hash: utxo.reference_script_hash.clone(),
45            },
46        };
47        Ok(utxo)
48    }
49
50    pub async fn resolve_script_ref(&self, script_hash: &str) -> Result<Option<String>, WError> {
51        let script: Script = self
52            .blockfrost_client
53            .fetch_specific_script(script_hash)
54            .await?;
55        match script.r#type {
56            Type::Timelock => {
57                let script_json = self
58                    .blockfrost_client
59                    .fetch_native_script_json(script_hash)
60                    .await?;
61                let script: NativeScript =
62                    NativeScript::from_json(&serde_json::json!(script_json).to_string())
63                        .map_err(WError::from_err("json to string"))?;
64                let script_ref = to_script_ref(&ScriptType::Native(script));
65                Ok(Some(
66                    script_ref
67                        .native_script()
68                        .ok_or_else(WError::from_opt("resolve_script_ref", "script_ref"))?
69                        .to_hex(),
70                ))
71            }
72            Type::PlutusV1 => {
73                let script_cbor = self
74                    .blockfrost_client
75                    .fetch_plutus_script_cbor(script_hash)
76                    .await?;
77
78                let normalized = normalize_plutus_script(&script_cbor)
79                    .map_err(WError::from_err("normalize_plutus_script"))?;
80                let script: PlutusScript = PlutusScript::from_hex_with_version(
81                    &normalized,
82                    &csl::Language::new_plutus_v1(),
83                )
84                .map_err(WError::from_err("from_hex_with_version"))?;
85                let script_ref: ScriptRef = to_script_ref(&ScriptType::Plutus(script));
86                let result = Some(
87                    script_ref
88                        .plutus_script()
89                        .ok_or_else(WError::from_opt("resolve_script_ref", "script_ref"))?
90                        .to_hex(),
91                );
92
93                Ok(result)
94            }
95            Type::PlutusV2 => {
96                let script_cbor = self
97                    .blockfrost_client
98                    .fetch_plutus_script_cbor(script_hash)
99                    .await?;
100
101                let normalized = normalize_plutus_script(&script_cbor)
102                    .map_err(WError::from_err("normalize_plutus_script"))?;
103                let script: PlutusScript = PlutusScript::from_hex_with_version(
104                    &normalized,
105                    &csl::Language::new_plutus_v2(),
106                )
107                .map_err(WError::from_err("from_hex_with_version"))?;
108                let script_ref: ScriptRef = to_script_ref(&ScriptType::Plutus(script));
109                let result = Some(
110                    script_ref
111                        .plutus_script()
112                        .ok_or_else(WError::from_opt("resolve_script_ref", "script_ref"))?
113                        .to_hex(),
114                );
115
116                Ok(result)
117            }
118            Type::PlutusV3 => {
119                let script_cbor = self
120                    .blockfrost_client
121                    .fetch_plutus_script_cbor(script_hash)
122                    .await?;
123
124                let normalized = normalize_plutus_script(&script_cbor)
125                    .map_err(WError::from_err("normalize_plutus_script"))?;
126                let script: PlutusScript = PlutusScript::from_hex_with_version(
127                    &normalized,
128                    &csl::Language::new_plutus_v3(),
129                )
130                .map_err(WError::from_err("from_hex_with_version"))?;
131                let script_ref: ScriptRef = to_script_ref(&ScriptType::Plutus(script));
132                let result = Some(
133                    script_ref
134                        .plutus_script()
135                        .ok_or_else(WError::from_opt("resolve_script_ref", "script_ref"))?
136                        .to_hex(),
137                );
138
139                Ok(result)
140            }
141        }
142    }
143}
144
145pub fn normalize_plutus_script(script_hex: &str) -> Result<String, WError> {
146    apply_double_cbor_encoding(script_hex)
147}
148
149pub fn to_script_ref(script: &ScriptType) -> ScriptRef {
150    match script {
151        ScriptType::Plutus(plutus) => ScriptRef::new_plutus_script(plutus),
152        ScriptType::Native(native) => ScriptRef::new_native_script(native),
153    }
154}