cml_chain_wasm/plutus/
utils.rs

1use crate::{
2    plutus::{PlutusData, Redeemers},
3    LegacyRedeemerList, PlutusDataList,
4};
5use cml_chain::plutus::Language;
6use cml_core_wasm::{
7    impl_raw_bytes_api, impl_wasm_cbor_api, impl_wasm_cbor_json_api, impl_wasm_conversions,
8};
9use cml_crypto_wasm::ScriptHash;
10use wasm_bindgen::prelude::{wasm_bindgen, JsError};
11
12use super::{ExUnits, PlutusV1Script, PlutusV2Script, PlutusV3Script};
13
14#[derive(Clone, Debug)]
15#[wasm_bindgen]
16pub struct ConstrPlutusData(cml_chain::plutus::ConstrPlutusData);
17
18impl_wasm_conversions!(cml_chain::plutus::ConstrPlutusData, ConstrPlutusData);
19
20impl_wasm_cbor_json_api!(ConstrPlutusData);
21
22#[wasm_bindgen]
23impl ConstrPlutusData {
24    pub fn alternative(&self) -> u64 {
25        self.0.alternative
26    }
27
28    pub fn fields(&self) -> PlutusDataList {
29        self.0.fields.clone().into()
30    }
31
32    pub fn new(alternative: u64, fields: &PlutusDataList) -> Self {
33        Self(cml_chain::plutus::ConstrPlutusData::new(
34            alternative,
35            fields.clone().into(),
36        ))
37    }
38}
39
40#[wasm_bindgen]
41impl PlutusData {
42    /**
43     *  Convert to a Datum that will serialize equivalent to cardano-node's format
44     *
45     *  Please VERY STRONGLY consider using PlutusData::from_cbor_bytes() instead wherever possible.
46     * You should try to never rely on a tool encoding CBOR a certain way as there are many possible,
47     * and just because it matches with a specific datum, doesn't mean that a different datum won't differ.
48     * This is critical as that means the datum hash won't match.
49     * After creation a datum (or other hashable CBOR object) should only be treated as raw CBOR bytes,
50     * or through a type that respects its specific CBOR format e.g. CML's PlutusData::from_cbor_bytes()
51     *
52     *  This function is just here in case there's no possible way at all to create from CBOR bytes and
53     * thus cold only be constructed manually and then had this function called on it.
54     *
55     *  This is also the format that CSL and Lucid use
56     */
57    pub fn to_cardano_node_format(&self) -> Self {
58        self.0.to_cardano_node_format().into()
59    }
60}
61
62#[derive(Clone, Debug)]
63#[wasm_bindgen]
64pub struct PlutusMap(cml_chain::plutus::PlutusMap);
65
66impl_wasm_conversions!(cml_chain::plutus::PlutusMap, PlutusMap);
67
68impl_wasm_cbor_api!(PlutusMap);
69
70#[wasm_bindgen]
71impl PlutusMap {
72    pub fn new() -> Self {
73        Self(cml_chain::plutus::PlutusMap::new())
74    }
75
76    pub fn len(&self) -> usize {
77        self.0.len()
78    }
79
80    pub fn is_empty(&self) -> bool {
81        self.0.is_empty()
82    }
83
84    /// Replaces all datums of a given key, if any exist.
85    pub fn set(&mut self, key: &PlutusData, value: &PlutusData) {
86        self.0.set(key.clone().into(), value.clone().into())
87    }
88
89    /// Gets the plutus datum corresponding to a given key, if it exists.
90    /// Note: In the case of duplicate keys this only returns the first datum.
91    /// This is an extremely rare occurence on-chain but can happen.
92    pub fn get(&self, key: &PlutusData) -> Option<PlutusData> {
93        self.0.get(key.as_ref()).map(|pd| pd.clone().into())
94    }
95
96    /// In the extremely unlikely situation there are duplicate keys, this gets all of a single key
97    pub fn get_all(&self, key: &PlutusData) -> Option<PlutusDataList> {
98        self.0
99            .get_all(key.as_ref())
100            .map(|datums| datums.into_iter().cloned().collect::<Vec<_>>().into())
101    }
102
103    pub fn keys(&self) -> PlutusDataList {
104        PlutusDataList(
105            self.0
106                .entries
107                .iter()
108                .map(|(k, _v)| k.clone())
109                .collect::<Vec<_>>(),
110        )
111    }
112}
113
114/// Version-agnostic Plutus script
115#[wasm_bindgen]
116#[derive(Clone, Debug)]
117pub struct PlutusScript(cml_chain::plutus::utils::PlutusScript);
118
119impl_wasm_conversions!(cml_chain::plutus::utils::PlutusScript, PlutusScript);
120
121#[wasm_bindgen]
122impl PlutusScript {
123    pub fn from_v1(script: &PlutusV1Script) -> Self {
124        cml_chain::plutus::utils::PlutusScript::PlutusV1(script.as_ref().clone()).into()
125    }
126
127    pub fn from_v2(script: &PlutusV2Script) -> Self {
128        cml_chain::plutus::utils::PlutusScript::PlutusV2(script.as_ref().clone()).into()
129    }
130
131    pub fn from_v3(script: &PlutusV3Script) -> Self {
132        cml_chain::plutus::utils::PlutusScript::PlutusV3(script.as_ref().clone()).into()
133    }
134
135    pub fn hash(&self) -> ScriptHash {
136        self.0.hash().into()
137    }
138
139    pub fn as_v1(&self) -> Option<PlutusV1Script> {
140        match &self.0 {
141            cml_chain::plutus::utils::PlutusScript::PlutusV1(v1) => Some(v1.clone().into()),
142            _ => None,
143        }
144    }
145
146    pub fn as_v2(&self) -> Option<PlutusV2Script> {
147        match &self.0 {
148            cml_chain::plutus::utils::PlutusScript::PlutusV2(v2) => Some(v2.clone().into()),
149            _ => None,
150        }
151    }
152
153    pub fn as_v3(&self) -> Option<PlutusV3Script> {
154        match &self.0 {
155            cml_chain::plutus::utils::PlutusScript::PlutusV3(v3) => Some(v3.clone().into()),
156            _ => None,
157        }
158    }
159
160    pub fn version(&self) -> Language {
161        self.0.version()
162    }
163}
164
165#[wasm_bindgen]
166impl PlutusV1Script {
167    pub fn hash(&self) -> ScriptHash {
168        self.0.hash().into()
169    }
170}
171
172#[wasm_bindgen]
173impl PlutusV2Script {
174    pub fn hash(&self) -> ScriptHash {
175        self.0.hash().into()
176    }
177}
178
179#[wasm_bindgen]
180impl PlutusV3Script {
181    pub fn hash(&self) -> ScriptHash {
182        self.0.hash().into()
183    }
184}
185
186impl_raw_bytes_api!(cml_chain::plutus::PlutusV1Script, PlutusV1Script);
187
188impl_raw_bytes_api!(cml_chain::plutus::PlutusV2Script, PlutusV2Script);
189
190impl_raw_bytes_api!(cml_chain::plutus::PlutusV3Script, PlutusV3Script);
191
192#[wasm_bindgen]
193impl Redeemers {
194    pub fn to_flat_format(&self) -> LegacyRedeemerList {
195        self.0.clone().to_flat_format().into()
196    }
197}
198
199#[wasm_bindgen]
200impl ExUnits {
201    pub fn checked_add(&self, other: &ExUnits) -> Result<ExUnits, JsError> {
202        self.0
203            .checked_add(other.as_ref())
204            .map(Into::into)
205            .map_err(Into::into)
206    }
207}
208
209#[wasm_bindgen]
210pub fn compute_total_ex_units(redeemers: &Redeemers) -> Result<ExUnits, JsError> {
211    cml_chain::plutus::utils::compute_total_ex_units(redeemers.to_flat_format().as_ref())
212        .map(Into::into)
213        .map_err(Into::into)
214}