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                let result = Some(hex::encode(script_ref.to_unwrapped_bytes()));
66                Ok(result)
67            }
68            Type::PlutusV1 => {
69                let script_cbor = self
70                    .blockfrost_client
71                    .fetch_plutus_script_cbor(script_hash)
72                    .await?;
73
74                let normalized = normalize_plutus_script(&script_cbor)
75                    .map_err(WError::from_err("normalize_plutus_script"))?;
76                let script: PlutusScript = PlutusScript::from_hex_with_version(
77                    &normalized,
78                    &csl::Language::new_plutus_v1(),
79                )
80                .map_err(WError::from_err("from_hex_with_version"))?;
81                let script_ref: ScriptRef = to_script_ref(&ScriptType::Plutus(script));
82                let result = Some(hex::encode(script_ref.to_unwrapped_bytes()));
83
84                Ok(result)
85            }
86            Type::PlutusV2 => {
87                let script_cbor = self
88                    .blockfrost_client
89                    .fetch_plutus_script_cbor(script_hash)
90                    .await?;
91
92                let normalized = normalize_plutus_script(&script_cbor)
93                    .map_err(WError::from_err("normalize_plutus_script"))?;
94                let script: PlutusScript = PlutusScript::from_hex_with_version(
95                    &normalized,
96                    &csl::Language::new_plutus_v2(),
97                )
98                .map_err(WError::from_err("from_hex_with_version"))?;
99                let script_ref: ScriptRef = to_script_ref(&ScriptType::Plutus(script));
100                let result = Some(hex::encode(script_ref.to_unwrapped_bytes()));
101
102                Ok(result)
103            }
104            Type::PlutusV3 => {
105                let script_cbor = self
106                    .blockfrost_client
107                    .fetch_plutus_script_cbor(script_hash)
108                    .await?;
109
110                let normalized = normalize_plutus_script(&script_cbor)
111                    .map_err(WError::from_err("normalize_plutus_script"))?;
112                let script: PlutusScript = PlutusScript::from_hex_with_version(
113                    &normalized,
114                    &csl::Language::new_plutus_v3(),
115                )
116                .map_err(WError::from_err("from_hex_with_version"))?;
117                let script_ref: ScriptRef = to_script_ref(&ScriptType::Plutus(script));
118                let result = Some(hex::encode(script_ref.to_unwrapped_bytes()));
119
120                Ok(result)
121            }
122        }
123    }
124}
125
126pub fn normalize_plutus_script(script_hex: &str) -> Result<String, WError> {
127    apply_double_cbor_encoding(script_hex)
128}
129
130pub fn to_script_ref(script: &ScriptType) -> ScriptRef {
131    match script {
132        ScriptType::Plutus(plutus) => ScriptRef::new_plutus_script(plutus),
133        ScriptType::Native(native) => ScriptRef::new_native_script(native),
134    }
135}