1use super::certificate_builder::*;
2use super::input_builder::InputBuilderResult;
3use super::mint_builder::MintBuilderResult;
4use super::output_builder::{OutputBuilderError, SingleOutputBuilderResult};
5use super::proposal_builder::ProposalBuilderResult;
6use super::redeemer_builder::RedeemerBuilderError;
7use super::redeemer_builder::RedeemerSetBuilder;
8use super::redeemer_builder::RedeemerWitnessKey;
9use super::vote_builder::VoteBuilderResult;
10use super::withdrawal_builder::WithdrawalBuilderResult;
11use super::witness_builder::merge_fake_witness;
12use super::witness_builder::PlutusScriptWitness;
13use super::witness_builder::RequiredWitnessSet;
14use super::witness_builder::TransactionWitnessSetBuilder;
15use super::witness_builder::{InputAggregateWitnessData, WitnessBuilderError};
16use crate::address::Address;
17use crate::assets::MultiAsset;
18use crate::assets::{AssetArithmeticError, Mint};
19use crate::auxdata::AuxiliaryData;
20use crate::builders::output_builder::TransactionOutputBuilder;
21use crate::certs::{Certificate, Credential};
22use crate::crypto::hash::{calc_script_data_hash, hash_auxiliary_data, ScriptDataHashError};
23use crate::crypto::{BootstrapWitness, Vkeywitness};
24use crate::deposit::{internal_get_deposit, internal_get_implicit_input};
25use crate::fees::LinearFee;
26use crate::governance::{ProposalProcedure, VotingProcedures};
27use crate::min_ada::min_ada_required;
28use crate::plutus::{CostModels, ExUnits, Language};
29use crate::plutus::{PlutusData, Redeemers};
30use crate::transaction::{
31 DatumOption, ScriptRef, Transaction, TransactionBody, TransactionInput, TransactionOutput,
32 TransactionWitnessSet,
33};
34use crate::{assets::AssetName, Coin, ExUnitPrices, NetworkId, PolicyId, Value, Withdrawals};
35use cbor_event::{de::Deserializer, se::Serializer};
36use cml_core::ordered_hash_map::OrderedHashMap;
37use cml_core::serialization::{CBORReadLen, Deserialize};
38use cml_core::{ArithmeticError, DeserializeError, DeserializeFailure, Slot};
39use cml_crypto::{Ed25519KeyHash, ScriptDataHash, ScriptHash, Serialize};
40use num::Zero;
41use rand::Rng;
42use std::collections::{BTreeSet, HashMap};
43use std::convert::TryInto;
44use std::io::{BufRead, Seek, Write};
45use std::ops::DerefMut;
46
47#[cfg(not(feature = "used_from_wasm"))]
48use noop_proc_macro::wasm_bindgen;
49#[cfg(feature = "used_from_wasm")]
50use wasm_bindgen::prelude::wasm_bindgen;
51
52#[derive(Clone, Debug)]
58pub struct TransactionUnspentOutput {
59 pub input: TransactionInput,
60 pub output: TransactionOutput,
61}
62
63impl TransactionUnspentOutput {
64 pub fn new(input: TransactionInput, output: TransactionOutput) -> Self {
65 Self { input, output }
66 }
67}
68
69impl cbor_event::se::Serialize for TransactionUnspentOutput {
72 fn serialize<'se, W: Write>(
73 &self,
74 serializer: &'se mut Serializer<W>,
75 ) -> cbor_event::Result<&'se mut Serializer<W>> {
76 serializer.write_array(cbor_event::Len::Len(2))?;
77 self.input.serialize(serializer, false)?;
78 self.output.serialize(serializer, false)
79 }
80}
81
82impl Deserialize for TransactionUnspentOutput {
83 fn deserialize<R: BufRead + Seek>(raw: &mut Deserializer<R>) -> Result<Self, DeserializeError> {
84 let len = raw.array_sz()?;
85 let mut read_len = CBORReadLen::new(len);
86 read_len.read_elems(2)?;
87 read_len.finish()?;
88 (|| -> Result<_, DeserializeError> {
89 let input = TransactionInput::deserialize(raw)
90 .map_err(|e: DeserializeError| e.annotate("input"))?;
91 let output = TransactionOutput::deserialize(raw)
92 .map_err(|e: DeserializeError| e.annotate("output"))?;
93 match len {
94 cbor_event::LenSz::Len(_, _) => (),
95 cbor_event::LenSz::Indefinite => match raw.special()? {
96 cbor_event::Special::Break => (),
97 _ => return Err(DeserializeFailure::EndingBreakMissing.into()),
98 },
99 }
100 Ok(Self { input, output })
101 })()
102 .map_err(|e| e.annotate("TransactionUnspentOutput"))
103 }
104}
105
106#[derive(Clone, Default, Debug)]
107struct WitnessBuilders {
108 pub witness_set_builder: TransactionWitnessSetBuilder,
109 pub fake_required_witnesses: RequiredWitnessSet,
110 pub redeemer_set_builder: RedeemerSetBuilder,
111}
112impl WitnessBuilders {
113 fn merge_data(
114 &self,
115 include_fake: bool,
116 ) -> Result<TransactionWitnessSetBuilder, WitnessBuilderError> {
117 let redeemers = self.redeemer_set_builder.build(true)?;
119 let mut witness_set_clone = self.witness_set_builder.clone();
120 redeemers
121 .to_flat_format()
122 .into_iter()
123 .for_each(|r| witness_set_clone.add_redeemer(r));
124
125 if include_fake {
126 merge_fake_witness(&mut witness_set_clone, &self.fake_required_witnesses);
127 let own_requirements = witness_set_clone.required_wits.clone();
128 merge_fake_witness(&mut witness_set_clone, &own_requirements);
129 }
130
131 Ok(witness_set_clone)
132 }
133
134 pub fn build_fake(&self) -> Result<TransactionWitnessSet, WitnessBuilderError> {
136 self.merge_data(true).map(|wit_builder| wit_builder.build())
137 }
138
139 pub fn build_unchecked(&self) -> Result<TransactionWitnessSetBuilder, WitnessBuilderError> {
141 self.merge_data(false)
142 }
143}
144
145fn fake_full_tx(
149 tx_builder: &TransactionBuilder,
150 body: TransactionBody,
151) -> Result<Transaction, TxBuilderError> {
152 Ok(Transaction::new(
153 body,
154 tx_builder.witness_builders.build_fake()?,
155 true,
156 tx_builder.auxiliary_data.clone(),
157 ))
158}
159
160#[derive(Debug, Copy, Clone)]
161pub enum TxBuilderConfigField {
162 FeeAlgo,
163 PoolDeposit,
164 KeyDeposit,
165 MaxValueSize,
166 MaxTxSize,
167 CoinsPerUtxoBytes,
168 ExUnitPrices,
169 CollateralPercentage,
170 MaxCollateralInputs,
171}
172
173#[derive(Debug, thiserror::Error)]
174pub enum TxBuilderError {
175 #[error("Witness build failed: {0}")]
176 WitnessBuildFailed(#[from] WitnessBuilderError),
177 #[error("Redeem build failed: {0}")]
178 RedeemerBuildFailed(#[from] RedeemerBuilderError),
179 #[error("Output build failed: {0}")]
180 OutputBuildFailed(#[from] OutputBuilderError),
181 #[error("Arithmetic: {0}")]
182 Arithmetic(#[from] ArithmeticError),
183 #[error("Asset arithmetic: {0}")]
184 AssetArithmetic(#[from] AssetArithmeticError),
185 #[error("Uninitialized field: {0:?}")]
186 UninitializedField(TxBuilderConfigField),
187 #[error(
188 "Multiasset values not supported by RandomImprove. Please use RandomImproveMultiAsset"
189 )]
190 RandomImproveCantContainMultiasset,
191 #[error("UTxO Balance Insufficient. Inputs: {0:?}, Outputs: {1:?}")]
192 UTxOBalanceInsufficient(Value, Value),
193 #[error("NFTs too large for change output")]
194 NFTTooLargeForChange,
195 #[error("Collateral can only be payment keys (scripts not allowed)")]
196 CollateralMustBePayment,
197 #[error("Max collateral input count {0} exceeded")]
198 MaxCollateralInputExceeded(u32),
199 #[error("Max value size of {0} exceeded. Found: {1}")]
200 MaxValueSizeExceeded(u32, usize),
201 #[error("Max transaction size of {0} exceeded. Found: {1}")]
202 MaxTxSizeExceeded(u32, usize),
203 #[error("Value {0} less tan the minimum UTXO value {1}")]
204 ValueBelowMinUTXOValue(u64, u64),
205 #[error("Fee not specified")]
206 FeeNotSpecified,
207 #[error("Reference Script hash {0} not found in reference script witness set {1:?}")]
208 RefScriptNotFound(ScriptHash, BTreeSet<ScriptHash>),
209 #[error("Not enough ADA leftover to include non-ADA assets in a change address")]
210 InsufficientADAForAssets,
211 #[error("Missing input or output (possibly some native asset)")]
212 MissingInputOrOutput,
213 #[error("Cannot use collateral return without also having collateral input")]
214 CollateralReturnRequiresCollateralInput,
215 #[error("ScriptDatumHash failed: {0}")]
216 ScriptDatumHashFailed(#[from] ScriptDataHashError),
217 #[error("Duplicate Mint Asset: {0:?}:{1:?}")]
218 DuplicateMint(PolicyId, AssetName),
219}
220
221fn min_fee(tx_builder: &TransactionBuilder) -> Result<Coin, TxBuilderError> {
222 let full_tx = fake_full_tx(tx_builder, tx_builder.build_body()?)?;
223 crate::fees::min_no_script_fee(&full_tx, &tx_builder.config.fee_algo).map_err(Into::into)
225}
226
227fn min_fee_with_exunits(tx_builder: &TransactionBuilder) -> Result<Coin, TxBuilderError> {
228 let full_tx = fake_full_tx(tx_builder, tx_builder.build_body()?)?;
229 fn ref_script_orig_size_builder(utxo: &TransactionUnspentOutput) -> Option<(ScriptHash, u64)> {
232 utxo.output.script_ref().map(|script_ref| {
233 (
234 script_ref.hash(),
235 script_ref
236 .raw_plutus_bytes()
237 .expect("TODO: handle this")
238 .len() as u64,
239 )
240 })
241 }
242
243 let ref_script_orig_sizes: HashMap<ScriptHash, u64> =
244 if let Some(ref_inputs) = &tx_builder.reference_inputs {
245 ref_inputs
246 .iter()
247 .filter_map(ref_script_orig_size_builder)
248 .collect()
249 } else {
250 HashMap::default()
251 };
252
253 let mut total_ref_script_size = 0;
254 for utxo in tx_builder.inputs.iter() {
255 if let Some(Credential::Script { hash, .. }) = utxo.output.address().payment_cred() {
256 if let Some(orig_size) = ref_script_orig_sizes.get(hash) {
257 total_ref_script_size += *orig_size;
258 }
259 }
260 }
261
262 crate::fees::min_fee(
263 &full_tx,
264 &tx_builder.config.fee_algo,
265 &tx_builder.config.ex_unit_prices,
266 total_ref_script_size,
267 )
268 .map_err(Into::into)
269}
270
271#[wasm_bindgen]
272pub enum CoinSelectionStrategyCIP2 {
273 LargestFirst,
275 RandomImprove,
277 LargestFirstMultiAsset,
279 RandomImproveMultiAsset,
281}
282
283#[derive(Clone, Debug)]
284pub struct TransactionBuilderConfig {
285 fee_algo: LinearFee,
286 pool_deposit: u64, key_deposit: u64, max_value_size: u32, max_tx_size: u32, coins_per_utxo_byte: Coin, ex_unit_prices: ExUnitPrices, cost_models: CostModels, _collateral_percentage: u32, max_collateral_inputs: u32, prefer_pure_change: bool,
296}
297
298#[derive(Clone, Debug, Default)]
299pub struct TransactionBuilderConfigBuilder {
300 fee_algo: Option<LinearFee>,
301 pool_deposit: Option<u64>, key_deposit: Option<u64>, max_value_size: Option<u32>, max_tx_size: Option<u32>, coins_per_utxo_byte: Option<Coin>, ex_unit_prices: Option<ExUnitPrices>, cost_models: Option<CostModels>, collateral_percentage: Option<u32>, max_collateral_inputs: Option<u32>, prefer_pure_change: bool,
311}
312
313impl TransactionBuilderConfigBuilder {
314 pub fn new() -> Self {
315 Self::default()
317 }
318
319 pub fn fee_algo(mut self, fee_algo: LinearFee) -> Self {
320 self.fee_algo = Some(fee_algo);
321 self
322 }
323
324 pub fn coins_per_utxo_byte(mut self, coins_per_utxo_byte: Coin) -> Self {
325 self.coins_per_utxo_byte = Some(coins_per_utxo_byte);
326 self
327 }
328
329 pub fn pool_deposit(mut self, pool_deposit: u64) -> Self {
330 self.pool_deposit = Some(pool_deposit);
331 self
332 }
333
334 pub fn key_deposit(mut self, key_deposit: u64) -> Self {
335 self.key_deposit = Some(key_deposit);
336 self
337 }
338
339 pub fn max_value_size(mut self, max_value_size: u32) -> Self {
340 self.max_value_size = Some(max_value_size);
341 self
342 }
343
344 pub fn max_tx_size(mut self, max_tx_size: u32) -> Self {
345 self.max_tx_size = Some(max_tx_size);
346 self
347 }
348
349 pub fn prefer_pure_change(mut self, prefer_pure_change: bool) -> Self {
350 self.prefer_pure_change = prefer_pure_change;
351 self
352 }
353
354 pub fn ex_unit_prices(mut self, ex_unit_prices: ExUnitPrices) -> Self {
355 self.ex_unit_prices = Some(ex_unit_prices);
356 self
357 }
358
359 pub fn cost_models(mut self, cost_models: CostModels) -> Self {
360 self.cost_models = Some(cost_models);
361 self
362 }
363
364 pub fn collateral_percentage(mut self, collateral_percentage: u32) -> Self {
365 self.collateral_percentage = Some(collateral_percentage);
366 self
367 }
368
369 pub fn max_collateral_inputs(mut self, max_collateral_inputs: u32) -> Self {
370 self.max_collateral_inputs = Some(max_collateral_inputs);
371 self
372 }
373
374 pub fn build(self) -> Result<TransactionBuilderConfig, TxBuilderError> {
375 Ok(TransactionBuilderConfig {
376 fee_algo: self.fee_algo.ok_or(TxBuilderError::UninitializedField(
377 TxBuilderConfigField::FeeAlgo,
378 ))?,
379 pool_deposit: self.pool_deposit.ok_or(TxBuilderError::UninitializedField(
380 TxBuilderConfigField::PoolDeposit,
381 ))?,
382 key_deposit: self.key_deposit.ok_or(TxBuilderError::UninitializedField(
383 TxBuilderConfigField::KeyDeposit,
384 ))?,
385 max_value_size: self
386 .max_value_size
387 .ok_or(TxBuilderError::UninitializedField(
388 TxBuilderConfigField::MaxValueSize,
389 ))?,
390 max_tx_size: self.max_tx_size.ok_or(TxBuilderError::UninitializedField(
391 TxBuilderConfigField::MaxTxSize,
392 ))?,
393 coins_per_utxo_byte: self.coins_per_utxo_byte.ok_or(
394 TxBuilderError::UninitializedField(TxBuilderConfigField::CoinsPerUtxoBytes),
395 )?,
396 ex_unit_prices: self
397 .ex_unit_prices
398 .ok_or(TxBuilderError::UninitializedField(
399 TxBuilderConfigField::ExUnitPrices,
400 ))?,
401 cost_models: if self.cost_models.is_some() {
402 self.cost_models.unwrap()
403 } else {
404 CostModels::default()
405 },
406 _collateral_percentage: self.collateral_percentage.ok_or(
407 TxBuilderError::UninitializedField(TxBuilderConfigField::CollateralPercentage),
408 )?,
409 max_collateral_inputs: self.max_collateral_inputs.ok_or(
410 TxBuilderError::UninitializedField(TxBuilderConfigField::MaxCollateralInputs),
411 )?,
412 prefer_pure_change: self.prefer_pure_change,
413 })
414 }
415}
416
417#[derive(Clone, Debug)]
418pub struct TransactionBuilder {
419 config: TransactionBuilderConfig,
420 inputs: Vec<TransactionUnspentOutput>,
421 outputs: Vec<TransactionOutput>,
422 fee: Option<Coin>,
423 ttl: Option<Slot>, certs: Option<Vec<Certificate>>,
425 withdrawals: Option<Withdrawals>,
426 proposals: Option<Vec<ProposalProcedure>>,
427 votes: Option<VotingProcedures>,
428 auxiliary_data: Option<AuxiliaryData>,
429 validity_start_interval: Option<Slot>,
430 mint: Option<Mint>,
431 collateral: Option<Vec<TransactionUnspentOutput>>,
432 required_signers: Option<BTreeSet<Ed25519KeyHash>>,
433 network_id: Option<NetworkId>,
434 witness_builders: WitnessBuilders,
435 utxos: Vec<InputBuilderResult>,
436 collateral_return: Option<TransactionOutput>,
437 reference_inputs: Option<Vec<TransactionUnspentOutput>>,
438 donation: Option<Coin>,
439 current_treasury_value: Option<Coin>,
440}
441
442impl TransactionBuilder {
443 pub fn select_utxos(
451 &mut self,
452 strategy: CoinSelectionStrategyCIP2,
453 ) -> Result<(), TxBuilderError> {
454 let available_inputs = self.utxos.clone();
455 let mut input_total = self.get_total_input()?;
456 let mut output_total = self
457 .get_total_output()?
458 .checked_add(&Value::from(self.min_fee(false)?))?;
459 match strategy {
460 CoinSelectionStrategyCIP2::LargestFirst => {
461 self.cip2_largest_first_by(
462 &available_inputs,
463 &mut (0..available_inputs.len()).collect(),
464 &mut input_total,
465 &mut output_total,
466 |value| Some(value.coin),
467 )?;
468 }
469 CoinSelectionStrategyCIP2::RandomImprove => {
470 if self
471 .outputs
472 .iter()
473 .any(|output| output.amount().has_multiassets())
474 {
475 return Err(TxBuilderError::RandomImproveCantContainMultiasset);
476 }
477 let mut rng = rand::thread_rng();
478 let mut available_indices =
479 (0..available_inputs.len()).collect::<BTreeSet<usize>>();
480 self.cip2_random_improve_by(
481 &available_inputs,
482 &mut available_indices,
483 &mut input_total,
484 &mut output_total,
485 |value| Some(value.coin),
486 &mut rng,
487 )?;
488 while input_total.coin < output_total.coin {
492 if available_indices.is_empty() {
493 return Err(TxBuilderError::UTxOBalanceInsufficient(
494 input_total.clone(),
495 output_total.clone(),
496 ));
497 }
498 let i = *available_indices
499 .iter()
500 .nth(rng.gen_range(0..available_indices.len()))
501 .unwrap();
502 available_indices.remove(&i);
503 let input = &available_inputs[i];
504 let input_fee = self.fee_for_input(input)?;
505 self.add_input(input.clone()).unwrap();
506 input_total = input_total.checked_add(input.utxo_info.amount())?;
507 output_total = output_total.checked_add(&Value::from(input_fee))?;
508 }
509 }
510 CoinSelectionStrategyCIP2::LargestFirstMultiAsset => {
511 let mut available_indices = (0..available_inputs.len()).collect::<Vec<usize>>();
513 for (policy_id, assets) in output_total.multiasset.clone().iter() {
515 for (asset_name, _) in assets.iter() {
516 self.cip2_largest_first_by(
517 &available_inputs,
518 &mut available_indices,
519 &mut input_total,
520 &mut output_total,
521 |value| value.multiasset.get(policy_id, asset_name),
522 )?;
523 }
524 }
525 self.cip2_largest_first_by(
527 &available_inputs,
528 &mut available_indices,
529 &mut input_total,
530 &mut output_total,
531 |value| Some(value.coin),
532 )?;
533 }
534 CoinSelectionStrategyCIP2::RandomImproveMultiAsset => {
535 let mut rng = rand::thread_rng();
536 let mut available_indices =
537 (0..available_inputs.len()).collect::<BTreeSet<usize>>();
538 for (policy_id, assets) in output_total.multiasset.clone().iter() {
540 for (asset_name, _) in assets.iter() {
541 self.cip2_random_improve_by(
542 &available_inputs,
543 &mut available_indices,
544 &mut input_total,
545 &mut output_total,
546 |value| value.multiasset.get(policy_id, asset_name),
547 &mut rng,
548 )?;
549 }
550 }
551 self.cip2_random_improve_by(
553 &available_inputs,
554 &mut available_indices,
555 &mut input_total,
556 &mut output_total,
557 |value| Some(value.coin),
558 &mut rng,
559 )?;
560 while input_total.coin < output_total.coin {
564 if available_indices.is_empty() {
565 return Err(TxBuilderError::UTxOBalanceInsufficient(
566 input_total.clone(),
567 output_total.clone(),
568 ));
569 }
570 let i = *available_indices
571 .iter()
572 .nth(rng.gen_range(0..available_indices.len()))
573 .unwrap();
574 available_indices.remove(&i);
575 let input = &available_inputs[i];
576 let input_fee = self.fee_for_input(input)?;
577 self.add_input(input.clone()).unwrap();
578 input_total = input_total.checked_add(input.utxo_info.amount())?;
579 output_total = output_total.checked_add(&Value::from(input_fee))?;
580 }
581 }
582 }
583
584 Ok(())
585 }
586
587 fn cip2_largest_first_by<F>(
588 &mut self,
589 available_inputs: &[InputBuilderResult],
590 available_indices: &mut Vec<usize>,
591 input_total: &mut Value,
592 output_total: &mut Value,
593 by: F,
594 ) -> Result<(), TxBuilderError>
595 where
596 F: Fn(&Value) -> Option<u64>,
597 {
598 let mut relevant_indices = available_indices.clone();
599 relevant_indices.retain(|i| by(available_inputs[*i].utxo_info.amount()).is_some());
600 relevant_indices
602 .sort_by_key(|i| by(available_inputs[*i].utxo_info.amount()).expect("filtered above"));
603
604 for i in relevant_indices.iter().rev() {
606 if by(input_total).unwrap_or_else(u64::zero)
607 >= by(output_total).expect("do not call on asset types that aren't in the output")
608 {
609 break;
610 }
611 let input = &available_inputs[*i];
612 let input_fee = self.fee_for_input(input)?;
614 self.add_input(input.clone()).unwrap();
615 *input_total = input_total.checked_add(input.utxo_info.amount())?;
616 *output_total = output_total.checked_add(&Value::from(input_fee))?;
617 available_indices.swap_remove(available_indices.iter().position(|j| i == j).unwrap());
618 }
619
620 if by(input_total).unwrap_or_else(u64::zero)
621 < by(output_total).expect("do not call on asset types that aren't in the output")
622 {
623 return Err(TxBuilderError::UTxOBalanceInsufficient(
624 input_total.clone(),
625 output_total.clone(),
626 ));
627 }
628
629 Ok(())
630 }
631
632 fn cip2_random_improve_by<F, R: Rng + ?Sized>(
633 &mut self,
634 available_inputs: &[InputBuilderResult],
635 available_indices: &mut BTreeSet<usize>,
636 input_total: &mut Value,
637 output_total: &mut Value,
638 by: F,
639 rng: &mut R,
640 ) -> Result<(), TxBuilderError>
641 where
642 F: Fn(&Value) -> Option<u64>,
643 {
644 let mut relevant_indices = available_indices
646 .iter()
647 .filter(|i| by(available_inputs[**i].utxo_info.amount()).is_some())
648 .cloned()
649 .collect::<Vec<usize>>();
650 let mut associated_indices: HashMap<TransactionOutput, Vec<usize>> = HashMap::new();
651 let mut outputs = self
652 .outputs
653 .iter()
654 .filter(|output| by(output.amount()).is_some())
655 .cloned()
656 .collect::<Vec<TransactionOutput>>();
657 outputs.sort_by_key(|output| by(output.amount()).expect("filtered above"));
658 for output in outputs.iter().rev() {
659 let mut added = u64::zero();
675 let needed = by(output.amount()).unwrap();
676 while added < needed {
677 if relevant_indices.is_empty() {
678 return Err(TxBuilderError::UTxOBalanceInsufficient(
679 input_total.clone(),
680 output_total.clone(),
681 ));
682 }
683 let random_index = rng.gen_range(0..relevant_indices.len());
684 let i = relevant_indices.swap_remove(random_index);
685 available_indices.remove(&i);
686 let input = &available_inputs[i];
687 added = added
688 .checked_add(
689 by(input.utxo_info.amount())
690 .expect("do not call on asset types that aren't in the output"),
691 )
692 .ok_or(ArithmeticError::IntegerOverflow)?;
693 associated_indices
694 .entry(output.clone())
695 .or_default()
696 .push(i);
697 }
698 }
699 if !relevant_indices.is_empty() {
700 for output in outputs.iter_mut() {
702 let associated = associated_indices.get_mut(output).unwrap();
703 for i in associated.iter_mut() {
704 let random_index = rng.gen_range(0..relevant_indices.len());
705 let j: &mut usize = relevant_indices.get_mut(random_index).unwrap();
706 let should_improve = {
707 let input = &available_inputs[*i];
708 let new_input = &available_inputs[*j];
709 let cur = input.utxo_info.amount().coin;
710 let new = new_input.utxo_info.amount().coin;
711 let min = output.amount().coin;
712 let ideal = 2 * min;
713 let max = 3 * min;
714 let move_closer = (ideal as i128 - new as i128).abs()
715 < (ideal as i128 - cur as i128).abs();
716 let not_exceed_max = new < max;
717
718 move_closer && not_exceed_max
719 };
720 if should_improve {
721 available_indices.insert(*i);
722 available_indices.remove(j);
723 std::mem::swap(i, j);
724 }
725 }
726 }
727 }
728
729 for output in outputs.iter() {
731 for i in associated_indices.get(output).unwrap().iter() {
732 let input = &available_inputs[*i];
733 let input_fee = self.fee_for_input(input)?;
734 self.add_input(input.clone()).unwrap();
735 *input_total = input_total.checked_add(input.utxo_info.amount())?;
736 *output_total = output_total.checked_add(&Value::from(input_fee))?;
737 }
738 }
739
740 Ok(())
741 }
742
743 pub fn add_input(&mut self, mut result: InputBuilderResult) -> Result<(), TxBuilderError> {
744 if let Some(reference_inputs) = &self.reference_inputs {
745 result.required_wits.remove_ref_scripts(reference_inputs);
746 }
747 if let Some(script_ref) = result.utxo_info.script_ref() {
748 self.witness_builders
749 .witness_set_builder
750 .required_wits
751 .script_refs
752 .insert(script_ref.hash());
753 }
754 self.witness_builders
755 .redeemer_set_builder
756 .add_spend(&result);
757 self.witness_builders
758 .witness_set_builder
759 .add_required_wits(result.required_wits);
760 self.inputs.push(TransactionUnspentOutput {
761 input: result.input,
762 output: result.utxo_info,
763 });
764 if let Some(data) = result.aggregate_witness {
765 self.witness_builders
766 .witness_set_builder
767 .add_input_aggregate_real_witness_data(&data);
768 self.witness_builders
769 .fake_required_witnesses
770 .add_input_aggregate_fake_witness_data(&data);
771 if let InputAggregateWitnessData::PlutusScript(script_witness, required_signers, _) =
772 data
773 {
774 required_signers
775 .iter()
776 .for_each(|signer| self.add_required_signer(*signer));
777
778 match &script_witness.script {
779 PlutusScriptWitness::Ref(ref_script_hash) => {
780 if !self
783 .witness_builders
784 .witness_set_builder
785 .required_wits
786 .script_refs
787 .contains(ref_script_hash)
788 && !self.reference_inputs.iter().any(|ref_inputs| {
789 ref_inputs.iter().any(|ref_input| {
790 ref_input
791 .output
792 .script_ref()
793 .iter()
794 .any(|ref_script| ref_script.hash() == *ref_script_hash)
795 })
796 })
797 {
798 Err(TxBuilderError::RefScriptNotFound(
799 *ref_script_hash,
800 self.witness_builders
801 .witness_set_builder
802 .required_wits
803 .script_refs
804 .clone(),
805 ))
806 } else {
807 Ok(())
808 }
809 }
810 _ => Ok(()),
811 }
812 .unwrap();
813 }
814 }
815 Ok(())
816 }
817
818 pub fn add_utxo(&mut self, result: InputBuilderResult) {
819 self.utxos.push(result);
820 }
821
822 pub fn fee_for_input(&self, result: &InputBuilderResult) -> Result<Coin, TxBuilderError> {
824 let mut self_copy = self.clone();
825
826 self_copy.set_fee(0);
830
831 let fee_before = min_fee(&self_copy)?;
832
833 self_copy.add_input(result.clone()).unwrap();
834 let fee_after = min_fee(&self_copy)?;
835 fee_after
836 .checked_sub(fee_before)
837 .ok_or_else(|| ArithmeticError::IntegerOverflow.into())
838 }
839
840 pub fn add_reference_input(&mut self, utxo: TransactionUnspentOutput) {
842 let reference_inputs = match self.reference_inputs.as_mut() {
843 None => {
844 self.reference_inputs = Some(Vec::<TransactionUnspentOutput>::new());
845 self.reference_inputs.as_mut().unwrap()
846 }
847 Some(inputs) => inputs,
848 };
849
850 if let Some(script_ref) = utxo.output.script_ref() {
851 self.witness_builders
852 .witness_set_builder
853 .required_wits
854 .script_refs
855 .insert(script_ref.hash());
856 }
857
858 reference_inputs.push(utxo);
859 }
860
861 pub fn add_output(
863 &mut self,
864 builder_result: SingleOutputBuilderResult,
865 ) -> Result<(), TxBuilderError> {
866 let output = builder_result.output;
867 let value_size = output.amount().to_cbor_bytes().len();
868 if value_size > self.config.max_value_size as usize {
869 return Err(TxBuilderError::MaxValueSizeExceeded(
870 self.config.max_value_size,
871 value_size,
872 ));
873 }
874 let min_ada = min_ada_required(&output, self.config.coins_per_utxo_byte)?;
875 if output.amount().coin < min_ada {
876 Err(TxBuilderError::ValueBelowMinUTXOValue(
877 output.amount().coin,
878 min_ada,
879 ))
880 } else {
881 if let Some(datum) = builder_result.communication_datum {
882 self.witness_builders
883 .witness_set_builder
884 .add_plutus_datum(datum);
885 }
886 self.outputs.push(output);
887 Ok(())
888 }
889 }
890
891 pub fn fee_for_output(
893 &self,
894 builder: &SingleOutputBuilderResult,
895 ) -> Result<Coin, TxBuilderError> {
896 let mut self_copy = self.clone();
897
898 self_copy.set_fee(0);
902
903 let fee_before = min_fee(&self_copy)?;
904
905 self_copy.add_output(builder.clone())?;
906 let fee_after = min_fee(&self_copy)?;
907 fee_after
908 .checked_sub(fee_before)
909 .ok_or_else(|| ArithmeticError::IntegerOverflow.into())
910 }
911
912 pub fn set_fee(&mut self, fee: Coin) {
913 self.fee = Some(fee)
914 }
915
916 pub fn set_donation(&mut self, donation: Coin) {
917 self.donation = Some(donation)
918 }
919
920 pub fn set_current_treasury_value(&mut self, current_treasury_value: Coin) {
921 self.current_treasury_value = Some(current_treasury_value)
922 }
923
924 pub fn set_ttl(&mut self, ttl: Slot) {
925 self.ttl = Some(ttl)
926 }
927
928 pub fn set_validity_start_interval(&mut self, validity_start_interval: Slot) {
929 self.validity_start_interval = Some(validity_start_interval)
930 }
931
932 pub fn add_cert(&mut self, mut result: CertificateBuilderResult) {
933 if let Some(reference_inputs) = &self.reference_inputs {
934 result.required_wits.remove_ref_scripts(reference_inputs);
935 }
936 self.witness_builders.redeemer_set_builder.add_cert(&result);
937 if self.certs.is_none() {
938 self.certs = Some(Vec::new());
939 }
940 self.certs.as_mut().unwrap().push(result.cert);
941 if let Some(data) = result.aggregate_witness {
942 self.witness_builders
943 .witness_set_builder
944 .add_input_aggregate_real_witness_data(&data);
945 self.witness_builders
946 .fake_required_witnesses
947 .add_input_aggregate_fake_witness_data(&data);
948 if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
949 required_signers
950 .iter()
951 .for_each(|signer| self.add_required_signer(*signer));
952 }
953 }
954 self.witness_builders
955 .witness_set_builder
956 .add_required_wits(result.required_wits);
957 }
958
959 pub fn add_proposal(&mut self, mut result: ProposalBuilderResult) {
960 if let Some(reference_inputs) = &self.reference_inputs {
961 result.required_wits.remove_ref_scripts(reference_inputs);
962 }
963 self.witness_builders
964 .redeemer_set_builder
965 .add_proposal(&result);
966 if self.proposals.is_none() {
967 self.proposals = Some(Vec::new());
968 }
969 self.proposals
970 .as_mut()
971 .unwrap()
972 .append(&mut result.proposals);
973 for data in result.aggregate_witnesses {
974 self.witness_builders
975 .witness_set_builder
976 .add_input_aggregate_real_witness_data(&data);
977 self.witness_builders
978 .fake_required_witnesses
979 .add_input_aggregate_fake_witness_data(&data);
980 if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
981 required_signers
982 .iter()
983 .for_each(|signer| self.add_required_signer(*signer));
984 }
985 }
986 self.witness_builders
987 .witness_set_builder
988 .add_required_wits(result.required_wits);
989 }
990
991 pub fn add_vote(&mut self, mut result: VoteBuilderResult) {
992 if let Some(reference_inputs) = &self.reference_inputs {
993 result.required_wits.remove_ref_scripts(reference_inputs);
994 }
995 self.witness_builders.redeemer_set_builder.add_vote(&result);
996 if let Some(votes) = self.votes.as_mut() {
997 votes.extend(result.votes.take());
998 } else {
999 self.votes = Some(result.votes);
1000 }
1001 for data in result.aggregate_witnesses {
1002 self.witness_builders
1003 .witness_set_builder
1004 .add_input_aggregate_real_witness_data(&data);
1005 self.witness_builders
1006 .fake_required_witnesses
1007 .add_input_aggregate_fake_witness_data(&data);
1008 if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
1009 required_signers
1010 .iter()
1011 .for_each(|signer| self.add_required_signer(*signer));
1012 }
1013 }
1014 self.witness_builders
1015 .witness_set_builder
1016 .add_required_wits(result.required_wits);
1017 }
1018
1019 pub fn get_withdrawals(&self) -> Option<Withdrawals> {
1020 self.withdrawals.clone()
1021 }
1022
1023 pub fn add_withdrawal(&mut self, mut result: WithdrawalBuilderResult) {
1024 if let Some(reference_inputs) = &self.reference_inputs {
1025 result.required_wits.remove_ref_scripts(reference_inputs);
1026 }
1027 self.witness_builders
1028 .redeemer_set_builder
1029 .add_reward(&result);
1030 if self.withdrawals.is_none() {
1031 self.withdrawals = Some(OrderedHashMap::default());
1032 }
1033 self.withdrawals
1034 .as_mut()
1035 .unwrap()
1036 .insert(result.address, result.amount);
1037 if let Some(data) = result.aggregate_witness {
1038 self.witness_builders
1039 .witness_set_builder
1040 .add_input_aggregate_real_witness_data(&data);
1041 self.witness_builders
1042 .fake_required_witnesses
1043 .add_input_aggregate_fake_witness_data(&data);
1044 if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
1045 required_signers
1046 .iter()
1047 .for_each(|signer| self.add_required_signer(*signer));
1048 }
1049 }
1050 self.witness_builders
1051 .witness_set_builder
1052 .add_required_wits(result.required_wits);
1053 }
1054
1055 pub fn get_auxiliary_data(&self) -> Option<AuxiliaryData> {
1056 self.auxiliary_data.clone()
1057 }
1058
1059 pub fn set_auxiliary_data(&mut self, new_aux_data: AuxiliaryData) {
1060 self.auxiliary_data = Some(new_aux_data)
1061 }
1062
1063 pub fn add_auxiliary_data(&mut self, new_aux_data: AuxiliaryData) {
1064 match self.auxiliary_data.as_mut() {
1065 Some(data) => {
1066 data.add(new_aux_data);
1067 }
1068 None => {
1069 self.auxiliary_data = Some(new_aux_data);
1070 }
1071 }
1072 }
1073
1074 pub fn add_mint(&mut self, mut result: MintBuilderResult) -> Result<(), TxBuilderError> {
1075 if let Some(reference_inputs) = &self.reference_inputs {
1076 result.required_wits.remove_ref_scripts(reference_inputs);
1077 }
1078 self.witness_builders.redeemer_set_builder.add_mint(&result);
1079 self.witness_builders
1080 .witness_set_builder
1081 .add_required_wits(result.required_wits.clone());
1082 let mut mint = self.mint.take().unwrap_or_default();
1083 let combined_assets = mint.deref_mut().entry(result.policy_id).or_default();
1084 for (asset_name, asset_value) in result.assets.iter() {
1085 if combined_assets
1086 .insert(asset_name.clone(), *asset_value)
1087 .is_some()
1088 {
1089 return Err(TxBuilderError::DuplicateMint(
1090 result.policy_id,
1091 asset_name.clone(),
1092 ));
1093 }
1094 }
1095 self.mint = Some(mint);
1096 if let Some(data) = result.aggregate_witness {
1097 self.witness_builders
1098 .witness_set_builder
1099 .add_input_aggregate_real_witness_data(&data);
1100 self.witness_builders
1101 .fake_required_witnesses
1102 .add_input_aggregate_fake_witness_data(&data);
1103 if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
1104 required_signers
1105 .iter()
1106 .for_each(|signer| self.add_required_signer(*signer));
1107 }
1108 }
1109 Ok(())
1110 }
1111
1112 pub fn get_mint(&self) -> Option<Mint> {
1114 self.mint.clone()
1115 }
1116
1117 pub fn new(cfg: TransactionBuilderConfig) -> Self {
1118 Self {
1119 config: cfg,
1120 inputs: Vec::new(),
1121 outputs: Vec::new(),
1122 fee: None,
1123 ttl: None,
1124 certs: None,
1125 withdrawals: None,
1126 proposals: None,
1127 votes: None,
1128 auxiliary_data: None,
1129 validity_start_interval: None,
1130 mint: None,
1131 collateral: None,
1132 required_signers: None,
1133 network_id: None,
1134 witness_builders: WitnessBuilders::default(),
1135 utxos: Vec::new(),
1136 collateral_return: None,
1137 reference_inputs: None,
1138 donation: None,
1139 current_treasury_value: None,
1140 }
1141 }
1142
1143 pub fn add_collateral(&mut self, mut result: InputBuilderResult) -> Result<(), TxBuilderError> {
1144 if result.aggregate_witness.is_some() {
1145 return Err(TxBuilderError::CollateralMustBePayment);
1146 }
1147 if let Some(reference_inputs) = &self.reference_inputs {
1148 result.required_wits.remove_ref_scripts(reference_inputs);
1149 }
1150 let new_input = TransactionUnspentOutput {
1151 input: result.input,
1152 output: result.utxo_info,
1153 };
1154 match &mut self.collateral {
1155 None => self.collateral = Some(vec![new_input]),
1156 Some(collateral) => {
1157 if self.config.max_collateral_inputs <= collateral.len().try_into().unwrap() {
1158 return Err(TxBuilderError::MaxCollateralInputExceeded(
1159 self.config.max_collateral_inputs,
1160 ));
1161 }
1162 collateral.push(new_input);
1163 }
1164 }
1165
1166 if let Some(data) = result.aggregate_witness {
1169 self.witness_builders
1170 .witness_set_builder
1171 .add_input_aggregate_real_witness_data(&data);
1172 self.witness_builders
1173 .fake_required_witnesses
1174 .add_input_aggregate_fake_witness_data(&data);
1175 if let InputAggregateWitnessData::PlutusScript(_, required_signers, _) = data {
1176 required_signers
1177 .iter()
1178 .for_each(|signer| self.add_required_signer(*signer));
1179 }
1180 }
1181 self.witness_builders
1182 .witness_set_builder
1183 .add_required_wits(result.required_wits);
1184
1185 Ok(())
1186 }
1187
1188 pub fn add_required_signer(&mut self, hash: Ed25519KeyHash) {
1189 let mut set = RequiredWitnessSet::new();
1190 set.add_vkey_key_hash(hash);
1191 self.witness_builders
1192 .witness_set_builder
1193 .add_required_wits(set);
1194
1195 match &mut self.required_signers {
1196 None => {
1197 let mut required_signers = BTreeSet::new();
1198 required_signers.insert(hash);
1199 self.required_signers = Some(required_signers);
1200 }
1201 Some(required_signers) => {
1202 required_signers.insert(hash);
1203 }
1204 }
1205 }
1206
1207 pub fn set_network_id(&mut self, network_id: NetworkId) {
1208 self.network_id = Some(network_id)
1209 }
1210
1211 pub fn network_id(&self) -> Option<NetworkId> {
1212 self.network_id
1213 }
1214
1215 pub fn get_explicit_input(&self) -> Result<Value, TxBuilderError> {
1217 self.inputs
1218 .iter()
1219 .try_fold(Value::zero(), |acc, tx_builder_input| {
1220 acc.checked_add(tx_builder_input.output.amount())
1221 })
1222 .map_err(Into::into)
1223 }
1224
1225 pub fn get_implicit_input(&self) -> Result<Value, TxBuilderError> {
1227 internal_get_implicit_input(
1228 self.withdrawals.as_ref(),
1229 self.certs.as_deref(),
1230 self.config.pool_deposit,
1231 self.config.key_deposit,
1232 )
1233 .map_err(Into::into)
1234 }
1235
1236 fn get_mint_as_values(&self) -> (Value, Value) {
1238 self.mint
1239 .as_ref()
1240 .map(|m| {
1241 (
1242 Value::from(m.as_positive_multiasset()),
1243 Value::from(m.as_negative_multiasset()),
1244 )
1245 })
1246 .unwrap_or((Value::zero(), Value::zero()))
1247 }
1248
1249 pub fn get_total_input(&self) -> Result<Value, TxBuilderError> {
1251 let (mint_value, _) = self.get_mint_as_values();
1252 self.get_explicit_input()?
1253 .checked_add(&self.get_implicit_input()?)
1254 .and_then(|x| x.checked_add(&mint_value))
1255 .map_err(Into::into)
1256 }
1257
1258 pub fn get_total_output(&self) -> Result<Value, TxBuilderError> {
1260 let (_, burn_value) = self.get_mint_as_values();
1261 self.get_explicit_output()?
1262 .checked_add(&Value::from(self.get_deposit()?))
1263 .and_then(|x| x.checked_add(&burn_value))
1264 .and_then(|x| match self.donation {
1265 Some(donation) => x.checked_add(&Value::from(donation)),
1266 None => Ok(x),
1267 })
1268 .map_err(Into::into)
1269 }
1270
1271 pub fn get_explicit_output(&self) -> Result<Value, TxBuilderError> {
1273 self.outputs
1274 .iter()
1275 .try_fold(Value::from(0), |acc, output| {
1276 acc.checked_add(output.amount())
1277 })
1278 .map_err(Into::into)
1279 }
1280
1281 pub fn get_deposit(&self) -> Result<Coin, TxBuilderError> {
1282 internal_get_deposit(
1283 self.certs.as_deref(),
1284 self.proposals.as_deref(),
1285 self.config.pool_deposit,
1286 self.config.key_deposit,
1287 )
1288 .map_err(Into::into)
1289 }
1290
1291 pub fn get_fee_if_set(&self) -> Option<Coin> {
1292 self.fee
1293 }
1294
1295 pub fn set_collateral_return(&mut self, output: TransactionOutput) {
1296 self.collateral_return = Some(output);
1297 }
1298
1299 fn calc_collateral_total(&self) -> Result<Option<Coin>, TxBuilderError> {
1300 match self.collateral_return.as_ref() {
1301 None => Ok(None),
1302 Some(coll_ret) => {
1303 let input_sum = match self.collateral.as_ref() {
1304 Some(collateral) => collateral.iter().try_fold(Coin::zero(), |acc, next| {
1305 acc.checked_add(next.output.amount().coin)
1306 .ok_or(ArithmeticError::IntegerOverflow)
1307 }),
1308 None => return Err(TxBuilderError::CollateralReturnRequiresCollateralInput),
1309 }?;
1310
1311 let coll_tot = input_sum
1312 .checked_sub(coll_ret.amount().coin)
1313 .ok_or(ArithmeticError::IntegerOverflow)?;
1314 Ok(Some(coll_tot))
1315 }
1316 }
1317 }
1318
1319 fn build_and_size(&self) -> Result<(TransactionBody, usize), TxBuilderError> {
1320 let fee = self.fee.ok_or(TxBuilderError::FeeNotSpecified)?;
1321
1322 let redeemers = self.witness_builders.redeemer_set_builder.build(true)?;
1323 let has_dummy_exunit = redeemers
1324 .clone()
1325 .to_flat_format()
1326 .iter()
1327 .any(|redeemer| redeemer.ex_units == ExUnits::dummy());
1328
1329 let script_data_hash = match self.witness_builders.redeemer_set_builder.is_empty() {
1330 true => None,
1331 false => match has_dummy_exunit {
1335 true => Some(ScriptDataHash::from([0u8; ScriptDataHash::BYTE_COUNT])),
1336 false => {
1337 let scripts = self.witness_builders.witness_set_builder.scripts.values();
1338 let mut languages =
1339 scripts.fold(BTreeSet::<Language>::new(), |mut langs, script| {
1340 if let Some(lang) = script.language() {
1341 langs.insert(lang);
1342 }
1343 langs
1344 });
1345 if let Some(reference_inputs) = self.reference_inputs.as_ref() {
1346 reference_inputs
1347 .iter()
1348 .fold(&mut languages, |langs, input| {
1349 if let Some(script_ref) = &input.output.script_ref() {
1350 if let Some(lang) = script_ref.language() {
1351 langs.insert(lang);
1352 }
1353 }
1354 langs
1355 });
1356 };
1357 self.inputs
1358 .clone()
1359 .iter()
1360 .fold(&mut languages, |langs, input| {
1361 if let Some(script_ref) = &input.output.script_ref() {
1362 if let Some(lang) = script_ref.language() {
1363 langs.insert(lang);
1364 }
1365 }
1366 langs
1367 });
1368 calc_script_data_hash(
1369 &redeemers,
1370 &self
1371 .witness_builders
1372 .witness_set_builder
1373 .get_plutus_datum()
1374 .into(),
1375 &self.config.cost_models,
1376 &languages.iter().copied().collect::<Vec<_>>(),
1377 None,
1378 )?
1379 }
1380 },
1381 };
1382 let mut built = TransactionBody {
1383 inputs: self
1384 .inputs
1385 .iter()
1386 .map(|tx_builder_input| tx_builder_input.input.clone())
1387 .collect::<Vec<_>>()
1388 .into(),
1389 outputs: self.outputs.clone(),
1390 fee,
1391 ttl: self.ttl,
1392 certs: self.certs.as_ref().map(|certs| certs.clone().into()),
1393 withdrawals: self.withdrawals.clone(),
1394 auxiliary_data_hash: self.auxiliary_data.as_ref().map(hash_auxiliary_data),
1395 validity_interval_start: self.validity_start_interval,
1396 mint: self.mint.clone(),
1397 script_data_hash,
1398 collateral_inputs: self.collateral.as_ref().map(|collateral| {
1399 collateral
1400 .iter()
1401 .map(|c| c.input.clone())
1402 .collect::<Vec<_>>()
1403 .into()
1404 }),
1405 required_signers: self
1406 .required_signers
1407 .as_ref()
1408 .map(|set| set.iter().cloned().collect::<Vec<_>>().into()),
1409 network_id: self.network_id,
1410 collateral_return: self.collateral_return.clone(),
1411 total_collateral: self.calc_collateral_total()?,
1412 reference_inputs: self.reference_inputs.as_ref().map(|inputs| {
1413 inputs
1414 .iter()
1415 .map(|utxo| utxo.input.clone())
1416 .collect::<Vec<_>>()
1417 .into()
1418 }),
1419 voting_procedures: self.votes.clone(),
1420 proposal_procedures: self
1421 .proposals
1422 .as_ref()
1423 .map(|proposals| proposals.clone().into()),
1424 current_treasury_value: self.current_treasury_value,
1425 donation: self.donation,
1426 encodings: None,
1427 };
1428
1429 {
1431 built
1434 .inputs
1435 .sort_by(|a, b| match a.transaction_id.cmp(&b.transaction_id) {
1436 std::cmp::Ordering::Equal => a.index.cmp(&b.index),
1437 rest => rest,
1438 });
1439
1440 if let Some(withdrawals) = built.withdrawals {
1441 let mut sorted_keys = withdrawals.keys().collect::<Vec<_>>();
1442 sorted_keys.sort();
1443
1444 let mut sorted_linked_hashmap = Withdrawals::new();
1445 sorted_linked_hashmap =
1446 sorted_keys
1447 .iter()
1448 .fold(sorted_linked_hashmap, |mut accum, key| {
1449 accum.insert((*key).clone(), *withdrawals.get(key).unwrap());
1450 accum
1451 });
1452 built.withdrawals = Some(sorted_linked_hashmap)
1453 };
1454 }
1455
1456 let full_tx = fake_full_tx(self, built)?;
1458 let full_tx_size = full_tx.to_cbor_bytes().len();
1459 Ok((full_tx.body, full_tx_size))
1460 }
1461
1462 pub fn full_size(&self) -> Result<usize, TxBuilderError> {
1463 self.build_and_size().map(|r| r.1)
1464 }
1465
1466 pub fn output_sizes(&self) -> Vec<usize> {
1467 self.outputs
1468 .iter()
1469 .map(|o| o.to_cbor_bytes().len())
1470 .collect()
1471 }
1472
1473 fn build_body(&self) -> Result<TransactionBody, TxBuilderError> {
1475 let (body, full_tx_size) = self.build_and_size()?;
1476 if full_tx_size > self.config.max_tx_size as usize {
1477 Err(TxBuilderError::MaxTxSizeExceeded(
1478 self.config.max_tx_size,
1479 full_tx_size,
1480 ))
1481 } else {
1482 Ok(body)
1483 }
1484 }
1485
1486 pub fn build_for_evaluation(
1491 &self,
1492 algo: ChangeSelectionAlgo,
1493 change_address: &Address,
1494 ) -> Result<TxRedeemerBuilder, TxBuilderError> {
1495 let mut tx = self.clone();
1498 choose_change_selection_algo(algo)(&mut tx, change_address, false)?;
1499
1500 Ok(TxRedeemerBuilder {
1501 draft_body: tx.build_body()?,
1502 witness_builders: tx.witness_builders.clone(),
1503 auxiliary_data: tx.auxiliary_data.clone(),
1504 })
1505 }
1506
1507 pub fn build(
1511 &mut self,
1512 algo: ChangeSelectionAlgo,
1513 change_address: &Address,
1514 ) -> Result<SignedTxBuilder, TxBuilderError> {
1515 choose_change_selection_algo(algo)(self, change_address, true)?;
1517
1518 Ok(SignedTxBuilder {
1519 body: self.build_body()?,
1520 witness_set: self.witness_builders.build_unchecked()?,
1522 is_valid: true,
1523 auxiliary_data: self.auxiliary_data.clone(),
1524 })
1525 }
1526
1527 pub fn set_exunits(&mut self, redeemer: RedeemerWitnessKey, ex_units: ExUnits) {
1529 self.witness_builders
1530 .redeemer_set_builder
1531 .update_ex_units(redeemer, ex_units);
1532 }
1533
1534 pub fn min_fee(&self, script_calulation: bool) -> Result<Coin, TxBuilderError> {
1538 if !script_calulation {
1539 let mut self_copy = self.clone();
1540 self_copy.fee = Some(u64::MAX);
1541 min_fee(&self_copy)
1542 } else {
1543 let mut self_copy = self.clone();
1544 self_copy.fee = Some(u64::MAX);
1545 min_fee_with_exunits(&self_copy)
1546 }
1547 }
1548}
1549
1550#[derive(Debug, Clone)]
1551pub struct TxRedeemerBuilder {
1552 draft_body: TransactionBody,
1553 witness_builders: WitnessBuilders,
1554 auxiliary_data: Option<AuxiliaryData>,
1555}
1556
1557impl TxRedeemerBuilder {
1558 pub fn build(&self) -> Result<Redeemers, RedeemerBuilderError> {
1562 self.witness_builders.redeemer_set_builder.build(true)
1563 }
1564
1565 pub fn set_exunits(&mut self, redeemer: RedeemerWitnessKey, ex_units: ExUnits) {
1567 self.witness_builders
1568 .redeemer_set_builder
1569 .update_ex_units(redeemer, ex_units);
1570 }
1571
1572 pub fn draft_body(&self) -> TransactionBody {
1575 self.draft_body.clone()
1576 }
1577
1578 pub fn auxiliary_data(&self) -> Option<AuxiliaryData> {
1579 self.auxiliary_data.clone()
1580 }
1581
1582 pub fn draft_tx(&self) -> Result<Transaction, WitnessBuilderError> {
1586 Ok(Transaction::new(
1587 self.draft_body.clone(),
1588 self.witness_builders.build_fake()?,
1590 true,
1591 self.auxiliary_data.clone(),
1592 ))
1593 }
1594}
1595
1596#[derive(Debug, Clone)]
1597pub struct SignedTxBuilder {
1598 body: TransactionBody,
1599 witness_set: TransactionWitnessSetBuilder,
1600 is_valid: bool,
1601 auxiliary_data: Option<AuxiliaryData>,
1602}
1603
1604impl SignedTxBuilder {
1605 pub fn new_with_data(
1606 body: TransactionBody,
1607 witness_set: TransactionWitnessSetBuilder,
1608 is_valid: bool,
1609 auxiliary_data: AuxiliaryData,
1610 ) -> SignedTxBuilder {
1611 SignedTxBuilder {
1612 body,
1613 witness_set,
1614 is_valid,
1615 auxiliary_data: Some(auxiliary_data),
1616 }
1617 }
1618
1619 pub fn new_without_data(
1620 body: TransactionBody,
1621 witness_set: TransactionWitnessSetBuilder,
1622 is_valid: bool,
1623 ) -> SignedTxBuilder {
1624 SignedTxBuilder {
1625 body,
1626 witness_set,
1627 is_valid,
1628 auxiliary_data: None,
1629 }
1630 }
1631
1632 pub fn build_checked(self) -> Result<Transaction, WitnessBuilderError> {
1636 Ok(Transaction::new(
1637 self.body,
1638 self.witness_set.try_build()?,
1639 self.is_valid,
1640 self.auxiliary_data,
1641 ))
1642 }
1643
1644 pub fn build_unchecked(self) -> Transaction {
1652 Transaction::new(
1653 self.body,
1654 self.witness_set.build(),
1655 self.is_valid,
1656 self.auxiliary_data,
1657 )
1658 }
1659
1660 pub fn add_vkey(&mut self, vkey: Vkeywitness) {
1672 self.witness_set.add_vkey(vkey);
1673 }
1674
1675 pub fn add_bootstrap(&mut self, bootstrap: BootstrapWitness) {
1676 self.witness_set.add_bootstrap(bootstrap);
1677 }
1678
1679 pub fn body(&self) -> TransactionBody {
1680 self.body.clone()
1681 }
1682
1683 pub fn witness_set(&self) -> TransactionWitnessSetBuilder {
1684 self.witness_set.clone()
1685 }
1686
1687 pub fn is_valid(&self) -> bool {
1688 self.is_valid
1689 }
1690
1691 pub fn auxiliary_data(&self) -> Option<AuxiliaryData> {
1692 self.auxiliary_data.clone()
1693 }
1694}
1695
1696#[wasm_bindgen]
1697pub enum ChangeSelectionAlgo {
1698 Default,
1699}
1700
1701pub fn choose_change_selection_algo(
1702 algo: ChangeSelectionAlgo,
1703) -> fn(&mut TransactionBuilder, &Address, include_exunits: bool) -> Result<bool, TxBuilderError> {
1704 match algo {
1705 ChangeSelectionAlgo::Default => add_change_if_needed,
1706 }
1707}
1708
1709pub fn add_change_if_needed(
1714 builder: &mut TransactionBuilder,
1715 address: &Address,
1716 include_exunits: bool,
1717) -> Result<bool, TxBuilderError> {
1718 let fee = match &builder.fee {
1719 None => builder.min_fee(include_exunits),
1720 Some(_x) => return Ok(false),
1722 }?;
1723
1724 let datum = None;
1727 let script_ref = None;
1728 let communication_datum = None;
1729
1730 let input_total = builder.get_total_input()?;
1731
1732 let output_total = builder.get_total_output()?;
1733
1734 use std::cmp::Ordering;
1735 match &input_total.partial_cmp(&output_total.checked_add(&Value::from(fee))?) {
1736 Some(Ordering::Equal) => {
1737 builder.set_fee(input_total.checked_sub(&output_total)?.coin);
1739 Ok(false)
1740 }
1741 Some(Ordering::Less) => Err(TxBuilderError::UTxOBalanceInsufficient(
1742 input_total.clone(),
1743 output_total.clone(),
1744 )),
1745 Some(Ordering::Greater) => {
1746 let change_estimator = input_total.checked_sub(&output_total)?;
1747 if change_estimator.has_multiassets() {
1748 fn will_adding_asset_make_output_overflow(
1749 output: &TransactionOutput,
1750 current_assets: &OrderedHashMap<AssetName, u64>,
1751 asset_to_add: (PolicyId, AssetName, u64),
1752 max_value_size: u32,
1753 coins_per_utxo_byte: Coin,
1754 ) -> bool {
1755 let (policy, asset_name, value) = asset_to_add;
1756 let mut current_assets_clone = current_assets.clone();
1757 current_assets_clone.insert(asset_name, value);
1758 let mut amount_clone = output.amount().clone();
1759 let mut val = Value::from(Coin::zero());
1760 val.multiasset.insert(policy, current_assets_clone);
1761 amount_clone = amount_clone.checked_add(&val).unwrap();
1762
1763 let mut output_clone = output.clone();
1764 output_clone.set_amount(val);
1765
1766 let min_ada = min_ada_required(&output_clone, coins_per_utxo_byte).unwrap();
1768 amount_clone.coin = min_ada;
1769
1770 amount_clone.to_cbor_bytes().len() > max_value_size as usize
1771 }
1772 fn pack_nfts_for_change(
1773 max_value_size: u32,
1774 coins_per_utxo_byte: Coin,
1775 change_address: &Address,
1776 change_estimator: &Value,
1777 datum: Option<DatumOption>,
1778 script_ref: &Option<ScriptRef>,
1779 _communication_datum: &Option<PlutusData>,
1780 ) -> Result<Vec<MultiAsset>, TxBuilderError> {
1781 let mut change_assets: Vec<MultiAsset> = Vec::new();
1784
1785 let mut base_coin = Value::from(change_estimator.coin);
1786 let mut output = TransactionOutput::new(
1787 change_address.clone(),
1788 base_coin.clone(),
1789 datum.clone(),
1790 script_ref.clone(),
1791 );
1792 for (policy, assets) in change_estimator.multiasset.iter() {
1803 let mut old_amount = output.amount().clone();
1817 let mut val = Value::from(Coin::zero());
1818 let mut next_nft = MultiAsset::default();
1819
1820 let mut rebuilt_assets = OrderedHashMap::new();
1821 for (asset_name, value) in assets.iter() {
1822 if will_adding_asset_make_output_overflow(
1823 &output,
1824 &rebuilt_assets,
1825 (*policy, asset_name.clone(), *value),
1826 max_value_size,
1827 coins_per_utxo_byte,
1828 ) {
1829 next_nft.insert(*policy, rebuilt_assets);
1834 val.multiasset = next_nft;
1835 output.set_amount(output.amount().checked_add(&val)?);
1836 change_assets.push(output.amount().multiasset.clone());
1837
1838 base_coin = Value::from(Coin::zero());
1840 output = TransactionOutput::new(
1841 change_address.clone(),
1842 base_coin.clone(),
1843 datum.clone(),
1844 script_ref.clone(),
1845 );
1846
1847 old_amount = output.amount().clone();
1849 val = Value::from(Coin::zero());
1850 next_nft = MultiAsset::default();
1851
1852 rebuilt_assets = OrderedHashMap::new();
1853 }
1854
1855 rebuilt_assets.insert(asset_name.clone(), *value);
1856 }
1857
1858 next_nft.insert(*policy, rebuilt_assets);
1859 val.multiasset = next_nft;
1860 output.set_amount(output.amount().checked_add(&val)?);
1861
1862 let mut output_copy = output.clone();
1864 output_copy.set_amount(val);
1865 let min_ada = min_ada_required(&output_copy, coins_per_utxo_byte).unwrap();
1866
1867 let mut amount_clone = output.amount().clone();
1868 amount_clone.coin = min_ada;
1869 if amount_clone.to_cbor_bytes().len() > max_value_size as usize {
1870 output.set_amount(old_amount);
1871 break;
1872 }
1873 }
1874 change_assets.push(output.amount().multiasset.clone());
1875 Ok(change_assets)
1876 }
1877 let mut change_left = input_total.checked_sub(&output_total)?;
1878 let mut new_fee = fee;
1879 let minimum_utxo_val = min_ada_required(
1882 &TransactionOutput::new(
1883 address.clone(),
1884 Value::from(1000000),
1887 datum.clone(),
1888 script_ref.clone(),
1889 ),
1890 builder.config.coins_per_utxo_byte,
1891 )?;
1892 while let Some(Ordering::Greater) =
1893 change_left.multiasset.partial_cmp(&MultiAsset::default())
1894 {
1895 let nft_changes = pack_nfts_for_change(
1896 builder.config.max_value_size,
1897 builder.config.coins_per_utxo_byte,
1898 address,
1899 &change_left,
1900 datum.clone(),
1901 &script_ref,
1902 &communication_datum,
1903 )?;
1904 if nft_changes.is_empty() {
1905 return Err(TxBuilderError::NFTTooLargeForChange);
1907 }
1908 for nft_change in nft_changes {
1910 let change_output = (TransactionOutputBuilder {
1911 address: Some(address.clone()),
1912 datum: datum.clone(),
1913 communication_datum: communication_datum.clone(),
1914 script_ref: script_ref.clone(),
1915 })
1916 .next()?
1917 .with_asset_and_min_required_coin(
1918 nft_change,
1919 builder.config.coins_per_utxo_byte,
1920 )?
1921 .build()?;
1922
1923 let fee_for_change = builder.fee_for_output(&change_output)?;
1925 new_fee = new_fee
1926 .checked_add(fee_for_change)
1927 .ok_or(ArithmeticError::IntegerOverflow)?;
1928 let change_ada_plus_fee = change_output
1929 .output
1930 .amount()
1931 .coin
1932 .checked_add(new_fee)
1933 .ok_or(ArithmeticError::IntegerOverflow)?;
1934 if change_left.coin < change_ada_plus_fee {
1935 return Err(TxBuilderError::InsufficientADAForAssets);
1936 }
1937 change_left = change_left.checked_sub(change_output.output.amount())?;
1938 builder.add_output(change_output)?;
1939 }
1940 }
1941 change_left = change_left.checked_sub(&Value::from(new_fee))?;
1942 let left_above_minimum = change_left.coin > minimum_utxo_val;
1944 if builder.config.prefer_pure_change && left_above_minimum {
1945 let pure_output = SingleOutputBuilderResult::new(TransactionOutput::new(
1946 address.clone(),
1947 change_left.clone(),
1948 datum.clone(),
1949 script_ref.clone(),
1950 ));
1951 let additional_fee = builder.fee_for_output(&pure_output)?;
1952 let potential_pure_value =
1953 change_left.checked_sub(&Value::from(additional_fee))?;
1954 let potential_pure_above_minimum = potential_pure_value.coin > minimum_utxo_val;
1955 if potential_pure_above_minimum {
1956 new_fee = new_fee
1957 .checked_add(additional_fee)
1958 .ok_or(ArithmeticError::IntegerOverflow)?;
1959 change_left = Value::zero();
1960 let change_output = SingleOutputBuilderResult::new(TransactionOutput::new(
1961 address.clone(),
1962 potential_pure_value,
1963 datum,
1964 script_ref,
1965 ));
1966 builder.add_output(change_output)?;
1967 }
1968 }
1969 builder.set_fee(new_fee);
1970 if !change_left.is_zero() {
1972 let last_with_remaining = builder
1973 .outputs
1974 .last()
1975 .unwrap()
1976 .amount()
1977 .checked_add(&change_left)?;
1978 builder
1979 .outputs
1980 .last_mut()
1981 .unwrap()
1982 .set_amount(last_with_remaining);
1983 }
1984 Ok(true)
1985 } else {
1986 let min_ada = min_ada_required(
1987 &TransactionOutput::new(
1988 address.clone(),
1989 change_estimator.clone(),
1990 datum.clone(),
1991 script_ref.clone(),
1992 ),
1993 builder.config.coins_per_utxo_byte,
1994 )?;
1995 fn burn_extra(
1997 builder: &mut TransactionBuilder,
1998 burn_amount: u64,
1999 ) -> Result<bool, TxBuilderError> {
2000 builder.set_fee(burn_amount);
2002 Ok(false) }
2004 match change_estimator.coin >= min_ada {
2005 false => burn_extra(builder, change_estimator.coin),
2006 true => {
2007 let fee_for_change = builder.fee_for_output(
2009 &SingleOutputBuilderResult::new(TransactionOutput::new(
2010 address.clone(),
2011 change_estimator.clone(),
2012 datum.clone(),
2013 script_ref.clone(),
2014 )),
2015 )?;
2016
2017 let new_fee = fee
2018 .checked_add(fee_for_change)
2019 .ok_or(ArithmeticError::IntegerOverflow)?;
2020 match change_estimator.coin
2021 >= min_ada
2022 .checked_add(new_fee)
2023 .ok_or(ArithmeticError::IntegerOverflow)?
2024 {
2025 false => burn_extra(builder, change_estimator.coin),
2026 true => {
2027 builder.set_fee(new_fee);
2029
2030 let change_output =
2031 SingleOutputBuilderResult::new(TransactionOutput::new(
2032 address.clone(),
2033 change_estimator.checked_sub(&Value::from(new_fee))?,
2034 datum,
2035 script_ref,
2036 ));
2037
2038 builder.add_output(change_output)?;
2039
2040 Ok(true)
2041 }
2042 }
2043 }
2044 }
2045 }
2046 }
2047 None => Err(TxBuilderError::MissingInputOrOutput),
2048 }
2049}
2050
2051#[cfg(test)]
2052mod tests {
2053 use std::collections::BTreeMap;
2054 use std::ops::Deref;
2055
2056 use cml_core::Int;
2057 use cml_crypto::{
2058 Bip32PrivateKey, Bip32PublicKey, DatumHash, Deserialize, PrivateKey, RawBytesEncoding,
2059 TransactionHash,
2060 };
2061
2062 use crate::address::{Address, BaseAddress, EnterpriseAddress, Pointer, PointerAddress};
2063 use crate::auxdata::{Metadata, MetadatumMap, TransactionMetadatum, TransactionMetadatumLabel};
2064 use crate::builders::witness_builder::{PartialPlutusWitness, PlutusScriptWitness};
2065 use crate::builders::{
2066 input_builder::SingleInputBuilder, mint_builder::SingleMintBuilder,
2067 witness_builder::NativeScriptWitnessInfo,
2068 };
2069 use crate::byron::{AddressContent, ByronAddress};
2070 use crate::certs::StakeCredential;
2071 use crate::crypto::hash::hash_transaction;
2072 use crate::crypto::utils::make_vkey_witness;
2073 use crate::genesis::network_info::{plutus_alonzo_cost_models, NetworkInfo};
2074 use crate::plutus::{PlutusScript, PlutusV1Script, PlutusV2Script, RedeemerTag};
2075 use crate::transaction::NativeScript;
2076 use crate::{Script, SubCoin};
2077
2078 use super::*;
2079 use crate::builders::output_builder::TransactionOutputBuilder;
2080
2081 const MAX_VALUE_SIZE: u32 = 4000;
2082 const MAX_TX_SIZE: u32 = 8000; static COINS_PER_UTXO_BYTE: u64 = 4310;
2085
2086 impl TransactionBuilder {
2087 fn add_change_if_needed_for_tests(
2088 &mut self,
2089 change_address: &Address,
2090 ) -> Result<bool, TxBuilderError> {
2091 choose_change_selection_algo(ChangeSelectionAlgo::Default)(self, change_address, false)
2092 }
2093 }
2094
2095 fn genesis_id() -> TransactionHash {
2096 TransactionHash::from([0u8; TransactionHash::BYTE_COUNT])
2097 }
2098
2099 fn root_key_15() -> Bip32PrivateKey {
2100 let entropy = [
2102 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22,
2103 0xd4, 0xd8, 0x09, 0x7c, 0x64, 0x12,
2104 ];
2105 Bip32PrivateKey::from_bip39_entropy(&entropy, &[])
2106 }
2107
2108 fn fake_key_hash(x: u8) -> Ed25519KeyHash {
2109 Ed25519KeyHash::from_raw_bytes(&[
2110 x, 239, 181, 120, 142, 135, 19, 200, 68, 223, 211, 43, 46, 145, 222, 30, 48, 159, 239,
2111 255, 213, 85, 248, 39, 204, 158, 225, 100,
2112 ])
2113 .unwrap()
2114 }
2115
2116 fn harden(index: u32) -> u32 {
2117 index | 0x80_00_00_00
2118 }
2119
2120 fn byron_address() -> Address {
2121 ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3")
2122 .unwrap()
2123 .to_address()
2124 }
2125
2126 fn create_linear_fee(coefficient: u64, constant: u64) -> LinearFee {
2127 LinearFee::new(coefficient, constant, 0)
2128 }
2129
2130 fn create_default_linear_fee() -> LinearFee {
2131 create_linear_fee(500, 2)
2132 }
2133
2134 fn create_tx_builder_full(
2135 linear_fee: LinearFee,
2136 pool_deposit: u64,
2137 key_deposit: u64,
2138 max_val_size: u32,
2139 coins_per_utxo_byte: u64,
2140 ) -> TransactionBuilder {
2141 let cfg = TransactionBuilderConfigBuilder::default()
2142 .fee_algo(linear_fee)
2143 .pool_deposit(pool_deposit)
2144 .key_deposit(key_deposit)
2145 .max_value_size(max_val_size)
2146 .max_tx_size(MAX_TX_SIZE)
2147 .coins_per_utxo_byte(coins_per_utxo_byte)
2148 .ex_unit_prices(ExUnitPrices::new(
2149 SubCoin::new(577, 10000),
2150 SubCoin::new(721, 10000000),
2151 ))
2152 .collateral_percentage(150)
2153 .max_collateral_inputs(3)
2154 .cost_models(plutus_alonzo_cost_models())
2155 .build()
2156 .unwrap();
2157 TransactionBuilder::new(cfg)
2158 }
2159
2160 fn create_tx_builder(
2161 linear_fee: LinearFee,
2162 coins_per_utxo_byte: u64,
2163 pool_deposit: u64,
2164 key_deposit: u64,
2165 ) -> TransactionBuilder {
2166 create_tx_builder_full(
2167 linear_fee,
2168 pool_deposit,
2169 key_deposit,
2170 MAX_VALUE_SIZE,
2171 coins_per_utxo_byte,
2172 )
2173 }
2174
2175 fn create_realistic_tx_builder() -> TransactionBuilder {
2176 create_tx_builder(
2177 create_linear_fee(44, 155381),
2178 COINS_PER_UTXO_BYTE,
2179 500000000,
2180 2000000,
2181 )
2182 }
2183
2184 fn create_tx_builder_with_fee_and_val_size(
2185 linear_fee: LinearFee,
2186 max_val_size: u32,
2187 ) -> TransactionBuilder {
2188 create_tx_builder_full(linear_fee, 1, 1, max_val_size, 1)
2189 }
2190
2191 fn create_tx_builder_with_fee(linear_fee: LinearFee) -> TransactionBuilder {
2192 create_tx_builder(linear_fee, 1, 1, 1)
2193 }
2194
2195 fn create_tx_builder_with_fee_and_pure_change(linear_fee: LinearFee) -> TransactionBuilder {
2196 TransactionBuilder::new(
2197 TransactionBuilderConfigBuilder::default()
2198 .fee_algo(linear_fee)
2199 .pool_deposit(1)
2200 .key_deposit(1)
2201 .max_value_size(MAX_VALUE_SIZE)
2202 .max_tx_size(MAX_TX_SIZE)
2203 .coins_per_utxo_byte(1)
2204 .ex_unit_prices(ExUnitPrices::new(SubCoin::new(0, 0), SubCoin::new(0, 0)))
2205 .collateral_percentage(150)
2206 .max_collateral_inputs(3)
2207 .prefer_pure_change(true)
2208 .build()
2209 .unwrap(),
2210 )
2211 }
2212
2213 fn create_tx_builder_with_key_deposit(deposit: u64) -> TransactionBuilder {
2214 create_tx_builder(create_default_linear_fee(), 1, 1, deposit)
2215 }
2216
2217 fn create_default_tx_builder() -> TransactionBuilder {
2218 create_tx_builder_with_fee(create_default_linear_fee())
2219 }
2220
2221 fn create_account() -> (
2222 (Bip32PublicKey, StakeCredential),
2223 (Bip32PublicKey, StakeCredential),
2224 Address,
2225 ) {
2226 let spend = root_key_15()
2227 .derive(harden(1852))
2228 .derive(harden(1815))
2229 .derive(harden(0))
2230 .derive(0)
2231 .derive(0)
2232 .to_public();
2233 let stake = root_key_15()
2234 .derive(harden(1852))
2235 .derive(harden(1815))
2236 .derive(harden(0))
2237 .derive(2)
2238 .derive(0)
2239 .to_public();
2240
2241 let spend_cred = StakeCredential::new_pub_key(spend.to_raw_key().hash());
2242 let stake_cred = StakeCredential::new_pub_key(stake.to_raw_key().hash());
2243 let address = BaseAddress::new(
2244 NetworkInfo::testnet().network_id(),
2245 spend_cred.clone(),
2246 stake_cred.clone(),
2247 )
2248 .to_address();
2249
2250 ((spend, spend_cred), (stake, stake_cred), address)
2251 }
2252
2253 #[test]
2254 fn build_tx_with_change() {
2255 let mut tx_builder = create_default_tx_builder();
2256 let change_key = root_key_15()
2257 .derive(harden(1852))
2258 .derive(harden(1815))
2259 .derive(harden(0))
2260 .derive(1)
2261 .derive(0)
2262 .to_public();
2263 let (_, (_, stake_cred), addr_net_0) = create_account();
2264
2265 let input = {
2266 SingleInputBuilder::new(
2267 TransactionInput::new(genesis_id(), 0),
2268 TransactionOutput::new(addr_net_0.clone(), Value::from(1_000_000), None, None),
2269 )
2270 .payment_key()
2271 .unwrap()
2272 };
2273 tx_builder.add_input(input).unwrap();
2274 tx_builder
2275 .add_output(
2276 TransactionOutputBuilder::new()
2277 .with_address(addr_net_0)
2278 .next()
2279 .unwrap()
2280 .with_value(222)
2281 .build()
2282 .unwrap(),
2283 )
2284 .unwrap();
2285 tx_builder.set_ttl(1000);
2286
2287 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2288 let change_addr =
2289 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2290 .to_address();
2291 let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr);
2292 assert!(added_change.unwrap());
2293 assert_eq!(tx_builder.outputs.len(), 2);
2294 assert_eq!(
2295 tx_builder
2296 .get_explicit_input()
2297 .unwrap()
2298 .checked_add(&tx_builder.get_implicit_input().unwrap())
2299 .unwrap(),
2300 tx_builder
2301 .get_explicit_output()
2302 .unwrap()
2303 .checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
2304 .unwrap()
2305 );
2306 assert_eq!(tx_builder.full_size().unwrap(), 291);
2307 assert_eq!(tx_builder.output_sizes(), vec![62, 65]);
2308 let _final_tx = tx_builder.build(ChangeSelectionAlgo::Default, &change_addr);
2309 }
2311
2312 #[test]
2313 fn build_tx_without_change() {
2314 let mut tx_builder = create_default_tx_builder();
2315 let change_key = root_key_15()
2316 .derive(harden(1852))
2317 .derive(harden(1815))
2318 .derive(harden(0))
2319 .derive(1)
2320 .derive(0)
2321 .to_public();
2322 let (_, (_, stake_cred), addr_net_0) = create_account();
2323 let input = {
2324 SingleInputBuilder::new(
2325 TransactionInput::new(genesis_id(), 0),
2326 TransactionOutput::new(addr_net_0.clone(), Value::from(1_000_000), None, None),
2327 )
2328 .payment_key()
2329 .unwrap()
2330 };
2331 tx_builder.add_input(input).unwrap();
2332 tx_builder
2333 .add_output(
2334 TransactionOutputBuilder::new()
2335 .with_address(addr_net_0)
2336 .next()
2337 .unwrap()
2338 .with_value(880_000)
2339 .build()
2340 .unwrap(),
2341 )
2342 .unwrap();
2343 tx_builder.set_ttl(1000);
2344
2345 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2346 let change_addr =
2347 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2348 .to_address();
2349 let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr);
2350 assert!(!added_change.unwrap());
2351 assert_eq!(tx_builder.outputs.len(), 1);
2352 assert_eq!(
2353 tx_builder
2354 .get_explicit_input()
2355 .unwrap()
2356 .checked_add(&tx_builder.get_implicit_input().unwrap())
2357 .unwrap(),
2358 tx_builder
2359 .get_explicit_output()
2360 .unwrap()
2361 .checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
2362 .unwrap()
2363 );
2364 let _final_tx = tx_builder.build(ChangeSelectionAlgo::Default, &change_addr);
2365 }
2367
2368 #[test]
2369 fn build_tx_with_certs() {
2370 let mut tx_builder = create_tx_builder_with_key_deposit(1_000_000);
2371 let change_key = root_key_15()
2372 .derive(harden(1852))
2373 .derive(harden(1815))
2374 .derive(harden(0))
2375 .derive(1)
2376 .derive(0)
2377 .to_public();
2378 let (_, (stake, stake_cred), addr_net_0) = create_account();
2379
2380 let input = {
2381 SingleInputBuilder::new(
2382 TransactionInput::new(genesis_id(), 0),
2383 TransactionOutput::new(addr_net_0, Value::from(5_000_000), None, None),
2384 )
2385 .payment_key()
2386 .unwrap()
2387 };
2388 tx_builder.add_input(input).unwrap();
2389 tx_builder.set_ttl(1000);
2390
2391 let cert =
2392 SingleCertificateBuilder::new(Certificate::new_stake_registration(stake_cred.clone()))
2393 .payment_key()
2394 .unwrap();
2395 tx_builder.add_cert(cert);
2396
2397 let cert = SingleCertificateBuilder::new(Certificate::new_stake_delegation(
2398 stake_cred.clone(),
2399 stake.to_raw_key().hash(), ))
2401 .payment_key()
2402 .unwrap();
2403 tx_builder.add_cert(cert);
2404
2405 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2406 let change_addr =
2407 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2408 .to_address();
2409 tx_builder
2410 .add_change_if_needed_for_tests(&change_addr)
2411 .unwrap();
2412 assert_eq!(tx_builder.min_fee(false).unwrap(), 218502);
2413 assert_eq!(tx_builder.get_fee_if_set().unwrap(), 218502);
2414 assert_eq!(tx_builder.get_deposit().unwrap(), 1000000);
2415 assert_eq!(tx_builder.outputs.len(), 1);
2416 assert_eq!(
2417 tx_builder
2418 .get_explicit_input()
2419 .unwrap()
2420 .checked_add(&tx_builder.get_implicit_input().unwrap())
2421 .unwrap(),
2422 tx_builder
2423 .get_explicit_output()
2424 .unwrap()
2425 .checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
2426 .unwrap()
2427 .checked_add(&Value::from(tx_builder.get_deposit().unwrap()))
2428 .unwrap()
2429 );
2430 let _final_tx = tx_builder.build(ChangeSelectionAlgo::Default, &change_addr);
2431 }
2433
2434 #[test]
2435 fn build_tx_exact_amount() {
2436 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
2438 let change_key = root_key_15()
2439 .derive(harden(1852))
2440 .derive(harden(1815))
2441 .derive(harden(0))
2442 .derive(1)
2443 .derive(0)
2444 .to_public();
2445 let (_, (_, stake_cred), addr_net_0) = create_account();
2446
2447 let input = {
2448 SingleInputBuilder::new(
2449 TransactionInput::new(genesis_id(), 0),
2450 TransactionOutput::new(addr_net_0.clone(), Value::from(222), None, None),
2451 )
2452 .payment_key()
2453 .unwrap()
2454 };
2455 tx_builder.add_input(input).unwrap();
2456
2457 tx_builder
2458 .add_output(
2459 TransactionOutputBuilder::new()
2460 .with_address(addr_net_0)
2461 .next()
2462 .unwrap()
2463 .with_value(222)
2464 .build()
2465 .unwrap(),
2466 )
2467 .unwrap();
2468 tx_builder.set_ttl(0);
2469
2470 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2471 let change_addr =
2472 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2473 .to_address();
2474 let added_change = tx_builder
2475 .add_change_if_needed_for_tests(&change_addr)
2476 .unwrap();
2477 assert!(!added_change);
2478 let final_tx = tx_builder.build_body().unwrap();
2479 assert_eq!(final_tx.outputs.len(), 1);
2480 }
2481
2482 #[test]
2483 fn build_tx_exact_change() {
2484 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
2486 let change_key = root_key_15()
2487 .derive(harden(1852))
2488 .derive(harden(1815))
2489 .derive(harden(0))
2490 .derive(1)
2491 .derive(0)
2492 .to_public();
2493 let (_, (_, stake_cred), addr_net_0) = create_account();
2494
2495 let input = {
2496 SingleInputBuilder::new(
2497 TransactionInput::new(genesis_id(), 0),
2498 TransactionOutput::new(addr_net_0.clone(), Value::from(542), None, None),
2499 )
2500 .payment_key()
2501 .unwrap()
2502 };
2503 tx_builder.add_input(input).unwrap();
2504
2505 tx_builder
2506 .add_output(
2507 TransactionOutputBuilder::new()
2508 .with_address(addr_net_0)
2509 .next()
2510 .unwrap()
2511 .with_value(222)
2512 .build()
2513 .unwrap(),
2514 )
2515 .unwrap();
2516 tx_builder.set_ttl(0);
2517
2518 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2519 let change_addr =
2520 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2521 .to_address();
2522 let added_change = tx_builder
2523 .add_change_if_needed_for_tests(&change_addr)
2524 .unwrap();
2525 assert!(added_change);
2526 let final_tx = tx_builder.build_body().unwrap();
2527 assert_eq!(final_tx.outputs.len(), 2);
2528 assert_eq!(final_tx.outputs[1].amount().coin, 320);
2529 }
2530
2531 #[test]
2532 #[should_panic]
2533 fn build_tx_insufficient_deposit() {
2534 let mut tx_builder = create_tx_builder_with_key_deposit(5);
2536 let change_key = root_key_15()
2537 .derive(harden(1852))
2538 .derive(harden(1815))
2539 .derive(harden(0))
2540 .derive(1)
2541 .derive(0)
2542 .to_public();
2543 let (_, (_, stake_cred), addr_net_0) = create_account();
2544
2545 let input = {
2546 SingleInputBuilder::new(
2547 TransactionInput::new(genesis_id(), 0),
2548 TransactionOutput::new(addr_net_0.clone(), Value::from(5), None, None),
2549 )
2550 .payment_key()
2551 .unwrap()
2552 };
2553 tx_builder.add_input(input).unwrap();
2554
2555 tx_builder
2556 .add_output(
2557 TransactionOutputBuilder::new()
2558 .with_address(addr_net_0)
2559 .next()
2560 .unwrap()
2561 .with_value(5)
2562 .build()
2563 .unwrap(),
2564 )
2565 .unwrap();
2566 tx_builder.set_ttl(0);
2567
2568 let cert =
2570 SingleCertificateBuilder::new(Certificate::new_stake_registration(stake_cred.clone()))
2571 .payment_key()
2572 .unwrap();
2573 tx_builder.add_cert(cert);
2574
2575 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2576 let change_addr =
2577 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2578 .to_address();
2579
2580 tx_builder
2581 .add_change_if_needed_for_tests(&change_addr)
2582 .unwrap();
2583 }
2584
2585 #[test]
2586 fn build_tx_with_inputs() {
2587 let mut tx_builder = create_default_tx_builder();
2588 let ((spend, spend_cred), (_, stake_cred), _) = create_account();
2589
2590 let input = {
2591 let address =
2592 EnterpriseAddress::new(NetworkInfo::testnet().network_id(), spend_cred.clone())
2593 .to_address();
2594 SingleInputBuilder::new(
2595 TransactionInput::new(genesis_id(), 0),
2596 TransactionOutput::new(address, Value::from(1_000_000), None, None),
2597 )
2598 .payment_key()
2599 .unwrap()
2600 };
2601 assert_eq!(tx_builder.fee_for_input(&input).unwrap(), 71000);
2602 tx_builder.add_input(input).unwrap();
2603
2604 let input = {
2605 let address = BaseAddress::new(
2606 NetworkInfo::testnet().network_id(),
2607 spend_cred.clone(),
2608 stake_cred,
2609 )
2610 .to_address();
2611 SingleInputBuilder::new(
2612 TransactionInput::new(genesis_id(), 0),
2613 TransactionOutput::new(address, Value::from(1_000_000), None, None),
2614 )
2615 .payment_key()
2616 .unwrap()
2617 };
2618 tx_builder.add_input(input).unwrap();
2619
2620 let input = {
2621 let address = PointerAddress::new(
2622 NetworkInfo::testnet().network_id(),
2623 spend_cred,
2624 Pointer::new(0, 0, 0),
2625 )
2626 .to_address();
2627 SingleInputBuilder::new(
2628 TransactionInput::new(genesis_id(), 0),
2629 TransactionOutput::new(address, Value::from(1_000_000), None, None),
2630 )
2631 .payment_key()
2632 .unwrap()
2633 };
2634 tx_builder.add_input(input).unwrap();
2635
2636 let input = {
2637 let address =
2638 AddressContent::icarus_from_key(spend, NetworkInfo::testnet().protocol_magic())
2639 .to_address()
2640 .to_address();
2641 SingleInputBuilder::new(
2642 TransactionInput::new(genesis_id(), 0),
2643 TransactionOutput::new(address, Value::from(1_000_000), None, None),
2644 )
2645 .payment_key()
2646 .unwrap()
2647 };
2648 tx_builder.add_input(input).unwrap();
2649
2650 assert_eq!(tx_builder.inputs.len(), 4);
2651 }
2652
2653 #[test]
2654 fn build_tx_with_mint_all_sent() {
2655 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 1));
2656 let change_key = root_key_15()
2657 .derive(harden(1852))
2658 .derive(harden(1815))
2659 .derive(harden(0))
2660 .derive(1)
2661 .derive(0)
2662 .to_public();
2663 let ((_, spend_cred), (_, stake_cred), _) = create_account();
2664
2665 let input = {
2666 let address =
2667 EnterpriseAddress::new(NetworkInfo::testnet().network_id(), spend_cred.clone())
2668 .to_address();
2669 SingleInputBuilder::new(
2670 TransactionInput::new(genesis_id(), 0),
2671 TransactionOutput::new(address, Value::from(764), None, None),
2672 )
2673 .payment_key()
2674 .unwrap()
2675 };
2676 tx_builder.add_input(input).unwrap();
2677
2678 let addr_net_0 = BaseAddress::new(
2679 NetworkInfo::testnet().network_id(),
2680 spend_cred,
2681 stake_cred.clone(),
2682 )
2683 .to_address();
2684
2685 let (min_script, policy_id) = mint_script_and_policy(0);
2686 let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
2687 let amount = 1234u64;
2688
2689 let result = SingleMintBuilder::new_single_asset(name.clone(), amount as i64)
2690 .native_script(
2691 min_script,
2692 NativeScriptWitnessInfo::assume_signature_count(),
2693 );
2694
2695 tx_builder.add_mint(result).unwrap();
2696
2697 let mut mass = MultiAsset::new();
2698 mass.set(policy_id, name, amount);
2699
2700 let mut output_amount = Value::from(264);
2702 output_amount.multiasset = mass;
2703
2704 tx_builder
2705 .add_output(
2706 TransactionOutputBuilder::new()
2707 .with_address(addr_net_0)
2708 .next()
2709 .unwrap()
2710 .with_value(output_amount)
2711 .build()
2712 .unwrap(),
2713 )
2714 .unwrap();
2715
2716 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2717 let change_addr =
2718 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2719 .to_address();
2720
2721 let added_change = tx_builder
2722 .add_change_if_needed_for_tests(&change_addr)
2723 .unwrap();
2724 assert!(added_change);
2725 assert_eq!(tx_builder.outputs.len(), 2);
2726
2727 let change = tx_builder.outputs[1].amount();
2729 assert_eq!(change.coin, 499);
2730 assert!(!change.has_multiassets());
2731 }
2732
2733 #[test]
2734 fn build_tx_with_mint_in_change() {
2735 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 1));
2736 let change_key = root_key_15()
2737 .derive(harden(1852))
2738 .derive(harden(1815))
2739 .derive(harden(0))
2740 .derive(1)
2741 .derive(0)
2742 .to_public();
2743 let ((_spend, spend_cred), (_, stake_cred), _) = create_account();
2744
2745 let input = {
2746 let address =
2747 EnterpriseAddress::new(NetworkInfo::testnet().network_id(), spend_cred.clone())
2748 .to_address();
2749 SingleInputBuilder::new(
2750 TransactionInput::new(genesis_id(), 0),
2751 TransactionOutput::new(address, Value::from(564), None, None),
2752 )
2753 .payment_key()
2754 .unwrap()
2755 };
2756 tx_builder.add_input(input).unwrap();
2757
2758 let addr_net_0 = BaseAddress::new(
2759 NetworkInfo::testnet().network_id(),
2760 spend_cred,
2761 stake_cred.clone(),
2762 )
2763 .to_address();
2764
2765 let (min_script, policy_id) = mint_script_and_policy(0);
2766 let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
2767
2768 let amount_minted = 1000i64;
2769 let amount_sent = 500u64;
2770
2771 let result = SingleMintBuilder::new_single_asset(name.clone(), amount_minted)
2772 .native_script(
2773 min_script,
2774 NativeScriptWitnessInfo::assume_signature_count(),
2775 );
2776
2777 tx_builder.add_mint(result).unwrap();
2778
2779 let mut mass = MultiAsset::new();
2780 mass.set(policy_id, name.clone(), amount_sent);
2781
2782 let mut output_amount = Value::from(264);
2784 output_amount.multiasset = mass;
2785
2786 tx_builder
2787 .add_output(
2788 TransactionOutputBuilder::new()
2789 .with_address(addr_net_0)
2790 .next()
2791 .unwrap()
2792 .with_value(output_amount)
2793 .build()
2794 .unwrap(),
2795 )
2796 .unwrap();
2797
2798 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2799 let change_addr =
2800 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2801 .to_address();
2802
2803 let added_change = tx_builder
2804 .add_change_if_needed_for_tests(&change_addr)
2805 .unwrap();
2806 assert!(added_change);
2807 assert_eq!(tx_builder.outputs.len(), 2);
2808
2809 let change = tx_builder.outputs[1].amount();
2811 assert_eq!(change.coin, 299);
2812 assert!(change.has_multiassets());
2813
2814 assert_eq!(
2815 change.multiasset.get(&policy_id, &name).unwrap() as i128,
2816 amount_minted.checked_sub(amount_sent as i64).unwrap() as i128,
2817 );
2818 }
2819
2820 #[test]
2821 fn build_tx_with_native_assets_change() {
2822 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 1));
2823 let change_key = root_key_15()
2824 .derive(harden(1852))
2825 .derive(harden(1815))
2826 .derive(harden(0))
2827 .derive(1)
2828 .derive(0)
2829 .to_public();
2830 let (_, (_, stake_cred), addr_net_0) = create_account();
2831
2832 let policy_id = PolicyId::from([0u8; 28]);
2833 let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
2834
2835 let ma_input1 = 100;
2836 let ma_input2 = 200;
2837 let ma_output1 = 60;
2838
2839 let multiassets = [ma_input1, ma_input2, ma_output1]
2840 .iter()
2841 .map(|input| {
2842 let mut multiasset = MultiAsset::new();
2843 multiasset.set(policy_id, name.clone(), *input);
2844 multiasset
2845 })
2846 .collect::<Vec<MultiAsset>>();
2847
2848 for (multiasset, ada) in multiassets.iter().zip([500u64, 500].iter().cloned()) {
2849 let mut input_amount = Value::from(ada);
2850 input_amount.multiasset = multiasset.clone();
2851
2852 let input = {
2853 SingleInputBuilder::new(
2854 TransactionInput::new(genesis_id(), 0),
2855 TransactionOutput::new(addr_net_0.clone(), input_amount, None, None),
2856 )
2857 .payment_key()
2858 .unwrap()
2859 };
2860 tx_builder.add_input(input).unwrap();
2861 }
2862
2863 let mut output_amount = Value::from(263);
2864 output_amount.multiasset = multiassets[2].clone();
2865
2866 tx_builder
2867 .add_output(
2868 TransactionOutputBuilder::new()
2869 .with_address(addr_net_0)
2870 .next()
2871 .unwrap()
2872 .with_value(output_amount)
2873 .build()
2874 .unwrap(),
2875 )
2876 .unwrap();
2877
2878 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2879 let change_addr =
2880 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2881 .to_address();
2882
2883 let added_change = tx_builder
2884 .add_change_if_needed_for_tests(&change_addr)
2885 .unwrap();
2886 assert!(added_change);
2887 let final_tx = tx_builder.build_body().unwrap();
2888 assert_eq!(final_tx.outputs.len(), 2);
2889 assert_eq!(
2890 final_tx.outputs[1]
2891 .amount()
2892 .multiasset
2893 .get(&policy_id, &name)
2894 .unwrap(),
2895 ma_input1 + ma_input2 - ma_output1
2896 );
2897 assert_eq!(final_tx.outputs[1].amount().coin, 736);
2898 }
2899
2900 #[test]
2901 fn build_tx_with_native_assets_change_and_purification() {
2902 let coin_per_utxo_byte = 1;
2903 let mut tx_builder = create_tx_builder_with_fee_and_pure_change(create_linear_fee(0, 1));
2905 let change_key = root_key_15()
2906 .derive(harden(1852))
2907 .derive(harden(1815))
2908 .derive(harden(0))
2909 .derive(1)
2910 .derive(0)
2911 .to_public();
2912 let (_, (_, stake_cred), addr_net_0) = create_account();
2913
2914 let policy_id = &PolicyId::from([0u8; 28]);
2915 let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
2916
2917 let ma_input1 = 100;
2918 let ma_input2 = 200;
2919 let ma_output1 = 60;
2920
2921 let multiassets = [ma_input1, ma_input2, ma_output1]
2922 .iter()
2923 .map(|input| {
2924 let mut multiasset = MultiAsset::new();
2925 multiasset.set(*policy_id, name.clone(), *input);
2926 multiasset
2927 })
2928 .collect::<Vec<MultiAsset>>();
2929
2930 for (multiasset, ada) in multiassets.iter().zip([500u64, 500].iter().cloned()) {
2931 let mut input_amount = Value::from(ada);
2932 input_amount.multiasset = multiasset.clone();
2933
2934 let input = {
2935 SingleInputBuilder::new(
2936 TransactionInput::new(genesis_id(), 0),
2937 TransactionOutput::new(addr_net_0.clone(), input_amount, None, None),
2938 )
2939 .payment_key()
2940 .unwrap()
2941 };
2942 tx_builder.add_input(input).unwrap();
2943 }
2944
2945 let mut output_amount = Value::from(263);
2946 output_amount.multiasset = multiassets[2].clone();
2947
2948 tx_builder
2949 .add_output(
2950 TransactionOutputBuilder::new()
2951 .with_address(addr_net_0)
2952 .next()
2953 .unwrap()
2954 .with_value(output_amount)
2955 .build()
2956 .unwrap(),
2957 )
2958 .unwrap();
2959
2960 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
2961 let change_addr =
2962 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
2963 .to_address();
2964
2965 let added_change = tx_builder
2966 .add_change_if_needed_for_tests(&change_addr)
2967 .unwrap();
2968 assert!(added_change);
2969 let final_tx = tx_builder.build_body().unwrap();
2970 assert_eq!(final_tx.outputs.len(), 3);
2971 assert_eq!(final_tx.outputs[0].amount().coin, 263);
2972 assert_eq!(
2973 final_tx.outputs[1]
2974 .amount()
2975 .multiasset
2976 .get(policy_id, &name)
2977 .unwrap(),
2978 ma_input1 + ma_input2 - ma_output1
2979 );
2980 let min_coin_for_dirty_change =
2982 min_ada_required(&final_tx.outputs[1], coin_per_utxo_byte).unwrap();
2983 assert_eq!(final_tx.outputs[1].amount().coin, min_coin_for_dirty_change);
2984 assert_eq!(final_tx.outputs[2].amount().coin, 473);
2985 assert!(!final_tx.outputs[2].amount().has_multiassets());
2986 }
2987
2988 #[test]
2989 fn build_tx_with_native_assets_change_and_no_purification_cuz_not_enough_pure_coin() {
2990 let mut tx_builder = create_tx_builder_with_fee_and_pure_change(create_linear_fee(1, 1));
2992 let change_key = root_key_15()
2993 .derive(harden(1852))
2994 .derive(harden(1815))
2995 .derive(harden(0))
2996 .derive(1)
2997 .derive(0)
2998 .to_public();
2999 let (_, (_, stake_cred), addr_net_0) = create_account();
3000
3001 let policy_id = &PolicyId::from([0u8; 28]);
3002 let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
3003
3004 let ma_input1 = 100;
3005 let ma_input2 = 200;
3006 let ma_output1 = 60;
3007
3008 let multiassets = [ma_input1, ma_input2, ma_output1]
3009 .iter()
3010 .map(|input| {
3011 let mut multiasset = MultiAsset::new();
3012 multiasset.set(*policy_id, name.clone(), *input);
3013 multiasset
3014 })
3015 .collect::<Vec<MultiAsset>>();
3016
3017 for (multiasset, ada) in multiassets.iter().zip([500u64, 500].iter().cloned()) {
3018 let mut input_amount = Value::from(ada);
3019 input_amount.multiasset = multiasset.clone();
3020
3021 let input = {
3022 SingleInputBuilder::new(
3023 TransactionInput::new(genesis_id(), 0),
3024 TransactionOutput::new(addr_net_0.clone(), input_amount, None, None),
3025 )
3026 .payment_key()
3027 .unwrap()
3028 };
3029 tx_builder.add_input(input).unwrap();
3030 }
3031
3032 let output_amount = Value::new(263, multiassets[2].clone());
3033
3034 tx_builder
3035 .add_output(
3036 TransactionOutputBuilder::new()
3037 .with_address(addr_net_0)
3038 .next()
3039 .unwrap()
3040 .with_value(output_amount)
3041 .build()
3042 .unwrap(),
3043 )
3044 .unwrap();
3045
3046 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
3047 let change_addr =
3048 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
3049 .to_address();
3050
3051 let added_change = tx_builder
3052 .add_change_if_needed_for_tests(&change_addr)
3053 .unwrap();
3054 assert!(added_change);
3055 let final_tx = tx_builder.build_body().unwrap();
3056 assert_eq!(final_tx.outputs.len(), 2);
3057 assert_eq!(final_tx.outputs[0].amount().coin, 263);
3058 assert_eq!(
3059 final_tx.outputs[1]
3060 .amount()
3061 .multiasset
3062 .get(policy_id, &name)
3063 .unwrap(),
3064 ma_input1 + ma_input2 - ma_output1
3065 );
3066 assert_eq!(final_tx.outputs[1].amount().coin, 330);
3069 }
3070
3071 #[test]
3072 #[should_panic]
3073 fn build_tx_leftover_assets() {
3074 let mut tx_builder = create_default_tx_builder();
3075 let change_key = root_key_15()
3076 .derive(harden(1852))
3077 .derive(harden(1815))
3078 .derive(harden(0))
3079 .derive(1)
3080 .derive(0)
3081 .to_public();
3082 let (_, (_, stake_cred), addr_net_0) = create_account();
3083
3084 let policy_id = PolicyId::from([0u8; 28]);
3086 let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
3087 let input_amount = {
3088 let mut input_multiasset = MultiAsset::new();
3089 input_multiasset.set(policy_id, name, 100);
3090 Value::new(1_000_000, input_multiasset)
3091 };
3092 let input = {
3093 SingleInputBuilder::new(
3094 TransactionInput::new(genesis_id(), 0),
3095 TransactionOutput::new(addr_net_0.clone(), input_amount, None, None),
3096 )
3097 .payment_key()
3098 .unwrap()
3099 };
3100 tx_builder.add_input(input).unwrap();
3101
3102 tx_builder
3103 .add_output(
3104 TransactionOutputBuilder::new()
3105 .with_address(addr_net_0)
3106 .next()
3107 .unwrap()
3108 .with_value(880_000)
3109 .build()
3110 .unwrap(),
3111 )
3112 .unwrap();
3113 tx_builder.set_ttl(1000);
3114
3115 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
3116 let change_addr =
3117 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
3118 .to_address();
3119 let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr);
3120 assert!(!added_change.unwrap());
3121 assert_eq!(tx_builder.outputs.len(), 1);
3122 assert_eq!(
3123 tx_builder
3124 .get_explicit_input()
3125 .unwrap()
3126 .checked_add(&tx_builder.get_implicit_input().unwrap())
3127 .unwrap(),
3128 tx_builder
3129 .get_explicit_output()
3130 .unwrap()
3131 .checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
3132 .unwrap()
3133 );
3134 let _final_tx = tx_builder.build_body(); }
3136
3137 #[test]
3138 fn build_tx_burn_less_than_min_ada() {
3139 let mut tx_builder = create_realistic_tx_builder();
3141
3142 let output_addr = ByronAddress::from_base58(
3143 "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
3144 )
3145 .unwrap();
3146 tx_builder
3147 .add_output(
3148 TransactionOutputBuilder::new()
3149 .with_address(output_addr.to_address())
3150 .next()
3151 .unwrap()
3152 .with_value(Value::from(2_000_000))
3153 .build()
3154 .unwrap(),
3155 )
3156 .unwrap();
3157
3158 let input = {
3159 let address = ByronAddress::from_base58(
3160 "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
3161 )
3162 .unwrap()
3163 .to_address();
3164 let builder = SingleInputBuilder::new(
3165 TransactionInput::new(genesis_id(), 0),
3166 TransactionOutput::new(address, Value::from(2_400_000), None, None),
3167 );
3168 builder.payment_key().unwrap()
3169 };
3170 tx_builder.add_input(input).unwrap();
3171
3172 tx_builder.set_ttl(1);
3173
3174 let change_addr = ByronAddress::from_base58(
3175 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3176 )
3177 .unwrap();
3178 let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr.to_address());
3179 assert!(!added_change.unwrap());
3180 assert_eq!(tx_builder.outputs.len(), 1);
3181 assert_eq!(
3182 tx_builder
3183 .get_explicit_input()
3184 .unwrap()
3185 .checked_add(&tx_builder.get_implicit_input().unwrap())
3186 .unwrap(),
3187 tx_builder
3188 .get_explicit_output()
3189 .unwrap()
3190 .checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
3191 .unwrap()
3192 );
3193 let _final_tx = tx_builder.build_body(); }
3195
3196 #[test]
3197 fn build_tx_burn_empty_assets() {
3198 let mut tx_builder = create_realistic_tx_builder();
3199
3200 let output_addr = ByronAddress::from_base58(
3201 "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
3202 )
3203 .unwrap();
3204 tx_builder
3205 .add_output(
3206 TransactionOutputBuilder::new()
3207 .with_address(output_addr.to_address())
3208 .next()
3209 .unwrap()
3210 .with_value(Value::from(2_000_000))
3211 .build()
3212 .unwrap(),
3213 )
3214 .unwrap();
3215
3216 let input_value = Value::from(2_400_000);
3217 let input = {
3218 let address = ByronAddress::from_base58(
3219 "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
3220 )
3221 .unwrap()
3222 .to_address();
3223 let builder = SingleInputBuilder::new(
3224 TransactionInput::new(genesis_id(), 0),
3225 TransactionOutput::new(address, input_value, None, None),
3226 );
3227 builder.payment_key().unwrap()
3228 };
3229 tx_builder.add_input(input).unwrap();
3230
3231 tx_builder.set_ttl(1);
3232
3233 let change_addr = ByronAddress::from_base58(
3234 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3235 )
3236 .unwrap();
3237 let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr.to_address());
3238 assert!(!added_change.unwrap());
3239 assert_eq!(tx_builder.outputs.len(), 1);
3240 assert_eq!(
3241 tx_builder
3242 .get_explicit_input()
3243 .unwrap()
3244 .checked_add(&tx_builder.get_implicit_input().unwrap())
3245 .unwrap()
3246 .coin,
3247 tx_builder
3248 .get_explicit_output()
3249 .unwrap()
3250 .checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
3251 .unwrap()
3252 .coin
3253 );
3254 let _final_tx = tx_builder.build_body(); }
3256
3257 #[test]
3258 fn build_tx_no_useless_multiasset() {
3259 let mut tx_builder = create_realistic_tx_builder();
3260
3261 let policy_id = PolicyId::from([0u8; 28]);
3262 let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
3263
3264 let input_amount = {
3266 let mut input_multiasset = MultiAsset::new();
3267 input_multiasset.set(policy_id, name.clone(), 100);
3268 Value::new(5_000_000, input_multiasset)
3269 };
3270
3271 let input = {
3272 let address = ByronAddress::from_base58(
3273 "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
3274 )
3275 .unwrap()
3276 .to_address();
3277 let builder = SingleInputBuilder::new(
3278 TransactionInput::new(genesis_id(), 0),
3279 TransactionOutput::new(address, input_amount, None, None),
3280 );
3281 builder.payment_key().unwrap()
3282 };
3283 tx_builder.add_input(input).unwrap();
3284
3285 let output_amount = {
3287 let mut output_multiasset = MultiAsset::new();
3288 output_multiasset.set(policy_id, name, 100);
3289 Value::new(2_000_000, output_multiasset)
3290 };
3291
3292 let output_addr = ByronAddress::from_base58(
3293 "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
3294 )
3295 .unwrap();
3296 tx_builder
3297 .add_output(
3298 TransactionOutputBuilder::new()
3299 .with_address(output_addr.to_address())
3300 .next()
3301 .unwrap()
3302 .with_value(output_amount)
3303 .build()
3304 .unwrap(),
3305 )
3306 .unwrap();
3307
3308 tx_builder.set_ttl(1);
3309
3310 let change_addr = ByronAddress::from_base58(
3311 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3312 )
3313 .unwrap();
3314 let added_change = tx_builder.add_change_if_needed_for_tests(&change_addr.to_address());
3315 assert!(added_change.unwrap());
3316 assert_eq!(tx_builder.outputs.len(), 2);
3317 let final_tx = tx_builder.build_body().unwrap();
3318 let change_output = &final_tx.outputs[1];
3319
3320 assert!(!change_output.amount().has_multiassets());
3323 }
3324
3325 fn create_multiasset() -> (MultiAsset, [ScriptHash; 3], [AssetName; 3]) {
3326 let policy_ids = [
3327 PolicyId::from([0u8; 28]),
3328 PolicyId::from([1u8; 28]),
3329 PolicyId::from([2u8; 28]),
3330 ];
3331 let names = [
3332 AssetName::new(vec![99u8; 32]).unwrap(),
3333 AssetName::new(vec![0u8, 1, 2, 3]).unwrap(),
3334 AssetName::new(vec![4u8, 5, 6, 7, 8, 9]).unwrap(),
3335 ];
3336 let multiasset = policy_ids.iter().zip(names.iter()).fold(
3337 MultiAsset::new(),
3338 |mut acc, (policy_id, name)| {
3339 acc.set(*policy_id, name.clone(), 500);
3340 acc
3341 },
3342 );
3343 (multiasset, policy_ids, names)
3344 }
3345
3346 #[test]
3347 fn build_tx_add_change_split_nfts() {
3348 let max_value_size = 100; let mut tx_builder =
3350 create_tx_builder_with_fee_and_val_size(create_linear_fee(0, 1), max_value_size);
3351
3352 let (multiasset, policy_ids, names) = create_multiasset();
3353
3354 let mut input_value = Value::from(1000);
3355 input_value.multiasset = multiasset;
3356
3357 let input = {
3358 let address = ByronAddress::from_base58(
3359 "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
3360 )
3361 .unwrap()
3362 .to_address();
3363 let builder = SingleInputBuilder::new(
3364 TransactionInput::new(genesis_id(), 0),
3365 TransactionOutput::new(address, input_value, None, None),
3366 );
3367 builder.payment_key().unwrap()
3368 };
3369 tx_builder.add_input(input).unwrap();
3370
3371 let output_addr = ByronAddress::from_base58(
3372 "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
3373 )
3374 .unwrap()
3375 .to_address();
3376 let output_amount = Value::from(208);
3377
3378 tx_builder
3379 .add_output(
3380 TransactionOutputBuilder::new()
3381 .with_address(output_addr)
3382 .next()
3383 .unwrap()
3384 .with_value(output_amount)
3385 .build()
3386 .unwrap(),
3387 )
3388 .unwrap();
3389
3390 let change_addr = ByronAddress::from_base58(
3391 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3392 )
3393 .unwrap()
3394 .to_address();
3395
3396 let added_change = tx_builder
3397 .add_change_if_needed_for_tests(&change_addr)
3398 .unwrap();
3399 assert!(added_change);
3400 let final_tx = tx_builder.build_body().unwrap();
3401 assert_eq!(final_tx.outputs.len(), 3);
3402 for (policy_id, asset_name) in policy_ids.iter().zip(names.iter()) {
3403 assert!(final_tx.outputs.iter().any(|output| output
3404 .amount()
3405 .multiasset
3406 .iter()
3407 .any(|(pid, a)| pid == policy_id && a.iter().any(|(name, _)| name == asset_name))));
3408 }
3409 for output in final_tx.outputs.iter() {
3410 assert!(output.amount().to_cbor_bytes().len() <= max_value_size as usize);
3411 }
3412 }
3413
3414 #[test]
3415 fn build_tx_too_big_output() {
3416 let mut tx_builder = create_tx_builder_with_fee_and_val_size(create_linear_fee(0, 1), 10);
3417
3418 let input = {
3419 let address = ByronAddress::from_base58(
3420 "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
3421 )
3422 .unwrap()
3423 .to_address();
3424 let builder = SingleInputBuilder::new(
3425 TransactionInput::new(genesis_id(), 0),
3426 TransactionOutput::new(address, Value::from(500), None, None),
3427 );
3428 builder.payment_key().unwrap()
3429 };
3430 tx_builder.add_input(input).unwrap();
3431
3432 let output_addr = ByronAddress::from_base58(
3433 "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
3434 )
3435 .unwrap()
3436 .to_address();
3437 let mut output_amount = Value::from(50);
3438 output_amount.multiasset = create_multiasset().0;
3439
3440 assert!(tx_builder
3441 .add_output(
3442 TransactionOutputBuilder::new()
3443 .with_address(output_addr)
3444 .next()
3445 .unwrap()
3446 .with_value(output_amount)
3447 .build()
3448 .unwrap()
3449 )
3450 .is_err());
3451 }
3452
3453 #[test]
3454 fn build_tx_add_change_nfts_not_enough_ada() {
3455 let mut tx_builder = create_tx_builder_with_fee_and_val_size(
3456 create_linear_fee(0, 1),
3457 150, );
3459
3460 let policy_ids = [
3461 PolicyId::from([0u8; 28]),
3462 PolicyId::from([1u8; 28]),
3463 PolicyId::from([2u8; 28]),
3464 ];
3465 let names = [
3466 AssetName::new(vec![99u8; 32]).unwrap(),
3467 AssetName::new(vec![0u8, 1, 2, 3]).unwrap(),
3468 AssetName::new(vec![4u8, 5, 6, 7, 8, 9]).unwrap(),
3469 ];
3470
3471 let multiasset = policy_ids.iter().zip(names.iter()).fold(
3472 MultiAsset::new(),
3473 |mut acc, (policy_id, name)| {
3474 acc.set(*policy_id, name.clone(), 500);
3475 acc
3476 },
3477 );
3478
3479 let mut input_value = Value::from(58);
3480 input_value.multiasset = multiasset;
3481
3482 let input = {
3483 let address = ByronAddress::from_base58(
3484 "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
3485 )
3486 .unwrap()
3487 .to_address();
3488 let builder = SingleInputBuilder::new(
3489 TransactionInput::new(genesis_id(), 0),
3490 TransactionOutput::new(address, input_value, None, None),
3491 );
3492 builder.payment_key().unwrap()
3493 };
3494 tx_builder.add_input(input).unwrap();
3495
3496 let output_addr = ByronAddress::from_base58(
3497 "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
3498 )
3499 .unwrap()
3500 .to_address();
3501 let output_amount = Value::from(208);
3502
3503 tx_builder
3504 .add_output(
3505 TransactionOutputBuilder::new()
3506 .with_address(output_addr)
3507 .next()
3508 .unwrap()
3509 .with_value(output_amount)
3510 .build()
3511 .unwrap(),
3512 )
3513 .unwrap();
3514
3515 let change_addr = ByronAddress::from_base58(
3516 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3517 )
3518 .unwrap()
3519 .to_address();
3520
3521 assert!(tx_builder
3522 .add_change_if_needed_for_tests(&change_addr)
3523 .is_err())
3524 }
3525
3526 fn make_input(input_hash_byte: u8, value: Value) -> InputBuilderResult {
3527 let (_, _, address) = create_account();
3528 SingleInputBuilder::new(
3529 TransactionInput::new(TransactionHash::from([input_hash_byte; 32]), 0),
3530 TransactionOutputBuilder::new()
3531 .with_address(address)
3532 .next()
3533 .unwrap()
3534 .with_value(value)
3535 .build()
3536 .unwrap()
3537 .output,
3538 )
3539 .payment_key()
3540 .unwrap()
3541 }
3542
3543 #[test]
3544 fn tx_builder_cip2_largest_first_increasing_fees() {
3545 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(1, 0));
3547 tx_builder
3548 .add_output(
3549 TransactionOutputBuilder::new()
3550 .with_address(
3551 Address::from_bech32(
3552 "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
3553 )
3554 .unwrap(),
3555 )
3556 .next()
3557 .unwrap()
3558 .with_value(10000)
3559 .build()
3560 .unwrap(),
3561 )
3562 .unwrap();
3563 tx_builder.add_utxo(make_input(0u8, Value::from(1500)));
3564 tx_builder.add_utxo(make_input(1u8, Value::from(2000)));
3565 tx_builder.add_utxo(make_input(2u8, Value::from(8000)));
3566 tx_builder.add_utxo(make_input(3u8, Value::from(4000)));
3567 tx_builder.add_utxo(make_input(4u8, Value::from(1000)));
3568 tx_builder
3569 .select_utxos(CoinSelectionStrategyCIP2::LargestFirst)
3570 .unwrap();
3571 let change_addr = ByronAddress::from_base58(
3572 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3573 )
3574 .unwrap()
3575 .to_address();
3576 let change_added = tx_builder
3577 .add_change_if_needed_for_tests(&change_addr)
3578 .unwrap();
3579 assert!(change_added);
3580 let tx = tx_builder.build_body().unwrap();
3581 assert_eq!(2, tx.outputs.len());
3583 assert_eq!(2, tx.inputs.len());
3584 assert_eq!(2u8, tx.inputs[0].transaction_id.to_raw_bytes()[0]);
3586 assert_eq!(3u8, tx.inputs[1].transaction_id.to_raw_bytes()[0]);
3587 }
3588
3589 #[test]
3590 fn tx_builder_cip2_largest_first_static_fees() {
3591 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
3593 tx_builder
3594 .add_output(
3595 TransactionOutputBuilder::new()
3596 .with_address(
3597 Address::from_bech32(
3598 "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
3599 )
3600 .unwrap(),
3601 )
3602 .next()
3603 .unwrap()
3604 .with_value(1200)
3605 .build()
3606 .unwrap(),
3607 )
3608 .unwrap();
3609 tx_builder.add_utxo(make_input(0u8, Value::from(150)));
3610 tx_builder.add_utxo(make_input(1u8, Value::from(200)));
3611 tx_builder.add_utxo(make_input(2u8, Value::from(800)));
3612 tx_builder.add_utxo(make_input(3u8, Value::from(400)));
3613 tx_builder.add_utxo(make_input(4u8, Value::from(100)));
3614 tx_builder
3615 .select_utxos(CoinSelectionStrategyCIP2::LargestFirst)
3616 .unwrap();
3617 let change_addr = ByronAddress::from_base58(
3618 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3619 )
3620 .unwrap()
3621 .to_address();
3622 let change_added = tx_builder
3623 .add_change_if_needed_for_tests(&change_addr)
3624 .unwrap();
3625 assert!(!change_added);
3626 let tx = tx_builder.build_body().unwrap();
3627 assert_eq!(1, tx.outputs.len());
3629 assert_eq!(2, tx.inputs.len());
3630 assert_eq!(2u8, tx.inputs[0].transaction_id.to_raw_bytes()[0]);
3632 assert_eq!(3u8, tx.inputs[1].transaction_id.to_raw_bytes()[0]);
3633 }
3634
3635 #[test]
3636 fn tx_builder_cip2_largest_first_multiasset() {
3637 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
3639 let pid1 = PolicyId::from([1u8; 28]);
3640 let pid2 = PolicyId::from([2u8; 28]);
3641 let asset_name1 = AssetName::new(vec![1u8; 8]).unwrap();
3642 let asset_name2 = AssetName::new(vec![2u8; 11]).unwrap();
3643 let asset_name3 = AssetName::new(vec![3u8; 9]).unwrap();
3644
3645 let mut output_value = Value::from(415);
3646 let mut output_ma = MultiAsset::new();
3647 output_ma.set(pid1, asset_name1.clone(), 5);
3648 output_ma.set(pid1, asset_name2.clone(), 1);
3649 output_ma.set(pid2, asset_name2.clone(), 2);
3650 output_ma.set(pid2, asset_name3.clone(), 4);
3651 output_value.multiasset = output_ma;
3652 tx_builder
3653 .add_output(SingleOutputBuilderResult::new(TransactionOutput::new(
3654 Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z")
3655 .unwrap(),
3656 output_value.clone(),
3657 None,
3658 None,
3659 )))
3660 .unwrap();
3661
3662 tx_builder.add_utxo(make_input(0u8, Value::from(150)));
3664
3665 let mut ma1 = MultiAsset::new();
3667 ma1.set(pid1, asset_name1.clone(), 10);
3668 ma1.set(pid1, asset_name2.clone(), 1);
3669 ma1.set(pid2, asset_name2.clone(), 2);
3670 let input1 = make_input(1u8, Value::new(200, ma1));
3671 tx_builder.add_utxo(input1);
3672
3673 let mut ma2 = MultiAsset::new();
3675 ma2.set(pid1, asset_name1.clone(), 20);
3676 ma2.set(pid2, asset_name3.clone(), 4);
3677 let input2 = make_input(2u8, Value::new(10, ma2));
3678 tx_builder.add_utxo(input2.clone());
3679
3680 let mut ma3 = MultiAsset::new();
3683 ma3.set(pid2, asset_name1.clone(), 5);
3684 ma3.set(pid1, asset_name2.clone(), 15);
3685 let input3 = make_input(3u8, Value::new(50, ma3));
3686 tx_builder.add_utxo(input3.clone());
3687
3688 let mut ma4 = MultiAsset::new();
3691 ma4.set(pid1, asset_name1.clone(), 10);
3692 ma4.set(pid1, asset_name2.clone(), 10);
3693 let input4 = make_input(4u8, Value::new(10, ma4));
3694 tx_builder.add_utxo(input4);
3695
3696 let mut ma5 = MultiAsset::new();
3698 ma5.set(pid1, asset_name2.clone(), 10);
3699 ma5.set(pid2, asset_name2.clone(), 3);
3700 let input5 = make_input(5u8, Value::new(10, ma5));
3701 tx_builder.add_utxo(input5.clone());
3702
3703 let input6 = make_input(6u8, Value::from(700));
3705 tx_builder.add_utxo(input6.clone());
3706
3707 tx_builder.add_utxo(make_input(7u8, Value::from(100)));
3709 tx_builder
3710 .select_utxos(CoinSelectionStrategyCIP2::LargestFirstMultiAsset)
3711 .unwrap();
3712 let change_addr = ByronAddress::from_base58(
3713 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3714 )
3715 .unwrap()
3716 .to_address();
3717 let change_added = tx_builder
3718 .add_change_if_needed_for_tests(&change_addr)
3719 .unwrap();
3720 assert!(change_added);
3721 let tx = tx_builder.build_body().unwrap();
3722
3723 assert_eq!(2, tx.outputs.len());
3724 assert_eq!(4, tx.inputs.len());
3725 assert_eq!(2u8, tx.inputs[0].transaction_id.to_raw_bytes()[0]);
3727 assert_eq!(3u8, tx.inputs[1].transaction_id.to_raw_bytes()[0]);
3728 assert_eq!(5u8, tx.inputs[2].transaction_id.to_raw_bytes()[0]);
3729 assert_eq!(6u8, tx.inputs[3].transaction_id.to_raw_bytes()[0]);
3730
3731 let change = tx.outputs[1].amount();
3732 assert_eq!(change.coin, 355);
3733 let change_ma = &change.multiasset;
3734 assert_eq!(15, change_ma.get(&pid1, &asset_name1).unwrap());
3735 assert_eq!(24, change_ma.get(&pid1, &asset_name2).unwrap());
3736 assert_eq!(1, change_ma.get(&pid2, &asset_name2).unwrap());
3737 assert_eq!(0, change_ma.get(&pid2, &asset_name3).unwrap_or_default());
3738 let expected_input = input2
3739 .utxo_info
3740 .amount()
3741 .checked_add(input3.utxo_info.amount())
3742 .unwrap()
3743 .checked_add(input5.utxo_info.amount())
3744 .unwrap()
3745 .checked_add(input6.utxo_info.amount())
3746 .unwrap();
3747 let expected_change = expected_input.checked_sub(&output_value).unwrap();
3748 assert_eq!(expected_change, *change);
3749 }
3750
3751 #[test]
3752 #[flaky_test::flaky_test]
3753 fn tx_builder_cip2_random_improve_multiasset() {
3754 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 0));
3755 let pid1 = PolicyId::from([1u8; 28]);
3756 let pid2 = PolicyId::from([2u8; 28]);
3757 let asset_name1 = AssetName::new(vec![1u8; 8]).unwrap();
3758 let asset_name2 = AssetName::new(vec![2u8; 11]).unwrap();
3759 let asset_name3 = AssetName::new(vec![3u8; 9]).unwrap();
3760
3761 let mut output_ma = MultiAsset::new();
3762 output_ma.set(pid1, asset_name1.clone(), 5);
3763 output_ma.set(pid1, asset_name2.clone(), 1);
3764 output_ma.set(pid2, asset_name2.clone(), 2);
3765 output_ma.set(pid2, asset_name3.clone(), 4);
3766 let output_value = Value::new(415, output_ma);
3767 tx_builder
3768 .add_output(SingleOutputBuilderResult::new(TransactionOutput::new(
3769 Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z")
3770 .unwrap(),
3771 output_value.clone(),
3772 None,
3773 None,
3774 )))
3775 .unwrap();
3776
3777 tx_builder.add_utxo(make_input(0u8, Value::from(150)));
3778
3779 let mut ma1 = MultiAsset::new();
3780 ma1.set(pid1, asset_name1.clone(), 10);
3781 ma1.set(pid1, asset_name2.clone(), 1);
3782 ma1.set(pid2, asset_name2.clone(), 2);
3783 let input1 = make_input(1u8, Value::new(200, ma1));
3784 tx_builder.add_utxo(input1);
3785
3786 let mut ma2 = MultiAsset::new();
3787 ma2.set(pid1, asset_name1.clone(), 20);
3788 ma2.set(pid2, asset_name3.clone(), 4);
3789 let input2 = make_input(2u8, Value::new(10, ma2));
3790 tx_builder.add_utxo(input2);
3791
3792 let mut ma3 = MultiAsset::new();
3793 ma3.set(pid2, asset_name1.clone(), 5);
3794 ma3.set(pid1, asset_name2.clone(), 15);
3795 let input3 = make_input(3u8, Value::new(50, ma3));
3796 tx_builder.add_utxo(input3);
3797
3798 let mut ma4 = MultiAsset::new();
3799 ma4.set(pid1, asset_name1, 10);
3800 ma4.set(pid1, asset_name2.clone(), 10);
3801 let input4 = make_input(4u8, Value::new(10, ma4));
3802 tx_builder.add_utxo(input4);
3803
3804 let mut ma5 = MultiAsset::new();
3805 ma5.set(pid1, asset_name2.clone(), 10);
3806 ma5.set(pid2, asset_name2.clone(), 3);
3807 let input5 = make_input(5u8, Value::new(10, ma5));
3808 tx_builder.add_utxo(input5);
3809
3810 let input6 = make_input(6u8, Value::from(400));
3811 tx_builder.add_utxo(input6);
3812 tx_builder.add_utxo(make_input(7u8, Value::from(100)));
3813
3814 let mut ma8 = MultiAsset::new();
3815 ma8.set(pid2, asset_name2, 10);
3816 let input8 = make_input(8u8, Value::new(10, ma8));
3817 tx_builder.add_utxo(input8);
3818
3819 let mut ma9 = MultiAsset::new();
3820 ma9.set(pid2, asset_name3, 10);
3821 let input9 = make_input(9u8, Value::new(10, ma9));
3822 tx_builder.add_utxo(input9);
3823
3824 tx_builder
3825 .select_utxos(CoinSelectionStrategyCIP2::RandomImproveMultiAsset)
3826 .unwrap();
3827 let change_addr = ByronAddress::from_base58(
3828 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3829 )
3830 .unwrap()
3831 .to_address();
3832 let change_added = tx_builder
3833 .add_change_if_needed_for_tests(&change_addr)
3834 .unwrap();
3835 assert!(change_added);
3836 let tx = tx_builder.build_body().unwrap();
3837
3838 assert_eq!(2, tx.outputs.len());
3839
3840 let input_total = tx_builder.get_explicit_input().unwrap();
3841 assert!(input_total >= output_value);
3842 }
3843
3844 #[test]
3845 #[flaky_test::flaky_test]
3846 fn tx_builder_cip2_random_improve() {
3847 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(1, 0));
3849 const COST: u64 = 10000;
3850 tx_builder
3851 .add_output(
3852 TransactionOutputBuilder::new()
3853 .with_address(
3854 Address::from_bech32(
3855 "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
3856 )
3857 .unwrap(),
3858 )
3859 .next()
3860 .unwrap()
3861 .with_value(COST)
3862 .build()
3863 .unwrap(),
3864 )
3865 .unwrap();
3866 tx_builder.utxos.push(make_input(0u8, Value::from(1500)));
3867 tx_builder.utxos.push(make_input(1u8, Value::from(2000)));
3868 tx_builder.utxos.push(make_input(2u8, Value::from(8000)));
3869 tx_builder.utxos.push(make_input(3u8, Value::from(4000)));
3870 tx_builder.utxos.push(make_input(4u8, Value::from(1000)));
3871 tx_builder.utxos.push(make_input(5u8, Value::from(2000)));
3872 tx_builder.utxos.push(make_input(6u8, Value::from(1500)));
3873 let add_inputs_res = tx_builder.select_utxos(CoinSelectionStrategyCIP2::RandomImprove);
3874 assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err());
3875 let change_addr = ByronAddress::from_base58(
3876 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
3877 )
3878 .unwrap()
3879 .to_address();
3880 let add_change_res = tx_builder.add_change_if_needed_for_tests(&change_addr);
3881 assert!(add_change_res.is_ok(), "{:?}", add_change_res.err());
3882 let tx_build_res = tx_builder.build_body();
3883 assert!(tx_build_res.is_ok(), "{:?}", tx_build_res.err());
3884 let tx = tx_build_res.unwrap();
3885 let mut input_values = BTreeMap::new();
3887 for utxo in tx_builder.utxos.iter() {
3888 input_values.insert(utxo.input.transaction_id, utxo.utxo_info.amount().clone());
3889 }
3890 let mut encountered = std::collections::HashSet::new();
3891 let mut input_total = Value::from(Coin::zero());
3892 for input in tx.inputs.iter() {
3893 let txid = &input.transaction_id;
3894 if !encountered.insert(*txid) {
3895 panic!("Input {:?} duplicated", txid);
3896 }
3897 let value = input_values.get(txid).unwrap();
3898 input_total = input_total.checked_add(value).unwrap();
3899 }
3900 assert!(
3901 input_total
3902 >= Value::from(
3903 tx_builder
3904 .min_fee(false)
3905 .unwrap()
3906 .checked_add(COST)
3907 .unwrap()
3908 )
3909 );
3910 }
3911
3912 #[test]
3913 #[flaky_test::flaky_test]
3914 fn tx_builder_cip2_random_improve_exclude_used_indices() {
3915 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(44, 155381));
3916 const COST: u64 = 1000000;
3917 tx_builder
3918 .add_output(
3919 TransactionOutputBuilder::new()
3920 .with_address(
3921 Address::from_bech32(
3922 "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
3923 )
3924 .unwrap(),
3925 )
3926 .next()
3927 .unwrap()
3928 .with_value(COST)
3929 .build()
3930 .unwrap(),
3931 )
3932 .unwrap();
3933 tx_builder.add_utxo(make_input(0u8, Value::from(1000000)));
3934 tx_builder.add_utxo(make_input(1u8, Value::from(10000000)));
3935 let mut input_total = tx_builder.get_total_input().unwrap();
3936 let mut output_total = tx_builder
3937 .get_explicit_output()
3938 .unwrap()
3939 .checked_add(&Value::from(tx_builder.get_deposit().unwrap()))
3940 .unwrap()
3941 .checked_add(&Value::from(tx_builder.min_fee(false).unwrap()))
3942 .unwrap();
3943 let available_inputs = tx_builder.utxos.clone();
3944 let mut available_indices: BTreeSet<usize> = (0..available_inputs.len()).collect();
3945 assert!(available_indices.len() == 2);
3946 use rand::SeedableRng;
3947 let mut rng = rand_chacha::ChaChaRng::seed_from_u64(1);
3948 tx_builder
3949 .cip2_random_improve_by(
3950 &available_inputs,
3951 &mut available_indices,
3952 &mut input_total,
3953 &mut output_total,
3954 |value| Some(value.coin),
3955 &mut rng,
3956 )
3957 .unwrap();
3958 assert!(!available_indices.contains(&0));
3959 assert!(available_indices.contains(&1));
3960 assert!(available_indices.len() < 2);
3961 }
3962
3963 #[test]
3964 #[flaky_test::flaky_test]
3965 fn tx_builder_cip2_random_improve_when_using_all_available_inputs() {
3966 let linear_fee = LinearFee::new(1, 0, 0);
3968 let cfg = TransactionBuilderConfigBuilder::default()
3969 .fee_algo(linear_fee)
3970 .pool_deposit(0)
3971 .key_deposit(0)
3972 .max_value_size(9999)
3973 .max_tx_size(9999)
3974 .coins_per_utxo_byte(Coin::zero())
3975 .ex_unit_prices(ExUnitPrices::new(
3976 SubCoin::new(u64::zero(), u64::zero()),
3977 SubCoin::new(u64::zero(), u64::zero()),
3978 ))
3979 .collateral_percentage(150)
3980 .max_collateral_inputs(3)
3981 .build()
3982 .unwrap();
3983 let mut tx_builder = TransactionBuilder::new(cfg);
3984 const COST: u64 = 1000;
3985 tx_builder
3986 .add_output(
3987 TransactionOutputBuilder::new()
3988 .with_address(
3989 Address::from_bech32(
3990 "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
3991 )
3992 .unwrap(),
3993 )
3994 .next()
3995 .unwrap()
3996 .with_value(COST)
3997 .build()
3998 .unwrap(),
3999 )
4000 .unwrap();
4001 tx_builder.add_utxo(make_input(1u8, Value::from(800)));
4002 tx_builder.add_utxo(make_input(2u8, Value::from(800)));
4003 let add_inputs_res = tx_builder.select_utxos(CoinSelectionStrategyCIP2::RandomImprove);
4004 assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err());
4005 }
4006
4007 #[test]
4008 #[flaky_test::flaky_test]
4009 fn tx_builder_cip2_random_improve_adds_enough_for_fees() {
4010 let linear_fee = LinearFee::new(1, 0, 0);
4012 let cfg = TransactionBuilderConfigBuilder::default()
4013 .fee_algo(linear_fee)
4014 .pool_deposit(0)
4015 .key_deposit(0)
4016 .max_value_size(9999)
4017 .max_tx_size(9999)
4018 .coins_per_utxo_byte(Coin::zero())
4019 .ex_unit_prices(ExUnitPrices::new(SubCoin::new(0, 0), SubCoin::new(0, 0)))
4020 .collateral_percentage(150)
4021 .max_collateral_inputs(3)
4022 .build()
4023 .unwrap();
4024 let mut tx_builder = TransactionBuilder::new(cfg);
4025 const COST: u64 = 100;
4026 tx_builder
4027 .add_output(
4028 TransactionOutputBuilder::new()
4029 .with_address(
4030 Address::from_bech32(
4031 "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z",
4032 )
4033 .unwrap(),
4034 )
4035 .next()
4036 .unwrap()
4037 .with_value(COST)
4038 .build()
4039 .unwrap(),
4040 )
4041 .unwrap();
4042 assert_eq!(tx_builder.min_fee(false).unwrap(), 56);
4043 tx_builder.add_utxo(make_input(1u8, Value::from(150)));
4044 tx_builder.add_utxo(make_input(2u8, Value::from(150)));
4045 tx_builder.add_utxo(make_input(3u8, Value::from(150)));
4046 let add_inputs_res = tx_builder.select_utxos(CoinSelectionStrategyCIP2::RandomImprove);
4047 assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err());
4048 assert_eq!(tx_builder.min_fee(false).unwrap(), 270);
4049 let change_addr = ByronAddress::from_base58(
4050 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
4051 )
4052 .unwrap()
4053 .to_address();
4054 let add_change_res = tx_builder.add_change_if_needed_for_tests(&change_addr);
4055 assert!(add_change_res.is_ok(), "{:?}", add_change_res.err());
4056 }
4057
4058 #[test]
4059 fn build_tx_pay_to_multisig() {
4060 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(10, 2));
4061 let (_, _, addr_net_0) = create_account();
4062
4063 let input = {
4064 SingleInputBuilder::new(
4065 TransactionInput::new(genesis_id(), 0),
4066 TransactionOutput::new(addr_net_0.clone(), Value::from(1_000_000), None, None),
4067 )
4068 .payment_key()
4069 .unwrap()
4070 };
4071 tx_builder.add_input(input).unwrap();
4072 tx_builder
4073 .add_output(
4074 TransactionOutputBuilder::new()
4075 .with_address(addr_net_0)
4076 .next()
4077 .unwrap()
4078 .with_value(999_000)
4079 .build()
4080 .unwrap(),
4081 )
4082 .unwrap();
4083 tx_builder.set_ttl(1000);
4084 tx_builder.set_fee(1_000);
4085
4086 assert_eq!(tx_builder.outputs.len(), 1);
4087 assert_eq!(
4088 tx_builder
4089 .get_explicit_input()
4090 .unwrap()
4091 .checked_add(&tx_builder.get_implicit_input().unwrap())
4092 .unwrap(),
4093 tx_builder
4094 .get_explicit_output()
4095 .unwrap()
4096 .checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
4097 .unwrap()
4098 );
4099
4100 let final_tx = tx_builder.build_body().unwrap();
4101 let deser_t = TransactionBody::from_cbor_bytes(&final_tx.to_cbor_bytes()).unwrap();
4102
4103 assert_eq!(deser_t.to_cbor_bytes(), final_tx.to_cbor_bytes());
4104 }
4105
4106 #[test]
4107 fn build_tx_multisig_spend_1on1_unsigned() {
4108 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(10, 2));
4109
4110 let ((spend, _), (_, stake_cred), addr_multisig) = create_account();
4111 let change_key = root_key_15()
4112 .derive(harden(1852))
4113 .derive(harden(1815))
4114 .derive(harden(0))
4115 .derive(1)
4116 .derive(0)
4117 .to_public();
4118 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
4119 let addr_output =
4120 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
4121 .to_address();
4122
4123 let input = {
4124 SingleInputBuilder::new(
4125 TransactionInput::new(genesis_id(), 0),
4126 TransactionOutput::new(addr_multisig, Value::from(1_000_000), None, None),
4127 )
4128 .payment_key()
4129 .unwrap()
4130 };
4131 tx_builder.add_input(input).unwrap();
4132
4133 tx_builder
4134 .add_output(
4135 TransactionOutputBuilder::new()
4136 .with_address(addr_output)
4137 .next()
4138 .unwrap()
4139 .with_value(999_000)
4140 .build()
4141 .unwrap(),
4142 )
4143 .unwrap();
4144 tx_builder.set_ttl(1000);
4145 tx_builder.set_fee(1_000);
4146
4147 let mut auxiliary_data = AuxiliaryData::new();
4148 let mut pubkey_native_scripts = Vec::new();
4149 let mut oneof_native_scripts = Vec::new();
4150
4151 let spending_hash = spend.to_raw_key().hash();
4152 pubkey_native_scripts.push(NativeScript::new_script_pubkey(spending_hash));
4153 oneof_native_scripts.push(NativeScript::new_script_n_of_k(1, pubkey_native_scripts));
4154 auxiliary_data.add_native_scripts(oneof_native_scripts);
4155 tx_builder.add_auxiliary_data(auxiliary_data.clone());
4156
4157 assert_eq!(tx_builder.outputs.len(), 1);
4158 assert_eq!(
4159 tx_builder
4160 .get_explicit_input()
4161 .unwrap()
4162 .checked_add(&tx_builder.get_implicit_input().unwrap())
4163 .unwrap(),
4164 tx_builder
4165 .get_explicit_output()
4166 .unwrap()
4167 .checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
4168 .unwrap()
4169 );
4170
4171 let final_tx = tx_builder.build_body().unwrap();
4172 let deser_t = TransactionBody::from_cbor_bytes(&final_tx.to_cbor_bytes()).unwrap();
4173
4174 assert_eq!(deser_t.to_cbor_bytes(), final_tx.to_cbor_bytes());
4175 assert_eq!(
4176 deser_t.auxiliary_data_hash.unwrap(),
4177 hash_auxiliary_data(&auxiliary_data)
4178 );
4179 }
4180
4181 #[test]
4182 fn build_tx_multisig_1on1_signed() {
4183 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(10, 2));
4184 let spend = root_key_15()
4185 .derive(harden(1854)) .derive(harden(1815))
4187 .derive(harden(0))
4188 .derive(0)
4189 .derive(0)
4190 .to_public();
4191 let stake = root_key_15()
4192 .derive(harden(1854)) .derive(harden(1815))
4194 .derive(harden(0))
4195 .derive(2)
4196 .derive(0)
4197 .to_public();
4198
4199 let spend_cred = StakeCredential::new_pub_key(spend.to_raw_key().hash());
4200 let stake_cred = StakeCredential::new_pub_key(stake.to_raw_key().hash());
4201 let addr_net_0 =
4202 BaseAddress::new(NetworkInfo::testnet().network_id(), spend_cred, stake_cred)
4203 .to_address();
4204 let input = {
4205 SingleInputBuilder::new(
4206 TransactionInput::new(genesis_id(), 0),
4207 TransactionOutput::new(addr_net_0.clone(), Value::from(1_000_000), None, None),
4208 )
4209 .payment_key()
4210 .unwrap()
4211 };
4212 tx_builder.add_input(input).unwrap();
4213 tx_builder
4214 .add_output(
4215 TransactionOutputBuilder::new()
4216 .with_address(addr_net_0)
4217 .next()
4218 .unwrap()
4219 .with_value(999_000)
4220 .build()
4221 .unwrap(),
4222 )
4223 .unwrap();
4224 tx_builder.set_ttl(1000);
4225 tx_builder.set_fee(1_000);
4226
4227 let mut auxiliary_data = AuxiliaryData::new();
4228 let mut pubkey_native_scripts = Vec::new();
4229 let mut oneof_native_scripts = Vec::new();
4230
4231 let spending_hash = spend.to_raw_key().hash();
4232 pubkey_native_scripts.push(NativeScript::new_script_pubkey(spending_hash));
4233 oneof_native_scripts.push(NativeScript::new_script_n_of_k(1, pubkey_native_scripts));
4234 auxiliary_data.add_native_scripts(oneof_native_scripts);
4235 tx_builder.add_auxiliary_data(auxiliary_data.clone());
4236
4237 let body = tx_builder.build_body().unwrap();
4238
4239 assert_eq!(tx_builder.outputs.len(), 1);
4240 assert_eq!(
4241 tx_builder
4242 .get_explicit_input()
4243 .unwrap()
4244 .checked_add(&tx_builder.get_implicit_input().unwrap())
4245 .unwrap(),
4246 tx_builder
4247 .get_explicit_output()
4248 .unwrap()
4249 .checked_add(&Value::from(tx_builder.get_fee_if_set().unwrap()))
4250 .unwrap()
4251 );
4252
4253 let mut witness_set = TransactionWitnessSet::new();
4254
4255 witness_set.vkeywitnesses = Some(
4256 vec![make_vkey_witness(
4257 &hash_transaction(&body),
4258 &PrivateKey::from_normal_bytes(
4259 &hex::decode(
4260 "c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a",
4261 )
4262 .unwrap(),
4263 )
4264 .unwrap(),
4265 )]
4266 .into(),
4267 );
4268
4269 let final_tx = Transaction::new(body, witness_set, true, None);
4270 let deser_t = Transaction::from_cbor_bytes(&final_tx.to_cbor_bytes()).unwrap();
4271 assert_eq!(deser_t.to_cbor_bytes(), final_tx.to_cbor_bytes());
4272 assert_eq!(
4273 deser_t.body.auxiliary_data_hash.unwrap(),
4274 hash_auxiliary_data(&auxiliary_data)
4275 );
4276 }
4277
4278 #[test]
4279 fn add_change_splits_change_into_multiple_outputs_when_nfts_overflow_output_size() {
4280 let linear_fee = LinearFee::new(0, 1, 0);
4281 let max_value_size = 100; let mut tx_builder = TransactionBuilder::new(
4283 TransactionBuilderConfigBuilder::default()
4284 .fee_algo(linear_fee)
4285 .pool_deposit(0)
4286 .key_deposit(0)
4287 .max_value_size(max_value_size)
4288 .max_tx_size(MAX_TX_SIZE)
4289 .coins_per_utxo_byte(1)
4290 .ex_unit_prices(ExUnitPrices::new(SubCoin::new(0, 0), SubCoin::new(0, 0)))
4291 .collateral_percentage(150)
4292 .max_collateral_inputs(3)
4293 .prefer_pure_change(true)
4294 .build()
4295 .unwrap(),
4296 );
4297
4298 let policy_id = PolicyId::from([0u8; 28]);
4299 let names = [
4300 AssetName::new(vec![0u8, 1, 2, 3]).unwrap(),
4301 AssetName::new(vec![4u8, 5, 6, 7]).unwrap(),
4302 AssetName::new(vec![5u8, 5, 6, 7]).unwrap(),
4303 AssetName::new(vec![6u8, 5, 6, 7]).unwrap(),
4304 AssetName::new(vec![99u8; 32]).unwrap(),
4305 ];
4306 let mut multiasset = MultiAsset::new();
4307 for name in names.iter() {
4308 multiasset.set(policy_id, name.clone(), 500);
4309 }
4310
4311 let input_value = Value::new(1300, multiasset);
4312
4313 let input = {
4314 let builder = SingleInputBuilder::new(
4315 TransactionInput::new(genesis_id(), 0),
4316 TransactionOutput::new(
4317 ByronAddress::from_base58(
4318 "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3",
4319 )
4320 .unwrap()
4321 .to_address(),
4322 input_value,
4323 None,
4324 None,
4325 ),
4326 );
4327 builder.payment_key().unwrap()
4328 };
4329 tx_builder.add_input(input).unwrap();
4330
4331 let output_addr = ByronAddress::from_base58(
4332 "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b",
4333 )
4334 .unwrap()
4335 .to_address();
4336 let output_amount = Value::from(208);
4337
4338 tx_builder
4339 .add_output(
4340 TransactionOutputBuilder::new()
4341 .with_address(output_addr)
4342 .next()
4343 .unwrap()
4344 .with_value(output_amount)
4345 .build()
4346 .unwrap(),
4347 )
4348 .unwrap();
4349
4350 let change_addr = ByronAddress::from_base58(
4351 "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho",
4352 )
4353 .unwrap()
4354 .to_address();
4355
4356 let add_change_result = tx_builder.add_change_if_needed_for_tests(&change_addr);
4357 assert!(add_change_result.is_ok());
4358 assert_eq!(tx_builder.outputs.len(), 4);
4359
4360 let change1 = &tx_builder.outputs[1];
4361 let change2 = &tx_builder.outputs[2];
4362 let change3 = &tx_builder.outputs[3];
4363
4364 assert_eq!(*change1.address(), change_addr);
4365 assert_eq!(change1.address(), change2.address());
4366 assert_eq!(change1.address(), change3.address());
4367
4368 assert_eq!(change1.amount().coin, 274);
4369 assert_eq!(change2.amount().coin, 279);
4370 assert_eq!(change3.amount().coin, 538);
4371
4372 assert!(change1.amount().has_multiassets());
4373 assert!(change2.amount().has_multiassets());
4374 assert!(!change3.amount().has_multiassets()); let masset1 = &change1.amount().multiasset;
4377 let masset2 = &change2.amount().multiasset;
4378
4379 assert_eq!(masset1.keys().len(), 1);
4380 assert_eq!(
4381 masset1.keys().collect::<Vec<_>>(),
4382 masset2.keys().collect::<Vec<_>>()
4383 );
4384
4385 let asset1 = masset1.deref().get(&policy_id).unwrap();
4386 let asset2 = masset2.deref().get(&policy_id).unwrap();
4387 assert_eq!(asset1.len(), 4);
4388 assert_eq!(asset2.len(), 1);
4389
4390 names.iter().for_each(|name| {
4391 let v1 = asset1.get(name);
4392 let v2 = asset2.get(name);
4393 assert_ne!(v1.is_some(), v2.is_some());
4394 assert_eq!(*v1.or(v2).unwrap(), 500);
4395 });
4396 }
4397
4398 fn create_metadatum() -> TransactionMetadatum {
4399 let mut entries = MetadatumMap::new();
4400 entries.set(
4401 TransactionMetadatum::new_text("qwe".into()).unwrap(),
4402 TransactionMetadatum::new_int(123i64.into()),
4403 );
4404 TransactionMetadatum::new_map(entries)
4405 }
4406
4407 fn create_general_metadata(metadatum_key: TransactionMetadatumLabel) -> Metadata {
4408 let mut metadata = Metadata::new();
4409 metadata.set(metadatum_key, create_metadatum());
4410 metadata
4411 }
4412
4413 fn create_aux_with_metadata(metadatum_key: TransactionMetadatumLabel) -> AuxiliaryData {
4414 let metadata = create_general_metadata(metadatum_key);
4415
4416 let mut aux = AuxiliaryData::new_shelley(metadata);
4417
4418 aux.add_native_scripts(vec![NativeScript::new_script_invalid_before(123)]);
4419
4420 aux
4421 }
4422
4423 fn assert_json_metadatum(dat: &TransactionMetadatum) {
4424 match dat {
4425 TransactionMetadatum::Map(map) => {
4426 assert_eq!(map.len(), 1);
4427 let key = TransactionMetadatum::new_text(String::from("qwe")).unwrap();
4428 let val = map.get(&key).unwrap();
4429 match val {
4430 TransactionMetadatum::Int(x) => assert_eq!(*x, 123u64.into()),
4431 _ => panic!(),
4432 }
4433 }
4434 _ => panic!(),
4435 }
4436 }
4437
4438 #[test]
4439 fn set_metadata_with_empty_auxiliary() {
4440 let mut tx_builder = create_default_tx_builder();
4441
4442 let num = 42;
4443 {
4444 let mut aux_data = AuxiliaryData::new();
4445 aux_data.metadata_mut().set(num, create_metadatum());
4446 tx_builder.add_auxiliary_data(aux_data);
4447 }
4448
4449 assert!(tx_builder.auxiliary_data.is_some());
4450
4451 let aux = tx_builder.auxiliary_data.unwrap();
4452 assert!(aux.metadata().is_some());
4453 assert!(aux.native_scripts().is_none());
4454 assert!(aux.plutus_v1_scripts().is_none());
4455 assert!(aux.plutus_v2_scripts().is_none());
4456
4457 let met = aux.metadata().unwrap();
4458
4459 assert_eq!(met.len(), 1);
4460 assert_json_metadatum(met.get(num).unwrap());
4461 }
4462
4463 #[test]
4464 fn set_metadata_with_existing_auxiliary() {
4465 let mut tx_builder = create_default_tx_builder();
4466
4467 let num1 = 42;
4468 tx_builder.add_auxiliary_data(create_aux_with_metadata(num1));
4469
4470 let num2 = 84;
4471 {
4472 let mut aux_data = AuxiliaryData::new();
4473 aux_data.metadata_mut().set(num2, create_metadatum());
4474 tx_builder.set_auxiliary_data(aux_data);
4475 }
4476
4477 let aux = tx_builder.auxiliary_data.unwrap();
4478 assert!(aux.metadata().is_some());
4479 assert!(aux.native_scripts().is_none());
4480 assert!(aux.plutus_v1_scripts().is_none());
4481 assert!(aux.plutus_v2_scripts().is_none());
4482
4483 let met = aux.metadata().unwrap();
4484 assert_eq!(met.len(), 1);
4485 assert!(met.get(num1).is_none());
4486 assert_json_metadatum(met.get(num2).unwrap());
4487 }
4488
4489 #[test]
4490 fn add_metadatum_with_empty_auxiliary() {
4491 let mut tx_builder = create_default_tx_builder();
4492
4493 let num = 42;
4494 {
4495 let mut aux_data = AuxiliaryData::new();
4496 aux_data.metadata_mut().set(num, create_metadatum());
4497 tx_builder.add_auxiliary_data(aux_data);
4498 }
4499
4500 assert!(tx_builder.auxiliary_data.is_some());
4501
4502 let aux = tx_builder.auxiliary_data.unwrap();
4503 assert!(aux.metadata().is_some());
4504 assert!(aux.native_scripts().is_none());
4505 assert!(aux.plutus_v1_scripts().is_none());
4506 assert!(aux.plutus_v2_scripts().is_none());
4507
4508 let met = aux.metadata().unwrap();
4509
4510 assert_eq!(met.len(), 1);
4511 assert_json_metadatum(met.get(num).unwrap());
4512 }
4513
4514 #[test]
4515 fn add_metadatum_with_existing_auxiliary() {
4516 let mut tx_builder = create_default_tx_builder();
4517
4518 let num1 = 42;
4519 tx_builder.add_auxiliary_data(create_aux_with_metadata(num1));
4520
4521 let num2 = 84;
4522 tx_builder.add_auxiliary_data(create_aux_with_metadata(num2));
4523
4524 let aux = tx_builder.auxiliary_data.unwrap();
4525 assert!(aux.metadata().is_some());
4526 assert!(aux.native_scripts().is_some());
4527 assert!(aux.plutus_v1_scripts().is_none());
4528 assert!(aux.plutus_v2_scripts().is_none());
4529
4530 let met = aux.metadata().unwrap();
4531 assert_eq!(met.len(), 2);
4532 assert_json_metadatum(met.get(num1).unwrap());
4533 assert_json_metadatum(met.get(num2).unwrap());
4534 }
4535
4536 #[test]
4537 fn add_json_metadatum_with_empty_auxiliary() {
4538 let mut tx_builder = create_default_tx_builder();
4539
4540 let num = 42;
4541 tx_builder.add_auxiliary_data(AuxiliaryData::new_shelley(create_general_metadata(num)));
4542
4543 assert!(tx_builder.auxiliary_data.is_some());
4544
4545 let aux = tx_builder.auxiliary_data.unwrap();
4546 assert!(aux.metadata().is_some());
4547 assert!(aux.native_scripts().is_none());
4548 assert!(aux.plutus_v1_scripts().is_none());
4549 assert!(aux.plutus_v2_scripts().is_none());
4550
4551 let met = aux.metadata().unwrap();
4552
4553 assert_eq!(met.len(), 1);
4554 assert_json_metadatum(met.get(num).unwrap());
4555 }
4556
4557 #[test]
4558 fn add_json_metadatum_with_existing_auxiliary() {
4559 let mut tx_builder = create_default_tx_builder();
4560
4561 let num1 = 42;
4562 tx_builder.add_auxiliary_data(create_aux_with_metadata(num1));
4563
4564 let num2 = 84;
4565 tx_builder.add_auxiliary_data(create_aux_with_metadata(num2));
4566
4567 let aux = tx_builder.auxiliary_data.unwrap();
4568 assert!(aux.metadata().is_some());
4569 assert!(aux.native_scripts().is_some());
4570 assert!(aux.plutus_v1_scripts().is_none());
4571 assert!(aux.plutus_v2_scripts().is_none());
4572
4573 let met = aux.metadata().unwrap();
4574 assert_eq!(met.len(), 2);
4575 assert_json_metadatum(met.get(num1).unwrap());
4576 assert_json_metadatum(met.get(num2).unwrap());
4577 }
4578
4579 #[test]
4580 fn add_metadata_with_empty_auxiliary() {
4581 let mut tx_builder = create_default_tx_builder();
4582
4583 let key = 42;
4584 let value = TransactionMetadatum::new_text("Hello World".to_string()).unwrap();
4585 {
4586 let mut aux_data = AuxiliaryData::new();
4587 aux_data.metadata_mut().set(key, value.clone());
4588 tx_builder.add_auxiliary_data(aux_data);
4589 }
4590
4591 let aux = tx_builder.auxiliary_data.unwrap();
4592 assert!(aux.metadata().is_some());
4593 assert!(aux.native_scripts().is_none());
4594 assert!(aux.plutus_v1_scripts().is_none());
4595 assert!(aux.plutus_v2_scripts().is_none());
4596
4597 let met = aux.metadata().unwrap();
4598 assert_eq!(met.len(), 1);
4599 assert_eq!(*met.get(key).unwrap(), value);
4600 }
4601
4602 #[test]
4603 fn add_json_metadata_with_empty_auxiliary() {
4604 let mut tx_builder = create_default_tx_builder();
4605
4606 let key = 42;
4607 tx_builder.add_auxiliary_data(AuxiliaryData::new_shelley(create_general_metadata(key)));
4608
4609 let aux = tx_builder.auxiliary_data.unwrap();
4610 assert!(aux.metadata().is_some());
4611 assert!(aux.native_scripts().is_none());
4612 assert!(aux.plutus_v1_scripts().is_none());
4613 assert!(aux.plutus_v2_scripts().is_none());
4614
4615 let met = aux.metadata().unwrap();
4616 assert_eq!(met.len(), 1);
4617 assert_json_metadatum(met.get(key).unwrap());
4618 }
4619
4620 #[test]
4621 fn add_metadata_with_existing_auxiliary() {
4622 let mut tx_builder = create_default_tx_builder();
4623
4624 let key1 = 42;
4625 tx_builder.add_auxiliary_data(create_aux_with_metadata(key1));
4626
4627 let key2 = 84;
4628 let val2 = TransactionMetadatum::new_text("Hello World".to_string()).unwrap();
4629 {
4630 let mut aux_data = AuxiliaryData::new();
4631 aux_data.metadata_mut().set(key2, val2.clone());
4632 tx_builder.add_auxiliary_data(aux_data);
4633 }
4634
4635 let aux = tx_builder.auxiliary_data.unwrap();
4636 assert!(aux.metadata().is_some());
4637 assert!(aux.native_scripts().is_some());
4638 assert!(aux.plutus_v1_scripts().is_none());
4639 assert!(aux.plutus_v2_scripts().is_none());
4640
4641 let met = aux.metadata().unwrap();
4642 assert_eq!(met.entries.len(), 2);
4643 assert_json_metadatum(met.get(key1).unwrap());
4644 assert_eq!(*met.get(key2).unwrap(), val2);
4645 }
4646
4647 #[test]
4648 fn add_json_metadata_with_existing_auxiliary() {
4649 let mut tx_builder = create_default_tx_builder();
4650
4651 let key1 = 42;
4652 tx_builder.add_auxiliary_data(create_aux_with_metadata(key1));
4653
4654 let key2 = 84;
4655 tx_builder.add_auxiliary_data(create_aux_with_metadata(key2));
4656
4657 let aux = tx_builder.auxiliary_data.unwrap();
4658 assert!(aux.metadata().is_some());
4659 assert!(aux.native_scripts().is_some());
4660 assert!(aux.plutus_v1_scripts().is_none());
4661 assert!(aux.plutus_v2_scripts().is_none());
4662
4663 let met = aux.metadata().unwrap();
4664 assert_eq!(met.entries.len(), 2);
4665 assert_json_metadatum(met.get(key1).unwrap());
4666 assert_json_metadatum(met.get(key2).unwrap());
4667 }
4668
4669 fn create_asset_name() -> AssetName {
4670 AssetName::new(vec![0u8, 1, 2, 3]).unwrap()
4671 }
4672
4673 fn create_mint_asset_builder() -> SingleMintBuilder {
4674 SingleMintBuilder::new_single_asset(create_asset_name(), 1234)
4675 }
4676
4677 fn create_multiasset_one_asset(policy_id: &PolicyId) -> MultiAsset {
4678 let mut mint = MultiAsset::default();
4679 mint.set(*policy_id, create_asset_name(), 1234);
4680 mint
4681 }
4682
4683 fn assert_mint_asset(mint: &Mint, policy_id: &PolicyId) {
4684 let result_asset = mint.deref().get(policy_id).unwrap();
4685 assert_eq!(result_asset.len(), 1);
4686 assert_eq!(
4687 *result_asset.deref().get(&create_asset_name()).unwrap(),
4688 1234
4689 );
4690 }
4691
4692 fn mint_script_and_policy_and_hash(x: u8) -> (NativeScript, PolicyId, Ed25519KeyHash) {
4693 let hash = fake_key_hash(x);
4694 let mint_script = NativeScript::new_script_pubkey(hash);
4695 let policy_id = mint_script.hash();
4696 (mint_script, policy_id, hash)
4697 }
4698
4699 fn mint_script_and_policy(x: u8) -> (NativeScript, PolicyId) {
4700 let (m, p, _) = mint_script_and_policy_and_hash(x);
4701 (m, p)
4702 }
4703
4704 #[test]
4705 fn set_mint_asset_with_empty_mint() {
4706 let mut tx_builder = create_default_tx_builder();
4707
4708 let (mint_script, policy_id) = mint_script_and_policy(0);
4709
4710 let result = create_mint_asset_builder().native_script(
4711 mint_script,
4712 NativeScriptWitnessInfo::assume_signature_count(),
4713 );
4714
4715 tx_builder.add_mint(result).unwrap();
4716
4717 assert!(tx_builder.mint.is_some());
4718
4719 let mint = tx_builder.mint.unwrap();
4720
4721 assert_eq!(mint.len(), 1);
4722 assert_mint_asset(&mint, &policy_id);
4723 }
4724
4725 #[test]
4726 fn set_mint_asset_with_existing_mint() {
4727 let mut tx_builder = create_default_tx_builder();
4728
4729 let (mint_script1, policy_id1) = mint_script_and_policy(0);
4730 let (mint_script2, policy_id2) = mint_script_and_policy(1);
4731
4732 let result = create_mint_asset_builder().native_script(
4733 mint_script1,
4734 NativeScriptWitnessInfo::assume_signature_count(),
4735 );
4736
4737 tx_builder.add_mint(result).unwrap();
4738
4739 let result = create_mint_asset_builder().native_script(
4740 mint_script2,
4741 NativeScriptWitnessInfo::assume_signature_count(),
4742 );
4743
4744 tx_builder.add_mint(result).unwrap();
4745
4746 assert!(tx_builder.mint.is_some());
4747
4748 let mint = tx_builder.mint.unwrap();
4749
4750 assert_eq!(mint.len(), 2);
4751 assert_mint_asset(&mint, &policy_id1);
4752 assert_mint_asset(&mint, &policy_id2);
4753 }
4754
4755 #[test]
4756 fn add_mint_asset_with_empty_mint() {
4757 let mut tx_builder = create_default_tx_builder();
4758
4759 let (mint_script, policy_id) = mint_script_and_policy(0);
4760
4761 let result = create_mint_asset_builder().native_script(
4762 mint_script,
4763 NativeScriptWitnessInfo::assume_signature_count(),
4764 );
4765
4766 tx_builder.add_mint(result).unwrap();
4767
4768 assert!(tx_builder.mint.is_some());
4769
4770 let mint = tx_builder.mint.unwrap();
4771
4772 assert_eq!(mint.len(), 1);
4773 assert_mint_asset(&mint, &policy_id);
4774 }
4775
4776 #[test]
4777 fn add_mint_asset_with_existing_mint() {
4778 let mut tx_builder = create_default_tx_builder();
4779
4780 let (mint_script1, policy_id1) = mint_script_and_policy(0);
4781 let (mint_script2, policy_id2) = mint_script_and_policy(1);
4782
4783 let result = create_mint_asset_builder().native_script(
4784 mint_script1,
4785 NativeScriptWitnessInfo::assume_signature_count(),
4786 );
4787
4788 tx_builder.add_mint(result).unwrap();
4789
4790 let result = create_mint_asset_builder().native_script(
4791 mint_script2,
4792 NativeScriptWitnessInfo::assume_signature_count(),
4793 );
4794
4795 tx_builder.add_mint(result).unwrap();
4796
4797 assert!(tx_builder.mint.is_some());
4798
4799 let mint = tx_builder.mint.unwrap();
4800
4801 assert_eq!(mint.len(), 2);
4802 assert_mint_asset(&mint, &policy_id1);
4803 assert_mint_asset(&mint, &policy_id2);
4804 }
4805
4806 #[test]
4807 fn add_mint_same_policy() {
4808 let mut tx_builder = create_default_tx_builder();
4809
4810 let (mint_script1, policy_id1) = mint_script_and_policy(1);
4811 let (mint_script2, policy_id2) = mint_script_and_policy(2);
4812 let (mint_script3, policy_id3) = mint_script_and_policy(3);
4813
4814 let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
4815 let name2 = AssetName::new(vec![1u8, 1, 2, 3]).unwrap();
4816 let name3 = AssetName::new(vec![2u8, 1, 2, 3]).unwrap();
4817 let name4 = AssetName::new(vec![3u8, 1, 2, 3]).unwrap();
4818 let amount = 1234;
4819
4820 let input = {
4822 let ((_spend, _), _, address) = create_account();
4823 SingleInputBuilder::new(
4824 TransactionInput::new(genesis_id(), 0),
4825 TransactionOutput::new(address, Value::from(10_000_000), None, None),
4826 )
4827 .payment_key()
4828 .unwrap()
4829 };
4830 tx_builder.add_input(input).unwrap();
4831
4832 let input = {
4834 let cred = StakeCredential::new_script(policy_id1);
4835 let address = BaseAddress::new(NetworkInfo::testnet().network_id(), cred.clone(), cred)
4836 .to_address();
4837 let builder = SingleInputBuilder::new(
4838 TransactionInput::new(genesis_id(), 0),
4839 TransactionOutput::new(address, Value::from(10_000_000), None, None),
4840 );
4841 builder
4842 .native_script(
4843 mint_script1.clone(),
4844 NativeScriptWitnessInfo::assume_signature_count(),
4845 )
4846 .unwrap()
4847 };
4848 tx_builder.add_input(input).unwrap();
4849
4850 let original_tx_fee = tx_builder.min_fee(false).unwrap();
4851 assert_eq!(original_tx_fee, 169002);
4852
4853 let result = SingleMintBuilder::new_single_asset(name1, amount).native_script(
4854 mint_script1,
4855 NativeScriptWitnessInfo::assume_signature_count(),
4856 );
4857
4858 tx_builder.add_mint(result).unwrap();
4859
4860 let result = SingleMintBuilder::new_single_asset(name2, amount).native_script(
4861 mint_script2,
4862 NativeScriptWitnessInfo::assume_signature_count(),
4863 );
4864
4865 tx_builder.add_mint(result).unwrap();
4866
4867 let result = SingleMintBuilder::new_single_asset(name3, amount).native_script(
4868 mint_script3.clone(),
4869 NativeScriptWitnessInfo::assume_signature_count(),
4870 );
4871
4872 tx_builder.add_mint(result).unwrap();
4873
4874 let result = SingleMintBuilder::new_single_asset(name4, amount).native_script(
4875 mint_script3,
4876 NativeScriptWitnessInfo::assume_signature_count(),
4877 );
4878
4879 tx_builder.add_mint(result).unwrap();
4880
4881 let mint = tx_builder.get_mint().unwrap();
4882
4883 assert_eq!(mint.len(), 3);
4884 assert_eq!(mint.deref().get(&policy_id1).unwrap().len(), 1);
4885 assert_eq!(mint.deref().get(&policy_id2).unwrap().len(), 1);
4886 assert_eq!(mint.deref().get(&policy_id3).unwrap().len(), 2);
4887
4888 let mint_scripts = tx_builder.witness_builders.build_fake().unwrap();
4889
4890 assert_eq!(mint_scripts.native_scripts.unwrap().len(), 3);
4891 assert_eq!(mint_scripts.vkeywitnesses.unwrap().len(), 6);
4892 assert!(mint_scripts.bootstrap_witnesses.is_none());
4893 assert!(mint_scripts.plutus_datums.is_none());
4894 assert!(mint_scripts.plutus_v1_scripts.is_none());
4895 assert!(mint_scripts.redeemers.is_none());
4896 }
4897
4898 #[test]
4899 fn add_output_amount() {
4900 let mut tx_builder = create_default_tx_builder();
4901
4902 let policy_id1 = PolicyId::from([0u8; 28]);
4903 let multiasset = create_multiasset_one_asset(&policy_id1);
4904 let value = Value::new(249, multiasset);
4905
4906 let address = byron_address();
4907 tx_builder
4908 .add_output(
4909 TransactionOutputBuilder::new()
4910 .with_address(address.clone())
4911 .next()
4912 .unwrap()
4913 .with_value(value.clone())
4914 .build()
4915 .unwrap(),
4916 )
4917 .unwrap();
4918
4919 assert_eq!(tx_builder.outputs.len(), 1);
4920 let out = &tx_builder.outputs[0];
4921
4922 assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
4923 assert_eq!(*out.amount(), value);
4924 }
4925
4926 #[test]
4927 fn add_output_coin() {
4928 let mut tx_builder = create_default_tx_builder();
4929
4930 let address = byron_address();
4931 let coin = 208;
4932 tx_builder
4933 .add_output(
4934 TransactionOutputBuilder::new()
4935 .with_address(address.clone())
4936 .next()
4937 .unwrap()
4938 .with_value(coin)
4939 .build()
4940 .unwrap(),
4941 )
4942 .unwrap();
4943
4944 assert_eq!(tx_builder.outputs.len(), 1);
4945 let out = &tx_builder.outputs[0];
4946
4947 assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
4948 assert_eq!(out.amount().coin, coin);
4949 assert!(!out.amount().has_multiassets());
4950 }
4951
4952 #[test]
4953 fn add_output_coin_and_multiasset() {
4954 let mut tx_builder = create_default_tx_builder();
4955
4956 let policy_id1 = PolicyId::from([0u8; 28]);
4957 let multiasset = create_multiasset_one_asset(&policy_id1);
4958
4959 let address = byron_address();
4960 let coin = 249;
4961
4962 tx_builder
4963 .add_output(
4964 TransactionOutputBuilder::new()
4965 .with_address(address.clone())
4966 .next()
4967 .unwrap()
4968 .with_value(Value::new(coin, multiasset.clone()))
4969 .build()
4970 .unwrap(),
4971 )
4972 .unwrap();
4973
4974 assert_eq!(tx_builder.outputs.len(), 1);
4975 let out = &tx_builder.outputs[0];
4976
4977 assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
4978 assert_eq!(out.amount().coin, coin);
4979 assert_eq!(out.amount().multiasset, multiasset);
4980 }
4981
4982 #[test]
4983 fn add_output_asset_and_min_required_coin() {
4984 let mut tx_builder = create_realistic_tx_builder();
4985
4986 let policy_id1 = PolicyId::from([0u8; 28]);
4987 let multiasset = create_multiasset_one_asset(&policy_id1);
4988
4989 let address = byron_address();
4990 tx_builder
4991 .add_output(
4992 TransactionOutputBuilder::new()
4993 .with_address(address.clone())
4994 .next()
4995 .unwrap()
4996 .with_asset_and_min_required_coin(
4997 multiasset.clone(),
4998 tx_builder.config.coins_per_utxo_byte,
4999 )
5000 .unwrap()
5001 .build()
5002 .unwrap(),
5003 )
5004 .unwrap();
5005
5006 assert_eq!(tx_builder.outputs.len(), 1);
5007 let out = &tx_builder.outputs[0];
5008
5009 assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
5010 assert_eq!(out.amount().multiasset, multiasset);
5011 assert_eq!(out.amount().coin, 1086120);
5012 }
5013
5014 #[test]
5015 fn add_mint_asset_and_output() {
5016 let mut tx_builder = create_default_tx_builder();
5017
5018 let (mint_script0, policy_id0) = mint_script_and_policy(0);
5019 let (mint_script1, policy_id1) = mint_script_and_policy(1);
5020
5021 let name = create_asset_name();
5022 let amount = 1234;
5023
5024 let address = byron_address();
5025 let coin = 249;
5026
5027 let result = SingleMintBuilder::new_single_asset(name.clone(), amount).native_script(
5028 mint_script0,
5029 NativeScriptWitnessInfo::assume_signature_count(),
5030 );
5031
5032 tx_builder.add_mint(result).unwrap();
5033
5034 let multiasset = {
5035 let mut multiasset = MultiAsset::new();
5036 multiasset.set(policy_id1, name.clone(), 1234);
5037 multiasset
5038 };
5039
5040 let output = TransactionOutputBuilder::new()
5041 .with_address(address.clone())
5042 .next()
5043 .unwrap()
5044 .with_value(Value::new(coin, multiasset))
5045 .build()
5046 .unwrap();
5047
5048 tx_builder.add_output(output).unwrap();
5049
5050 let result = SingleMintBuilder::new_single_asset(name.clone(), amount).native_script(
5051 mint_script1,
5052 NativeScriptWitnessInfo::assume_signature_count(),
5053 );
5054
5055 tx_builder.add_mint(result).unwrap();
5056
5057 assert!(tx_builder.mint.is_some());
5058
5059 let mint = tx_builder.mint.as_ref().unwrap();
5060
5061 assert_eq!(mint.len(), 2);
5063 assert_mint_asset(mint, &policy_id0);
5064 assert_mint_asset(mint, &policy_id1);
5065
5066 assert_eq!(tx_builder.outputs.len(), 1);
5068 let out = &tx_builder.outputs[0];
5069
5070 assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
5071 assert_eq!(out.amount().coin, coin);
5072
5073 let multiasset = &out.amount().multiasset;
5074
5075 assert_eq!(multiasset.len(), 1);
5077 assert!(multiasset.deref().get(&policy_id0).is_none());
5078 assert!(multiasset.deref().get(&policy_id1).is_some());
5079
5080 let asset = multiasset.deref().get(&policy_id1).unwrap();
5081 assert_eq!(asset.len(), 1);
5082 assert_eq!(*asset.get(&name).unwrap(), 1234);
5083 }
5084
5085 #[test]
5086 fn add_mint_asset_and_min_required_coin() {
5087 let mut tx_builder = create_realistic_tx_builder();
5088
5089 let (mint_script0, policy_id0) = mint_script_and_policy(0);
5090 let (mint_script1, policy_id1) = mint_script_and_policy(1);
5091
5092 let name = create_asset_name();
5093 let amount = 1234;
5094
5095 let address = byron_address();
5096
5097 let result = SingleMintBuilder::new_single_asset(name.clone(), amount).native_script(
5098 mint_script0,
5099 NativeScriptWitnessInfo::assume_signature_count(),
5100 );
5101
5102 tx_builder.add_mint(result).unwrap();
5103
5104 let multiasset = {
5105 let mut multiasset = MultiAsset::new();
5106 multiasset.set(policy_id1, name.clone(), 1234);
5107 multiasset
5108 };
5109
5110 let output = TransactionOutputBuilder::new()
5111 .with_address(address.clone())
5112 .next()
5113 .unwrap()
5114 .with_asset_and_min_required_coin(multiasset, tx_builder.config.coins_per_utxo_byte)
5115 .unwrap()
5116 .build()
5117 .unwrap();
5118
5119 tx_builder.add_output(output).unwrap();
5120
5121 let result = SingleMintBuilder::new_single_asset(name.clone(), amount).native_script(
5122 mint_script1,
5123 NativeScriptWitnessInfo::assume_signature_count(),
5124 );
5125
5126 tx_builder.add_mint(result).unwrap();
5127
5128 assert!(tx_builder.mint.is_some());
5129
5130 let mint = tx_builder.mint.as_ref().unwrap();
5131
5132 assert_eq!(mint.len(), 2);
5134 assert_mint_asset(mint, &policy_id0);
5135 assert_mint_asset(mint, &policy_id1);
5136
5137 assert_eq!(tx_builder.outputs.len(), 1);
5139 let out = &tx_builder.outputs[0];
5140
5141 assert_eq!(out.address().to_raw_bytes(), address.to_raw_bytes());
5142 assert_eq!(out.amount().coin, 1086120);
5143
5144 let multiasset = &out.amount().multiasset;
5145
5146 assert_eq!(multiasset.len(), 1);
5148 assert!(multiasset.deref().get(&policy_id0).is_none());
5149 assert!(multiasset.deref().get(&policy_id1).is_some());
5150
5151 let asset = multiasset.deref().get(&policy_id1).unwrap();
5152 assert_eq!(asset.len(), 1);
5153 assert_eq!(*asset.get(&name).unwrap(), 1234);
5154 }
5155
5156 #[test]
5157 fn total_input_with_mint_and_burn() {
5158 let mut tx_builder = create_tx_builder_with_fee(create_linear_fee(0, 1));
5159 let (_, (_stake, _), addr_test_0) = create_account();
5160
5161 let (mint_script1, policy_id1) = mint_script_and_policy(0);
5162 let (mint_script2, policy_id2) = mint_script_and_policy(1);
5163
5164 let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap();
5165
5166 let ma_input1 = 100;
5167 let ma_input2 = 200;
5168 let ma_output1 = 60;
5169
5170 let multiassets = [ma_input1, ma_input2, ma_output1]
5171 .iter()
5172 .map(|input| {
5173 let mut multiasset = MultiAsset::new();
5174 multiasset.set(policy_id1, name.clone(), *input);
5175 multiasset.set(policy_id2, name.clone(), *input);
5176 multiasset
5177 })
5178 .collect::<Vec<MultiAsset>>();
5179
5180 for (multiasset, ada) in multiassets.iter().zip([100u64, 100, 100].iter().cloned()) {
5181 let mut input_amount = Value::from(ada);
5182 input_amount.multiasset = multiasset.clone();
5183
5184 let input = {
5185 SingleInputBuilder::new(
5186 TransactionInput::new(genesis_id(), 0),
5187 TransactionOutput::new(addr_test_0.clone(), input_amount, None, None),
5188 )
5189 .payment_key()
5190 .unwrap()
5191 };
5192 tx_builder.add_input(input).unwrap();
5193 }
5194
5195 tx_builder
5196 .add_output(
5197 TransactionOutputBuilder::new()
5198 .with_address(byron_address())
5199 .next()
5200 .unwrap()
5201 .with_value(208)
5202 .build()
5203 .unwrap(),
5204 )
5205 .unwrap();
5206
5207 let total_input_before_mint = tx_builder.get_total_input().unwrap();
5208 let total_output_before_mint = tx_builder.get_total_output().unwrap();
5209
5210 assert_eq!(total_input_before_mint.coin, 300);
5211 assert_eq!(total_output_before_mint.coin, 208);
5212 let ma1_input = &total_input_before_mint.multiasset;
5213 let ma1_output = &total_output_before_mint;
5214 assert_eq!(ma1_input.get(&policy_id1, &name).unwrap(), 360);
5215 assert_eq!(ma1_input.get(&policy_id2, &name).unwrap(), 360);
5216 assert!(!ma1_output.has_multiassets());
5217
5218 let result = SingleMintBuilder::new_single_asset(name.clone(), 40).native_script(
5220 mint_script1,
5221 NativeScriptWitnessInfo::assume_signature_count(),
5222 );
5223 tx_builder.add_mint(result).unwrap();
5224
5225 let result = SingleMintBuilder::new_single_asset(name.clone(), -40).native_script(
5227 mint_script2,
5228 NativeScriptWitnessInfo::assume_signature_count(),
5229 );
5230 tx_builder.add_mint(result).unwrap();
5231
5232 let total_input_after_mint = tx_builder.get_total_input().unwrap();
5233 let total_output_after_mint = tx_builder.get_total_output().unwrap();
5234
5235 assert_eq!(total_input_after_mint.coin, 300);
5236 assert_eq!(total_output_before_mint.coin, 208);
5237 let ma2_input = total_input_after_mint.multiasset;
5238 let ma2_output = total_output_after_mint.multiasset;
5239 assert_eq!(ma2_input.get(&policy_id1, &name).unwrap(), 400);
5240 assert_eq!(ma2_input.get(&policy_id2, &name).unwrap(), 360);
5241 assert_eq!(ma2_output.get(&policy_id2, &name).unwrap(), 40);
5242 }
5243
5244 #[test]
5245 fn test_contract() {
5246 let mut tx_builder = create_realistic_tx_builder();
5247
5248 let mut spacebudz_asset = MultiAsset::new();
5257 spacebudz_asset.set(
5258 PolicyId::from_hex("6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52").unwrap(),
5259 AssetName::new(hex::decode("537061636542756442696433303533").unwrap()).unwrap(),
5260 1,
5261 );
5262
5263 let private_key = PrivateKey::from_normal_bytes(
5265 &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
5266 .unwrap(),
5267 )
5268 .unwrap();
5269
5270 {
5272 let required_signers = vec![
5273 private_key.to_public().hash(),
5275 ];
5276
5277 let input_utxo = TransactionOutputBuilder::new()
5278 .with_address(
5279 Address::from_bech32(
5280 "addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
5281 )
5282 .unwrap(),
5283 )
5284 .next()
5285 .unwrap()
5286 .with_value(Value::new(70000000, spacebudz_asset.clone()))
5287 .build()
5288 .unwrap();
5289 tx_builder.add_input(SingleInputBuilder::new(
5290 TransactionInput::new(
5291 TransactionHash::from_hex("473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c").unwrap(),
5292 0
5293 ),
5294 input_utxo.output
5295 ).plutus_script(
5296 PartialPlutusWitness::new(
5297 PlutusScriptWitness::from(
5298 PlutusScript::PlutusV1(PlutusV1Script::new(
5299 hex::decode("59193d010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
5300 ))
5301 ),
5302 PlutusData::from_cbor_bytes(&hex::decode("D866820380").unwrap()).unwrap(),
5303 ),
5304 required_signers.into(),
5305 PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()
5306 ).unwrap()).unwrap();
5307 }
5308
5309 {
5311 let output_utxo = TransactionOutputBuilder::new()
5312 .with_address(
5313 Address::from_bech32(
5314 "addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
5315 )
5316 .unwrap(),
5317 )
5318 .with_data(DatumOption::new_hash(
5319 DatumHash::from_hex(
5320 "f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8",
5321 )
5322 .unwrap(),
5323 ))
5324 .next()
5325 .unwrap()
5326 .with_value(Value::new(1851850, spacebudz_asset))
5327 .build()
5328 .unwrap();
5329 tx_builder.add_output(output_utxo).unwrap();
5330 }
5331
5332 {
5334 let output_utxo = TransactionOutputBuilder::new()
5335 .with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
5336 .next()
5337 .unwrap()
5338 .with_value(67250397)
5339 .build()
5340 .unwrap();
5341 tx_builder.add_output(output_utxo).unwrap();
5342 }
5343
5344 {
5346 let input_utxo = TransactionOutputBuilder::new()
5347 .with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
5348 .next()
5349 .unwrap()
5350 .with_value(5000000)
5351 .build()
5352 .unwrap();
5353 tx_builder
5354 .add_collateral(
5355 SingleInputBuilder::new(
5356 TransactionInput::new(
5357 TransactionHash::from_hex(
5358 "a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b",
5359 )
5360 .unwrap(),
5361 0,
5362 ),
5363 input_utxo.output,
5364 )
5365 .payment_key()
5366 .unwrap(),
5367 )
5368 .unwrap();
5369 }
5370
5371 {
5373 let mut map = MetadatumMap::new();
5374 map.set(
5375 TransactionMetadatum::new_int(Int::from(0u64)),
5376 TransactionMetadatum::new_bytes(hex::decode("d866820080").unwrap()).unwrap(),
5377 );
5378
5379 let mut aux_data = AuxiliaryData::new();
5380 aux_data
5381 .metadata_mut()
5382 .set(405, TransactionMetadatum::new_map(map));
5383 tx_builder.add_auxiliary_data(aux_data);
5384 }
5385
5386 let original_tx_fee = tx_builder.min_fee(false).unwrap();
5387 assert_eq!(original_tx_fee, 470421);
5388 tx_builder.set_fee(897753);
5389
5390 {
5391 tx_builder.set_exunits(
5392 RedeemerWitnessKey::new(RedeemerTag::Spend, 0),
5393 ExUnits::new(5000000, 2000000000),
5394 );
5395 }
5396 let tx = tx_builder.build(ChangeSelectionAlgo::Default, &Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap()).unwrap();
5397 assert_eq!(hex::encode(tx.body.to_cbor_bytes()), "a700d9010281825820473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c00018283581d71aba3c2914116298a146af57d8156b1583f183fc05c0aa48ee95bec71821a001c41caa1581c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52a14f537061636542756442696433303533015820f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8825839015627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9a013112333b21ec5063ae54f31b0ea883635b64530b70785a49c95041a040228dd021a000db2d907582029ed935cc80249c4de9f3e96fdcea6b7da123a543bbe75fffe9e2c66119e426d0b58205d5863643ea0687f9ca3ea903e9d86d81787373da1ebf196206c31f29608ce9b0dd9010281825820a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b000ed9010281581c1c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c");
5398 }
5399
5400 #[test]
5401 fn test_contract_dummy_exunit() {
5402 let mut tx_builder = create_realistic_tx_builder();
5403
5404 let mut spacebudz_asset = MultiAsset::new();
5405 spacebudz_asset.set(
5406 PolicyId::from_hex("6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52").unwrap(),
5407 AssetName::new(hex::decode("537061636542756442696433303533").unwrap()).unwrap(),
5408 1,
5409 );
5410
5411 let private_key = &PrivateKey::from_normal_bytes(
5413 &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
5414 .unwrap(),
5415 )
5416 .unwrap();
5417
5418 {
5420 let required_signers = vec![
5421 private_key.to_public().hash(),
5423 ];
5424
5425 let input_utxo = TransactionOutputBuilder::new()
5426 .with_address(
5427 Address::from_bech32(
5428 "addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
5429 )
5430 .unwrap(),
5431 )
5432 .next()
5433 .unwrap()
5434 .with_value(Value::new(70000000, spacebudz_asset.clone()))
5435 .build()
5436 .unwrap();
5437 tx_builder.add_input(SingleInputBuilder::new(
5438 TransactionInput::new(
5439 TransactionHash::from_hex("473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c").unwrap(),
5440 0
5441 ),
5442 input_utxo.output
5443 ).plutus_script(
5444 PartialPlutusWitness::new(
5445 PlutusScriptWitness::from(
5446 PlutusScript::PlutusV1(PlutusV1Script::new(
5447 hex::decode("59193d010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
5448 ))
5449 ),
5450 PlutusData::from_cbor_bytes(&hex::decode("D866820380").unwrap()).unwrap(),
5451 ),
5452 required_signers.into(),
5453 PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()
5454 ).unwrap()).unwrap();
5455 }
5456
5457 {
5459 let output_utxo = TransactionOutputBuilder::new()
5460 .with_address(
5461 Address::from_bech32(
5462 "addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
5463 )
5464 .unwrap(),
5465 )
5466 .with_data(DatumOption::new_hash(
5467 DatumHash::from_hex(
5468 "f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8",
5469 )
5470 .unwrap(),
5471 ))
5472 .next()
5473 .unwrap()
5474 .with_value(Value::new(1851850, spacebudz_asset))
5475 .build()
5476 .unwrap();
5477 tx_builder.add_output(output_utxo).unwrap();
5478 }
5479
5480 {
5482 let output_utxo = TransactionOutputBuilder::new()
5483 .with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
5484 .next()
5485 .unwrap()
5486 .with_value(67250397)
5487 .build()
5488 .unwrap();
5489 tx_builder.add_output(output_utxo).unwrap();
5490 }
5491
5492 {
5494 let input_utxo = TransactionOutputBuilder::new()
5495 .with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
5496 .next()
5497 .unwrap()
5498 .with_value(5000000)
5499 .build()
5500 .unwrap();
5501 tx_builder
5502 .add_collateral(
5503 SingleInputBuilder::new(
5504 TransactionInput::new(
5505 TransactionHash::from_hex(
5506 "a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b",
5507 )
5508 .unwrap(),
5509 0,
5510 ),
5511 input_utxo.output,
5512 )
5513 .payment_key()
5514 .unwrap(),
5515 )
5516 .unwrap();
5517 }
5518
5519 {
5521 let mut map = MetadatumMap::new();
5522 map.set(
5523 TransactionMetadatum::new_int(0u64.into()),
5524 TransactionMetadatum::new_bytes(hex::decode("d866820080").unwrap()).unwrap(),
5525 );
5526
5527 let mut aux_data = AuxiliaryData::new();
5528 aux_data
5529 .metadata_mut()
5530 .set(405, TransactionMetadatum::new_map(map));
5531 tx_builder.add_auxiliary_data(aux_data);
5532 }
5533
5534 tx_builder.set_fee(897753);
5535
5536 let mut tx_redeemer_builder = tx_builder
5537 .build_for_evaluation(
5538 ChangeSelectionAlgo::Default,
5539 &Address::from_bech32("addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed")
5540 .unwrap(),
5541 )
5542 .unwrap();
5543
5544 let fake_script_hash = tx_redeemer_builder.draft_body().script_data_hash.unwrap();
5545 assert_eq!(
5546 fake_script_hash.to_hex(),
5547 "0000000000000000000000000000000000000000000000000000000000000000"
5548 );
5549 {
5550 tx_redeemer_builder.set_exunits(
5551 RedeemerWitnessKey::new(RedeemerTag::Spend, 0),
5552 ExUnits::new(5000000, 2000000000),
5553 );
5554 tx_builder.set_exunits(
5555 RedeemerWitnessKey::new(RedeemerTag::Spend, 0),
5556 ExUnits::new(5000000, 2000000000),
5557 );
5558 }
5559 let signed_tx_builder = tx_builder
5560 .build(
5561 ChangeSelectionAlgo::Default,
5562 &Address::from_bech32("addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed")
5563 .unwrap(),
5564 )
5565 .unwrap();
5566 let real_script_hash = signed_tx_builder.body.script_data_hash.as_ref().unwrap();
5567 assert_eq!(
5568 real_script_hash.to_hex(),
5569 "5d5863643ea0687f9ca3ea903e9d86d81787373da1ebf196206c31f29608ce9b"
5570 );
5571
5572 let tx = &signed_tx_builder.body;
5573 assert_eq!(hex::encode(tx.to_cbor_bytes()), "a700d9010281825820473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c00018283581d71aba3c2914116298a146af57d8156b1583f183fc05c0aa48ee95bec71821a001c41caa1581c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52a14f537061636542756442696433303533015820f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8825839015627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9a013112333b21ec5063ae54f31b0ea883635b64530b70785a49c95041a040228dd021a000db2d907582029ed935cc80249c4de9f3e96fdcea6b7da123a543bbe75fffe9e2c66119e426d0b58205d5863643ea0687f9ca3ea903e9d86d81787373da1ebf196206c31f29608ce9b0dd9010281825820a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b000ed9010281581c1c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c");
5574 }
5575
5576 #[test]
5577 fn test_collateral() {
5578 let mut tx_builder = create_realistic_tx_builder();
5579
5580 let mut spacebudz_asset = MultiAsset::new();
5583 spacebudz_asset.set(
5584 PolicyId::from_hex("6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52").unwrap(),
5585 AssetName::new(hex::decode("537061636542756442696433303533").unwrap()).unwrap(),
5586 1,
5587 );
5588
5589 let private_key = PrivateKey::from_normal_bytes(
5591 &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
5592 .unwrap(),
5593 )
5594 .unwrap();
5595
5596 {
5598 let required_signers = vec![
5599 private_key.to_public().hash(),
5601 ];
5602
5603 let input_utxo = TransactionOutputBuilder::new()
5604 .with_address(
5605 Address::from_bech32(
5606 "addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
5607 )
5608 .unwrap(),
5609 )
5610 .next()
5611 .unwrap()
5612 .with_value(Value::new(70000000, spacebudz_asset.clone()))
5613 .build()
5614 .unwrap();
5615 tx_builder.add_input(SingleInputBuilder::new(
5616 TransactionInput::new(
5617 TransactionHash::from_hex("473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c").unwrap(),
5618 0
5619 ),
5620 input_utxo.output
5621 ).plutus_script(
5622 PartialPlutusWitness::new(
5623 PlutusScriptWitness::from(
5624 PlutusScript::PlutusV1(PlutusV1Script::new(
5625 hex::decode("59193d010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
5626 ))
5627 ),
5628 PlutusData::from_cbor_bytes(&hex::decode("D866820380").unwrap()).unwrap(),
5629 ),
5630 required_signers.into(),
5631 PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()
5632 ).unwrap()).unwrap();
5633 }
5634
5635 {
5637 let output_utxo = TransactionOutputBuilder::new()
5638 .with_address(
5639 Address::from_bech32(
5640 "addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed",
5641 )
5642 .unwrap(),
5643 )
5644 .with_data(DatumOption::new_hash(
5645 DatumHash::from_hex(
5646 "f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8",
5647 )
5648 .unwrap(),
5649 ))
5650 .next()
5651 .unwrap()
5652 .with_value(Value::new(1851850, spacebudz_asset))
5653 .build()
5654 .unwrap();
5655 tx_builder.add_output(output_utxo).unwrap();
5656 }
5657
5658 {
5660 let output_utxo = TransactionOutputBuilder::new()
5661 .with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
5662 .next()
5663 .unwrap()
5664 .with_value(67250397)
5665 .build()
5666 .unwrap();
5667 tx_builder.add_output(output_utxo).unwrap();
5668 }
5669
5670 {
5672 let input_utxo = TransactionOutputBuilder::new()
5673 .with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
5674 .next()
5675 .unwrap()
5676 .with_value(5000000)
5677 .build()
5678 .unwrap();
5679 tx_builder
5680 .add_collateral(
5681 SingleInputBuilder::new(
5682 TransactionInput::new(
5683 TransactionHash::from_hex(
5684 "a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b",
5685 )
5686 .unwrap(),
5687 0,
5688 ),
5689 input_utxo.output,
5690 )
5691 .payment_key()
5692 .unwrap(),
5693 )
5694 .unwrap();
5695 }
5696
5697 {
5699 let mut map = MetadatumMap::new();
5700 map.set(
5701 TransactionMetadatum::new_int(0u64.into()),
5702 TransactionMetadatum::new_bytes(hex::decode("d866820080").unwrap()).unwrap(),
5703 );
5704
5705 let mut aux_data = AuxiliaryData::new();
5706 aux_data
5707 .metadata_mut()
5708 .set(405, TransactionMetadatum::new_map(map));
5709 tx_builder.add_auxiliary_data(aux_data);
5710 }
5711
5712 tx_builder.set_collateral_return(
5713 TransactionOutputBuilder::new()
5714 .with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
5715 .next()
5716 .unwrap()
5717 .with_value(2000000)
5718 .build()
5719 .unwrap()
5720 .output
5721 );
5722
5723 tx_builder.set_fee(897753);
5724
5725 {
5726 tx_builder.set_exunits(
5727 RedeemerWitnessKey::new(RedeemerTag::Spend, 0),
5728 ExUnits::new(5000000, 2000000000),
5729 );
5730 }
5731 let signed_tx_builder = tx_builder
5732 .build(
5733 ChangeSelectionAlgo::Default,
5734 &Address::from_bech32("addr1wx468s53gytznzs5dt6hmq2kk9vr7xplcpwq4fywa9d7cug7fd0ed")
5735 .unwrap(),
5736 )
5737 .unwrap();
5738 assert_eq!(signed_tx_builder.body.total_collateral, Some(3000000));
5739 }
5740
5741 #[test]
5742 fn build_tx_with_ref_input() {
5743 let mut tx_builder = create_default_tx_builder();
5744 let change_key = root_key_15()
5745 .derive(harden(1852))
5746 .derive(harden(1815))
5747 .derive(harden(0))
5748 .derive(1)
5749 .derive(0)
5750 .to_public();
5751 let (_, (_, stake_cred), addr_net_0) = create_account();
5752 let input = {
5753 SingleInputBuilder::new(
5754 TransactionInput::new(genesis_id(), 0),
5755 TransactionOutput::new(addr_net_0.clone(), Value::from(5_000_000), None, None),
5756 )
5757 .payment_key()
5758 .unwrap()
5759 };
5760 tx_builder.add_input(input).unwrap();
5761 {
5763 let input_utxo = TransactionOutputBuilder::new()
5764 .with_address(Address::from_bech32("addr1q9tzwgthsm4hs8alk5v3rgjn7nf9pldlmnc3nrns6dvct2dqzvgjxvajrmzsvwh9fucmp65gxc6mv3fskurctfyuj5zqc7q30l").unwrap())
5765 .next()
5766 .unwrap()
5767 .with_value(5000000)
5768 .build()
5769 .unwrap();
5770 tx_builder
5771 .add_collateral(
5772 SingleInputBuilder::new(
5773 TransactionInput::new(
5774 TransactionHash::from_hex(
5775 "a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b",
5776 )
5777 .unwrap(),
5778 0,
5779 ),
5780 input_utxo.output,
5781 )
5782 .payment_key()
5783 .unwrap(),
5784 )
5785 .unwrap();
5786 }
5787
5788 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
5789 {
5790 let output = TransactionOutputBuilder::new()
5791 .with_address(BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred.clone(), stake_cred.clone()).to_address())
5792 .with_reference_script(
5793 Script::new_plutus_v1(
5794 PlutusV1Script::new(
5795 hex::decode("59193d010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
5796 )
5797 )
5798 )
5799 .with_data(DatumOption::new_datum(PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()))
5800 .next().unwrap()
5801 .with_value(880_000)
5802 .build().unwrap();
5803
5804 tx_builder.add_reference_input(TransactionUnspentOutput::new(
5805 TransactionInput::new(genesis_id(), 1),
5806 output.output,
5807 ));
5808 }
5809 tx_builder
5810 .add_output(
5811 TransactionOutputBuilder::new()
5812 .with_address(addr_net_0)
5813 .next()
5814 .unwrap()
5815 .with_value(880_000)
5816 .build()
5817 .unwrap(),
5818 )
5819 .unwrap();
5820 tx_builder.set_ttl(1000);
5821
5822 let change_addr =
5823 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
5824 .to_address();
5825 tx_builder
5826 .add_change_if_needed_for_tests(&change_addr)
5827 .unwrap();
5828 assert_eq!(tx_builder.outputs.len(), 2);
5829 let final_tx = tx_builder
5830 .build(ChangeSelectionAlgo::Default, &change_addr)
5831 .unwrap()
5832 .build_unchecked();
5833
5834 assert_eq!(final_tx.body.reference_inputs.unwrap().len(), 1);
5835 assert!(final_tx.witness_set.plutus_v1_scripts.is_none());
5836 }
5837
5838 #[test]
5839 fn build_tx_with_ref_input_script() {
5840 let mut tx_builder = create_default_tx_builder();
5841 let change_key = root_key_15()
5842 .derive(harden(1852))
5843 .derive(harden(1815))
5844 .derive(harden(0))
5845 .derive(1)
5846 .derive(0)
5847 .to_public();
5848 let (_, (_, stake_cred), addr_net_0) = create_account();
5849
5850 let script = Script::new_plutus_v2(
5851 PlutusV2Script::new(
5852 hex::decode("59193d020000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
5853 )
5854 );
5855
5856 let script_hash = script.hash();
5857
5858 let script_base_address = BaseAddress::new(
5859 NetworkInfo::testnet().network_id(),
5860 StakeCredential::new_script(script_hash),
5861 stake_cred.clone(),
5862 );
5863
5864 let input = {
5865 SingleInputBuilder::new(
5866 TransactionInput::new(genesis_id(), 0),
5867 TransactionOutput::new(addr_net_0.clone(), Value::from(5_000_000), None, None),
5868 )
5869 .payment_key()
5870 .unwrap()
5871 };
5872
5873 let input2 = {
5874 SingleInputBuilder::new(
5875 TransactionInput::new(genesis_id(), 1),
5876 TransactionOutput::new(
5877 script_base_address.to_address(),
5878 Value::from(5_000_000),
5879 None,
5880 None,
5881 ),
5882 )
5883 .plutus_script(
5884 PartialPlutusWitness::new(
5885 PlutusScriptWitness::from(script_hash),
5886 PlutusData::new_bytes(vec![]),
5887 ),
5888 vec![].into(),
5889 PlutusData::from_cbor_bytes(&hex::decode("D866820380").unwrap()).unwrap(),
5890 )
5891 .unwrap()
5892 };
5893
5894 let change_cred = StakeCredential::new_pub_key(change_key.to_raw_key().hash());
5895 let output = TransactionOutputBuilder::new()
5896 .with_address(BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred.clone(), stake_cred.clone()).to_address())
5897 .with_reference_script(
5898 Script::new_plutus_v2(
5899 PlutusV2Script::new(
5900 hex::decode("59193d020000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a0090080070060050040030022001111222123330010040030021112001112212330010030021120011").unwrap()
5901 )
5902 )
5903 )
5904 .with_data(DatumOption::new_datum(PlutusData::from_cbor_bytes(&hex::decode("d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301").unwrap()).unwrap()))
5905 .next().unwrap()
5906 .with_value(880_000)
5907 .build().unwrap();
5908
5909 tx_builder.add_reference_input(TransactionUnspentOutput::new(
5910 TransactionInput::new(genesis_id(), 1),
5911 output.output,
5912 ));
5913 tx_builder.add_input(input).unwrap();
5914
5915 tx_builder.add_input(input2).unwrap();
5916
5917 tx_builder
5918 .add_output(
5919 TransactionOutputBuilder::new()
5920 .with_address(addr_net_0)
5921 .next()
5922 .unwrap()
5923 .with_value(880_000)
5924 .build()
5925 .unwrap(),
5926 )
5927 .unwrap();
5928 tx_builder.set_ttl(1000);
5929
5930 let change_addr =
5931 BaseAddress::new(NetworkInfo::testnet().network_id(), change_cred, stake_cred)
5932 .to_address();
5933 tx_builder
5934 .add_change_if_needed_for_tests(&change_addr)
5935 .unwrap();
5936 assert_eq!(tx_builder.outputs.len(), 2);
5937 let final_tx = tx_builder
5938 .build(ChangeSelectionAlgo::Default, &change_addr)
5939 .unwrap()
5940 .build_unchecked();
5941
5942 assert_eq!(final_tx.body.reference_inputs.unwrap().len(), 1);
5943 assert!(final_tx.witness_set.plutus_v2_scripts.is_none());
5944 assert!(final_tx.witness_set.plutus_v1_scripts.is_none());
5945 }
5946}