cardano_serialization_lib/builders/
mint_builder.rs1use crate::*;
2use std::collections::BTreeMap;
3
4#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
5enum MintWitnessEnum {
6 Plutus(PlutusScriptSourceEnum, Redeemer),
7 NativeScript(NativeScriptSourceEnum),
8}
9
10#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
11#[wasm_bindgen]
12pub struct MintWitness(MintWitnessEnum);
13
14#[wasm_bindgen]
15impl MintWitness {
16 pub fn new_native_script(native_script: &NativeScriptSource) -> MintWitness {
17 MintWitness(MintWitnessEnum::NativeScript(native_script.0.clone()))
18 }
19
20 pub fn new_plutus_script(
21 plutus_script: &PlutusScriptSource,
22 redeemer: &Redeemer,
23 ) -> MintWitness {
24 MintWitness(MintWitnessEnum::Plutus(
25 plutus_script.0.clone(),
26 redeemer.clone(),
27 ))
28 }
29
30 pub(crate) fn script_hash(&self) -> ScriptHash {
31 match &self.0 {
32 MintWitnessEnum::NativeScript(native_script) => native_script.script_hash(),
33 MintWitnessEnum::Plutus(plutus_script, _) => plutus_script.script_hash(),
34 }
35 }
36}
37
38#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
39struct NativeMints {
40 script: NativeScriptSourceEnum,
41 mints: BTreeMap<AssetName, Int>,
42}
43
44impl NativeMints {
45
46 #[allow(dead_code)]
47 fn script_hash(&self) -> PolicyID {
48 match &self.script {
49 NativeScriptSourceEnum::NativeScript(script, _) => script.hash(),
50 NativeScriptSourceEnum::RefInput(_, script_hash, _, _) => script_hash.clone(),
51 }
52 }
53
54 fn ref_input(&self) -> Option<&TransactionInput> {
55 match &self.script {
56 NativeScriptSourceEnum::RefInput(input, _, _, _) => Some(input),
57 _ => None,
58 }
59 }
60
61 fn native_script(&self) -> Option<&NativeScript> {
62 match &self.script {
63 NativeScriptSourceEnum::NativeScript(script, _) => Some(script),
64 _ => None,
65 }
66 }
67}
68
69#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
70struct PlutusMints {
71 script: PlutusScriptSourceEnum,
72 redeemer: Redeemer,
73 mints: BTreeMap<AssetName, Int>,
74}
75
76#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
77enum ScriptMint {
78 Plutus(PlutusMints),
79 Native(NativeMints),
80}
81
82#[derive(Clone, Debug)]
83#[wasm_bindgen]
84pub struct MintBuilder {
85 mints: BTreeMap<PolicyID, ScriptMint>,
86}
87
88#[wasm_bindgen]
89impl MintBuilder {
90 pub fn new() -> MintBuilder {
91 MintBuilder {
92 mints: BTreeMap::new(),
93 }
94 }
95
96 pub fn add_asset(
97 &mut self,
98 mint: &MintWitness,
99 asset_name: &AssetName,
100 amount: &Int,
101 ) -> Result<(), JsError> {
102 if amount.0 == 0 {
103 return Err(JsError::from_str("Mint cannot be zero."));
104 }
105 self.update_mint_value(mint, asset_name, amount, false)?;
106 Ok(())
107 }
108
109 pub fn set_asset(
110 &mut self,
111 mint: &MintWitness,
112 asset_name: &AssetName,
113 amount: &Int,
114 ) -> Result<(), JsError> {
115 if amount.0 == 0 {
116 return Err(JsError::from_str("Mint cannot be zero."));
117 }
118 self.update_mint_value(mint, asset_name, amount, true)?;
119 Ok(())
120 }
121
122 fn update_mint_value(
123 &mut self,
124 mint_witness: &MintWitness,
125 asset_name: &AssetName,
126 amount: &Int,
127 overwrite: bool,
128 ) -> Result<(), JsError> {
129 if amount.0 == 0 {
130 return Err(JsError::from_str("Mint cannot be zero."));
131 }
132 let script_mint = self.mints.get(&mint_witness.script_hash());
133 Self::validate_mint_witness(mint_witness, script_mint)?;
134
135 match &mint_witness.0 {
136 MintWitnessEnum::NativeScript(native_script) => {
137 let script_mint =
138 self.mints
139 .entry(native_script.script_hash())
140 .or_insert(ScriptMint::Native(NativeMints {
141 script: native_script.clone(),
142 mints: BTreeMap::new(),
143 }));
144 match script_mint {
145 ScriptMint::Native(native_mints) => {
146 let mint = native_mints
147 .mints
148 .entry(asset_name.clone())
149 .or_insert(Int::new(&BigNum::zero()));
150 if overwrite {
151 mint.0 = amount.0;
152 } else {
153 mint.0 += amount.0;
154 }
155 }
156 _ => {}
157 }
158 }
159 MintWitnessEnum::Plutus(plutus_script, redeemer) => {
160 let script_mint =
161 self.mints
162 .entry(plutus_script.script_hash())
163 .or_insert(ScriptMint::Plutus(PlutusMints {
164 script: plutus_script.clone(),
165 redeemer: redeemer.clone(),
166 mints: BTreeMap::new(),
167 }));
168 match script_mint {
169 ScriptMint::Plutus(plutus_mints) => {
170 let mint = plutus_mints
171 .mints
172 .entry(asset_name.clone())
173 .or_insert(Int::new(&BigNum::zero()));
174 if overwrite {
175 mint.0 = amount.0;
176 } else {
177 mint.0 += amount.0;
178 }
179 }
180 _ => {}
181 }
182 }
183 }
184 Ok(())
185 }
186
187 fn validate_mint_witness(
188 mint_witness: &MintWitness,
189 current_script_mint: Option<&ScriptMint>,
190 ) -> Result<(), JsError> {
191 if let Some(current_script_mint) = current_script_mint {
192 match &mint_witness.0 {
193 MintWitnessEnum::NativeScript(native_script) => {
194 if let ScriptMint::Native(native_mints) = current_script_mint {
195 Self::validate_native_source_type(&native_mints.script, &native_script)?;
196 } else {
197 return Err(JsError::from_str(
198 &BuilderError::MintBuilderDifferentScriptType.as_str(),
199 ));
200 }
201 }
202 MintWitnessEnum::Plutus(plutus_script, redeemer) => {
203 if let ScriptMint::Plutus(plutus_mints) = current_script_mint {
204 Self::validate_plutus_script_source_type(
205 &plutus_mints.script,
206 &plutus_script,
207 )?;
208 if !plutus_mints.redeemer.partially_eq(redeemer) {
209 return Err(JsError::from_str(
210 &BuilderError::MintBuilderDifferentRedeemerDataAndExUnits(
211 plutus_mints.redeemer.to_json()?,
212 redeemer.to_json()?,
213 )
214 .as_str(),
215 ));
216 }
217 } else {
218 return Err(JsError::from_str(
219 &BuilderError::MintBuilderDifferentScriptType.as_str(),
220 ));
221 }
222 }
223 }
224 Ok(())
225 } else {
226 Ok(())
227 }
228 }
229
230 fn validate_native_source_type(
231 current_script_source: &NativeScriptSourceEnum,
232 input_script_source: &NativeScriptSourceEnum,
233 ) -> Result<(), JsError> {
234 match current_script_source {
235 NativeScriptSourceEnum::NativeScript(_, _) => {
236 if let NativeScriptSourceEnum::NativeScript(_, _) = input_script_source {
237 Ok(())
238 } else {
239 Err(JsError::from_str(
240 &BuilderError::MintBuilderDifferentWitnessTypeNonRef.as_str(),
241 ))
242 }
243 }
244 NativeScriptSourceEnum::RefInput(_, _, _, _) => {
245 if let NativeScriptSourceEnum::RefInput(_, _, _, _) = input_script_source {
246 Ok(())
247 } else {
248 Err(JsError::from_str(
249 &BuilderError::MintBuilderDifferentWitnessTypeRef.as_str(),
250 ))
251 }
252 }
253 }
254 }
255
256 fn validate_plutus_script_source_type(
257 current_script_source: &PlutusScriptSourceEnum,
258 input_script_source: &PlutusScriptSourceEnum,
259 ) -> Result<(), JsError> {
260 match current_script_source {
261 PlutusScriptSourceEnum::RefInput(_, _) => {
262 if let PlutusScriptSourceEnum::RefInput(_, _) = input_script_source {
263 Ok(())
264 } else {
265 Err(JsError::from_str(
266 &BuilderError::MintBuilderDifferentWitnessTypeRef.as_str(),
267 ))
268 }
269 }
270 PlutusScriptSourceEnum::Script(_, _) => {
271 if let PlutusScriptSourceEnum::Script(_, _) = input_script_source {
272 Ok(())
273 } else {
274 Err(JsError::from_str(
275 &BuilderError::MintBuilderDifferentWitnessTypeNonRef.as_str(),
276 ))
277 }
278 }
279 }
280 }
281
282 pub(crate) fn build_unchecked(&self) -> Mint {
283 let mut mint = Mint::new();
284 for (policy, script_mint) in self.mints.iter() {
285 let mut mint_asset = MintAssets::new();
286 match script_mint {
287 ScriptMint::Native(native_mints) => {
288 for (asset_name, amount) in &native_mints.mints {
289 mint_asset.insert_unchecked(asset_name, amount.clone());
290 }
291 }
292 ScriptMint::Plutus(plutus_mints) => {
293 for (asset_name, amount) in &plutus_mints.mints {
294 mint_asset.insert_unchecked(asset_name, amount.clone());
295 }
296 }
297 }
298 mint.insert(&policy, &mint_asset);
299 }
300 mint
301 }
302
303 pub fn build(&self) -> Result<Mint, JsError> {
304 let mut mint = Mint::new();
305 for (policy, script_mint) in &self.mints {
306 let mut mint_asset = MintAssets::new();
307 match script_mint {
308 ScriptMint::Native(native_mints) => {
309 for (asset_name, amount) in &native_mints.mints {
310 mint_asset.insert(asset_name, amount)?;
311 }
312 }
313 ScriptMint::Plutus(plutus_mints) => {
314 for (asset_name, amount) in &plutus_mints.mints {
315 mint_asset.insert(asset_name, amount)?;
316 }
317 }
318 }
319 mint.insert(&policy, &mint_asset);
320 }
321 Ok(mint)
322 }
323
324 pub fn get_native_scripts(&self) -> NativeScripts {
325 let mut native_scripts = Vec::new();
326 for script_mint in self.mints.values() {
327 match script_mint {
328 ScriptMint::Native(native_mints) => {
329 if let Some(script) = native_mints.native_script() {
330 native_scripts.push(script.clone());
331 }
332 }
333 _ => {}
334 }
335 }
336 NativeScripts::from(native_scripts)
337 }
338
339 pub fn get_plutus_witnesses(&self) -> PlutusWitnesses {
340 let mut plutus_witnesses = Vec::new();
341 let tag = RedeemerTag::new_mint();
342 for (index, (_, script_mint)) in self.mints.iter().enumerate() {
343 match script_mint {
344 ScriptMint::Plutus(plutus_mints) => {
345 plutus_witnesses.push(PlutusWitness::new_with_ref_without_datum(
346 &PlutusScriptSource(plutus_mints.script.clone()),
347 &plutus_mints.redeemer.clone_with_index_and_tag(
348 &BigNum::from(index),
349 &tag,
350 ),
351 ));
352 }
353 _ => {}
354 }
355 }
356 PlutusWitnesses(plutus_witnesses)
357 }
358
359 pub fn get_ref_inputs(&self) -> TransactionInputs {
360 let mut reference_inputs = Vec::new();
361 for script_mint in self.mints.values() {
362 match script_mint {
363 ScriptMint::Plutus(plutus_mints) => {
364 if let PlutusScriptSourceEnum::RefInput(ref_script, _) = &plutus_mints.script {
365 reference_inputs.push(ref_script.input_ref.clone());
366 }
367 }
368 ScriptMint::Native(native_mints) => {
369 if let Some(input) = native_mints.ref_input() {
370 reference_inputs.push(input.clone());
371 }
372 }
373 }
374 }
375 TransactionInputs::from_vec(reference_inputs)
376 }
377
378 pub fn get_redeemers(&self) -> Result<Redeemers, JsError> {
379 let tag = RedeemerTag::new_mint();
380 let mut redeeemers = Vec::new();
381 let mut index = BigNum::zero();
382 for (_, script_mint) in &self.mints {
383 match script_mint {
384 ScriptMint::Plutus(plutus_mints) => {
385 redeeemers.push(plutus_mints.redeemer.clone_with_index_and_tag(&index, &tag));
386 index = index.checked_add(&BigNum::one())?;
387 }
388 _ => {
389 index = index.checked_add(&BigNum::one())?;
390 }
391 }
392 }
393 Ok(Redeemers::from(redeeemers))
394 }
395
396 pub fn has_plutus_scripts(&self) -> bool {
397 for script_mint in self.mints.values() {
398 match script_mint {
399 ScriptMint::Plutus(_) => {
400 return true;
401 }
402 _ => {}
403 }
404 }
405 false
406 }
407
408 pub fn has_native_scripts(&self) -> bool {
409 for script_mint in self.mints.values() {
410 match script_mint {
411 ScriptMint::Native(_) => {
412 return true;
413 }
414 _ => {}
415 }
416 }
417 false
418 }
419
420 pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet<Language> {
421 let mut used_langs = BTreeSet::new();
422 for (_, script_mint) in &self.mints {
423 match script_mint {
424 ScriptMint::Plutus(plutus_mints) => {
425 used_langs.insert(plutus_mints.script.language());
426 }
427 _ => {}
428 }
429 }
430 used_langs
431 }
432
433 pub(crate) fn get_script_ref_inputs_with_size(
437 &self,
438 ) -> impl Iterator<Item = (&TransactionInput, usize)> {
439 self.mints.iter().filter_map(|(_, script_mint)| {
440 if let ScriptMint::Plutus(plutus_mints) = script_mint {
441 if let PlutusScriptSourceEnum::RefInput(script_ref, _) = &plutus_mints.script {
442 return Some((&script_ref.input_ref, script_ref.script_size));
443 }
444 }
445 None
446 })
447 }
448}