cml_chain/builders/
tx_builder.rs

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/**
53 * A UTXO structure.
54 * This is not used on-chain anywhere but is useful for the builders
55 * as well as interfacing with CIP30 (same name as there)
56 */
57#[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
69// this isn't on-chain (hence why cbor_event's Serialize since we don't care about outer format)
70// but being able to (de)serialize helps massively with CIP30.
71impl 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        // add redeemers
118        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    /// build with fake witnesses to estimate tx fee cost
135    pub fn build_fake(&self) -> Result<TransactionWitnessSet, WitnessBuilderError> {
136        self.merge_data(true).map(|wit_builder| wit_builder.build())
137    }
138
139    /// build without including fake witnesses used for fee estimation. Allows missing witnesses
140    pub fn build_unchecked(&self) -> Result<TransactionWitnessSetBuilder, WitnessBuilderError> {
141        self.merge_data(false)
142    }
143}
144
145// tx_body must be the result of building from tx_builder
146// constructs the rest of the Transaction using fake witness data of the correct length
147// for use in calculating the size of the final Transaction
148fn 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    // we can't know the of scripts yet as they can't be calculated until we build the tx
224    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    // we can't know the of scripts yet as they can't be calculated until we build the tx
230
231    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    /// Performs CIP2's Largest First ada-only selection. Will error if outputs contain non-ADA assets.
274    LargestFirst,
275    /// Performs CIP2's Random Improve ada-only selection. Will error if outputs contain non-ADA assets.
276    RandomImprove,
277    /// Same as LargestFirst, but before adding ADA, will insert by largest-first for each asset type.
278    LargestFirstMultiAsset,
279    /// Same as RandomImprove, but before adding ADA, will insert by random-improve for each asset type.
280    RandomImproveMultiAsset,
281}
282
283#[derive(Clone, Debug)]
284pub struct TransactionBuilderConfig {
285    fee_algo: LinearFee,
286    pool_deposit: u64,            // protocol parameter
287    key_deposit: u64,             // protocol parameter
288    max_value_size: u32,          // protocol parameter
289    max_tx_size: u32,             // protocol parameter
290    coins_per_utxo_byte: Coin,    // protocol parameter
291    ex_unit_prices: ExUnitPrices, // protocol parameter
292    cost_models: CostModels,      // protocol parameter
293    _collateral_percentage: u32,  // protocol parameter
294    max_collateral_inputs: u32,   // protocol parameter
295    prefer_pure_change: bool,
296}
297
298#[derive(Clone, Debug, Default)]
299pub struct TransactionBuilderConfigBuilder {
300    fee_algo: Option<LinearFee>,
301    pool_deposit: Option<u64>,            // protocol parameter
302    key_deposit: Option<u64>,             // protocol parameter
303    max_value_size: Option<u32>,          // protocol parameter
304    max_tx_size: Option<u32>,             // protocol parameter
305    coins_per_utxo_byte: Option<Coin>,    // protocol parameter
306    ex_unit_prices: Option<ExUnitPrices>, // protocol parameter
307    cost_models: Option<CostModels>,      // protocol parameter
308    collateral_percentage: Option<u32>,   // protocol parameter
309    max_collateral_inputs: Option<u32>,   // protocol parameter
310    prefer_pure_change: bool,
311}
312
313impl TransactionBuilderConfigBuilder {
314    pub fn new() -> Self {
315        // we have to provide new to expose it to WASM builds
316        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>, // absolute slot number
424    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    /// This automatically selects and adds inputs from {inputs} consisting of just enough to cover
444    /// the outputs that have already been added.
445    /// This should be called after adding all certs/outputs/etc and will be an error otherwise.
446    /// Uses CIP2: https://github.com/cardano-foundation/CIPs/blob/master/CIP-0002/CIP-0002.md
447    /// Adding a change output must be called after via TransactionBuilder::add_change_if_needed()
448    /// This function, diverging from CIP2, takes into account fees and will attempt to add additional
449    /// inputs to cover the minimum fees. This does not, however, set the txbuilder's fee.
450    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                // Phase 3: add extra inputs needed for fees (not covered by CIP-2)
489                // We do this at the end because this new inputs won't be associated with
490                // a specific output, so the improvement algorithm we do above does not apply here.
491                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                // indices into {available_inputs} for inputs that contain {policy_id}:{asset_name}
512                let mut available_indices = (0..available_inputs.len()).collect::<Vec<usize>>();
513                // run largest-fist by each asset type
514                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                // add in remaining ADA
526                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                // run random-improve by each asset type
539                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                // add in remaining ADA
552                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                // Phase 3: add extra inputs needed for fees (not covered by CIP-2)
561                // We do this at the end because this new inputs won't be associated with
562                // a specific output, so the improvement algorithm we do above does not apply here.
563                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        // ordered in ascending order by predicate {by}
601        relevant_indices
602            .sort_by_key(|i| by(available_inputs[*i].utxo_info.amount()).expect("filtered above"));
603
604        // iterate in decreasing order for predicate {by}
605        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            // differing from CIP2, we include the needed fees in the targets instead of just output values
613            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        // Phase 1: Random Selection
645        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            // TODO: how should we adapt this to inputs being associated when running for other assets?
660            // if we do these two phases for each asset and don't take into account the other runs for other assets
661            // then we over-add (and potentially fail if we don't have plenty of inputs)
662            // On the other hand, the improvement phase it difficult to determine if a change is an improvement
663            // if we're trying to improve for multiple assets at a time without knowing how important each input is
664            // e.g. maybe we have lots of asset A but not much of B
665            // For now I will just have this be entirely separate per-asset but we might want to in a later commit
666            // consider the improvements separately and have it take some kind of dot product / distance for assets
667            // during the improvement phase and have the improvement phase target multiple asset types at once.
668            // One issue with that is how to scale in between different assets. We could maybe normalize them by
669            // dividing each asset type by the sum of the required asset type in all outputs.
670            // Another possibility for adapting this to multiassets is when associating an input x for asset type a
671            // we try and subtract all other assets b != a from the outputs we're trying to cover.
672            // It might make sense to diverge further and not consider it per-output and to instead just match against
673            // the sum of all outputs as one single value.
674            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            // Phase 2: Improvement
701            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        // after finalizing the improvement we need to actually add these results to the builder
730        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                        // it could also be a reference script - check those too
781                        // TODO: do we want to change how we store ref scripts to cache the hash to avoid re-hashing (slow on wasm) every time an input is added?
782                        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    /// calculates how much the fee would increase if you added a given output
823    pub fn fee_for_input(&self, result: &InputBuilderResult) -> Result<Coin, TxBuilderError> {
824        let mut self_copy = self.clone();
825
826        // we need some value for these for it to be a a valid transaction
827        // but since we're only calculating the difference between the fee of two transactions
828        // it doesn't matter what these are set as, since it cancels out
829        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    /// Add a reference input. Must be called BEFORE adding anything (inputs, certs, etc) that refer to this reference input.
841    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    /// Add explicit output via a TransactionOutput object
862    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    /// calculates how much the fee would increase if you added a given output
892    pub fn fee_for_output(
893        &self,
894        builder: &SingleOutputBuilderResult,
895    ) -> Result<Coin, TxBuilderError> {
896        let mut self_copy = self.clone();
897
898        // we need some value for these for it to be a a valid transaction
899        // but since we're only calculating the different between the fee of two transactions
900        // it doesn't matter what these are set as, since it cancels out
901        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    /// Returns a copy of the current mint state in the builder
1113    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        // note: collateral doesn't get counted for ref scripts
1167
1168        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    /// does not include refunds or withdrawals
1216    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    /// withdrawals and refunds
1226    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    /// Returns mint as tuple of (mint_value, burn_value) or two zero values
1237    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    /// Return explicit input plus implicit input plus mint
1250    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    /// Return explicit output plus implicit output plus burn/donation (does not consider fee directly)
1259    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    /// does not include fee
1272    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            // dummy exunits use large values
1332            // to avoid users accidentally spending all their ADA in tx fees,
1333            // we make that dummy exunits set a dummy script_data_hash to ensure the tx fails if submitted to a node
1334            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        // indices for redeemers in smart contract txs require fields to be sorted
1430        {
1431            // We sort inputs and withdrawals only since certs remain in the order given and
1432            // mint is sorted as items are added (by the nature of BTreeMaps)
1433            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        // we must build a tx with fake data (of correct size) to check the final Transaction size
1457        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    /// Returns object the body of the new transaction
1474    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    // TODO: switch from ChangeSelectionAlgo to ChangeSelectionBuilder
1487    /// Builds the transaction and moves to the next step redeemer units can be added and a draft tx can
1488    /// be evaluated
1489    /// NOTE: is_valid set to true
1490    pub fn build_for_evaluation(
1491        &self,
1492        algo: ChangeSelectionAlgo,
1493        change_address: &Address,
1494    ) -> Result<TxRedeemerBuilder, TxBuilderError> {
1495        // First we finish change selection
1496
1497        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    // TODO: switch from ChangeSelectionAlgo to ChangeSelectionBuilder
1508    /// Builds the transaction and moves to the next step where any real witness can be added
1509    /// NOTE: is_valid set to true
1510    pub fn build(
1511        &mut self,
1512        algo: ChangeSelectionAlgo,
1513        change_address: &Address,
1514    ) -> Result<SignedTxBuilder, TxBuilderError> {
1515        // First we finish change selection
1516        choose_change_selection_algo(algo)(self, change_address, true)?;
1517
1518        Ok(SignedTxBuilder {
1519            body: self.build_body()?,
1520            // Side note: redeemer indices are calculated every time witness builder is built
1521            witness_set: self.witness_builders.build_unchecked()?,
1522            is_valid: true,
1523            auxiliary_data: self.auxiliary_data.clone(),
1524        })
1525    }
1526
1527    /// used to override the exunit values initially provided when adding inputs
1528    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    /// warning: sum of all parts of a transaction must equal 0. You cannot just set the fee to the min value and forget about it
1535    /// warning: min_fee may be slightly larger than the actual minimum fee (ex: a few lovelaces)
1536    /// this is done to simplify the library code, but can be fixed later
1537    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    /// Builds the transaction and moves to the next step where any real witness can be added
1559    /// NOTE: is_valid set to true
1560    /// Will NOT require you to have set required signers & witnesses
1561    pub fn build(&self) -> Result<Redeemers, RedeemerBuilderError> {
1562        self.witness_builders.redeemer_set_builder.build(true)
1563    }
1564
1565    /// used to override the exunit values initially provided when adding inputs
1566    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    /// Transaction body with a dummy values for redeemers & script_data_hash
1573    /// Used for calculating exunits or required signers
1574    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    /// Transaction body with a dummy values for redeemers & script_data_hash and padded with dummy witnesses
1583    /// Used for calculating exunits
1584    /// note: is_valid set to true
1585    pub fn draft_tx(&self) -> Result<Transaction, WitnessBuilderError> {
1586        Ok(Transaction::new(
1587            self.draft_body.clone(),
1588            // Side note: redeemer indices are calculated every time witness builder is built
1589            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    /**
1633     * Builds the final transaction and checks that all witnesses are there
1634     */
1635    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    /**
1645     * Builds the transaction without doing any witness checks.
1646     *
1647     * This can be useful if other witnesses will be added later.
1648     * e.g. CIP30 signing takes a Transaction with possible witnesses
1649     * to send to the wallet to fill in the missing ones.
1650     */
1651    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    // Note: we only allow adding vkey & bootstraps at this stage
1661    // This is because other witness kinds increase the tx size
1662    // so they should have been added during the TransactionBuilder step
1663    //
1664    // However, if you manually set the fee during the TransactionBuilder step
1665    // to allow adding some extra witnesses later,
1666    // use `build_unchecked`
1667    //
1668    // Note: can't easily check inside the `add_vkey` or `add_bootstrap` functions if the user added a wrong witness
1669    // This is because scripts may require keys that weren't known exactly during the tx building phase
1670
1671    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
1709/// Warning: this function will mutate the /fee/ field
1710/// Make sure to call this function last after setting all other tx-body properties
1711/// Editing inputs, outputs, mint, etc. after change been calculated
1712/// might cause a mismatch in calculated fee versus the required fee
1713pub 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        // generating the change output involves changing the fee
1721        Some(_x) => return Ok(false),
1722    }?;
1723
1724    // note: can't add datum / script_ref to change
1725    // because we don't know how many change outputs will need to be created
1726    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            // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being
1738            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                    // calculate minADA for more precise max value size
1767                    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                    // we insert the entire available ADA temporarily here since that could potentially impact the size
1782                    // as it could be 1, 2 3 or 4 bytes for Coin.
1783                    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                    // If this becomes slow on large TXs we can optimize it like the following
1793                    // to avoid cloning + reserializing the entire output.
1794                    // This would probably be more relevant if we use a smarter packing algorithm
1795                    // which might need to compare more size differences than greedy
1796                    //let mut bytes_used = output.to_bytes().len();
1797
1798                    // a greedy packing is done here to avoid an exponential bin-packing
1799                    // which in most cases likely shouldn't be the difference between
1800                    // having an extra change output or not unless there are gigantic
1801                    // differences in NFT policy sizes
1802                    for (policy, assets) in change_estimator.multiasset.iter() {
1803                        // for simplicity we also don't split assets within a single policy since
1804                        // you would need to have a very high amount of assets (which add 1-36 bytes each)
1805                        // in a single policy to make a difference. In the future if this becomes an issue
1806                        // we can change that here.
1807
1808                        // this is the other part of the optimization but we need to take into account
1809                        // the difference between CBOR encoding which can change which happens in two places:
1810                        // a) length within assets of one policy id
1811                        // b) length of the entire multiasset
1812                        // so for simplicity we will just do it the safe, naive way unless
1813                        // performance becomes an issue.
1814                        //let extra_bytes = policy.to_bytes().len() + assets.to_bytes().len() + 2 + cbor_len_diff;
1815                        //if bytes_used + extra_bytes <= max_value_size as usize {
1816                        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                                // if we got here, this means we will run into a overflow error,
1830                                // so we want to split into multiple outputs, for that we...
1831
1832                                // 1. insert the current assets as they are, as this won't overflow
1833                                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                                // 2. create a new output with the base coin value as zero
1839                                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                                // 3. continue building the new output from the asset we stopped
1848                                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                        // calculate minADA for more precise max value size
1863                        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                // we might need multiple change outputs for cases where the change has many asset types
1880                // which surpass the max UTXO size limit
1881                let minimum_utxo_val = min_ada_required(
1882                    &TransactionOutput::new(
1883                        address.clone(),
1884                        // This is value is taken from the old code's code for min_pure_ada():
1885                        // arbitrary value that happens to give the right number of bytes at the CBOR level
1886                        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                        // this likely should never happen
1906                        return Err(TxBuilderError::NFTTooLargeForChange);
1907                    }
1908                    // we only add the minimum needed (for now) to cover this output
1909                    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                        // increase fee
1924                        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                // add potentially a separate pure ADA change output
1943                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                // add in the rest of the ADA
1971                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                // no-asset case so we have no problem burning the rest if there is no other option
1996                fn burn_extra(
1997                    builder: &mut TransactionBuilder,
1998                    burn_amount: u64,
1999                ) -> Result<bool, TxBuilderError> {
2000                    // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being
2001                    builder.set_fee(burn_amount);
2002                    Ok(false) // not enough input to covert the extra fee from adding an output so we just burn whatever is left
2003                }
2004                match change_estimator.coin >= min_ada {
2005                    false => burn_extra(builder, change_estimator.coin),
2006                    true => {
2007                        // check how much the fee would increase if we added a change output
2008                        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                                // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being
2028                                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; // might be out of date but suffices for our tests
2083                                   // this is what is used in mainnet
2084    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        // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy
2101        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        // just test that it doesn't throw
2310    }
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        // just test that it doesn't throw
2366    }
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(), // in reality, this should be the pool owner's key, not ours
2400        ))
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        // just test that it doesn't throw
2432    }
2433
2434    #[test]
2435    fn build_tx_exact_amount() {
2436        // transactions where sum(input) == sum(output) exact should pass
2437        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        // transactions where we have exactly enough ADA to add change should pass
2485        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        // transactions should fail with insufficient fees if a deposit is required
2535        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        // add a cert which requires a deposit
2569        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        // One coin and the minted asset goes into the output
2701        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        // Change must be one remaining coin because fee is one constant coin
2728        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        // One coin and the minted asset goes into the output
2783        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        // Change must be one remaining coin because fee is one constant coin
2810        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        // Prefer pure change!
2904        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        // The first change output that contains all the tokens contain minimum required Coin
2981        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        // Prefer pure change!
2991        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        // The single change output contains more Coin then minimal utxo value
3067        // But not enough to cover the additional fee for a separate output
3068        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        // add an input that contains an asset not present in the output
3085        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(); // just test that it doesn't throw
3135    }
3136
3137    #[test]
3138    fn build_tx_burn_less_than_min_ada() {
3139        // with this mainnet value we should end up with a final min_ada_required of just under 1_000_000
3140        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(); // just test that it doesn't throw
3194    }
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(); // just test that it doesn't throw
3255    }
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        // add an output that uses up all the token but leaves ADA
3265        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        // add an input that contains an asset & ADA
3286        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        // since all tokens got sent in the output
3321        // the change should be only ADA and not have any multiasset struct in it
3322        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; // super low max output size to test with fewer assets
3349        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, // super low max output size to test with fewer assets
3458        );
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        // we have a = 1 to test increasing fees when more inputs are added
3546        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        // change needed
3582        assert_eq!(2, tx.outputs.len());
3583        assert_eq!(2, tx.inputs.len());
3584        // confirm order of only what is necessary
3585        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        // we have a = 0 so we know adding inputs/outputs doesn't change the fee so we can analyze more
3592        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        // change not needed - should be exact
3628        assert_eq!(1, tx.outputs.len());
3629        assert_eq!(2, tx.inputs.len());
3630        // confirm order of only what is necessary
3631        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        // we have a = 0 so we know adding inputs/outputs doesn't change the fee so we can analyze more
3638        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        // should not be taken
3663        tx_builder.add_utxo(make_input(0u8, Value::from(150)));
3664
3665        // should not be taken
3666        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        // taken first to satisfy pid1:asset_name1 (but also satisfies pid2:asset_name3)
3674        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        // taken second to satisfy pid1:asset_name2 (but also satisfies pid2:asset_name1)
3681
3682        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        // should not be taken either
3689
3690        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        // taken third to satisfy pid2:asset_name_2
3697        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        // should be taken to get enough ADA
3704        let input6 = make_input(6u8, Value::from(700));
3705        tx_builder.add_utxo(input6.clone());
3706
3707        // should not be taken
3708        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        // check order expected per-asset
3726        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        // we have a = 1 to test increasing fees when more inputs are added
3848        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        // we need to look up the values to ensure there's enough
3886        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        // we have a = 1 to test increasing fees when more inputs are added
3967        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        // we have a = 1 to test increasing fees when more inputs are added
4011        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)) //multisig
4186            .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)) //multisig
4193            .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; // super low max output size to test with fewer assets
4282        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()); // purified
4375
4376        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        // One input from an unrelated address
4821        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        // One input from a related address
4833        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        // Mint contains two entries
5062        assert_eq!(mint.len(), 2);
5063        assert_mint_asset(mint, &policy_id0);
5064        assert_mint_asset(mint, &policy_id1);
5065
5066        // One new output is created
5067        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        // Only second mint entry was added to the output
5076        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        // Mint contains two entries
5133        assert_eq!(mint.len(), 2);
5134        assert_mint_asset(mint, &policy_id0);
5135        assert_mint_asset(mint, &policy_id1);
5136
5137        // One new output is created
5138        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        // Only second mint entry was added to the output
5147        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        // Adding mint
5219        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        // Adding burn
5226        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 tx = Transaction::from_bytes(
5249        //     hex::decode("84a70081825820473899cb48414442ea107735f7fc3e020f0293122e9d05e4be6f03ffafde5a0c00018283581d71aba3c2914116298a146af57d8156b1583f183fc05c0aa48ee95bec71821a001c41caa1581c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d52a14f537061636542756442696433303533015820f7f2f57c58b5e4872201ab678928b0d63935e82d022d385e1bad5bfe347e89d8825839015627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9a013112333b21ec5063ae54f31b0ea883635b64530b70785a49c95041a040228dd021a000db2d907582029ed935cc80249c4de9f3e96fdcea6b7da123a543bbe75fffe9e2c66119e426d0b582039249ec62e53b77ff197bf6821548157b14d56ef63ec3a0b233180e3ae4241740d81825820a90a895d07049afc725a0d6a38c6b82218b8d1de60e7bd70ecdd58f1d9e1218b000e81581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9a40081825820c9b539dea76713f036285a9c89d164ad929597367a5572c9911832f12fffe0235840bb7d26b65a15f9aa917663178d27e2f16a59bbd4aafe067090dcb60826a585d2b81bf6f25136f5c74fdf78fefcd1928ac6e03d28d13da10de1c03b185e697301038159194059193d010000332332233223232333332222233332222332232333222323332223233333333222222223233322232333322223232332232333222323332223232332233223232333332222233223322332233223322332222323223223232533530343330093333573466e1d401920042304e3055357426aae7940208cccd5cd19b875007480088c140c158d5d09aab9e500923333573466e1d40212000204f235058353059335738921035054310005a49926499263333573466e1d40112006205223333573466e1d40152004205523333573466e1d40192002205323333573466e1d401d2000205623505935305a3357389201035054310005b4992649926498cccd5cd19b8735573aa004900011980619191919191919191919191999ab9a3370e6aae75402920002333333333301a335028232323333573466e1cd55cea8012400046604060766ae854008c0b4d5d09aba25002235066353067335738921035054310006849926135573ca00226ea8004d5d0a80519a8140149aba150093335502f75ca05c6ae854020ccd540bdd728171aba1500733502804435742a00c66a05066aa0aa09aeb4d5d0a8029919191999ab9a3370e6aae754009200023350223232323333573466e1cd55cea80124000466a05466a086eb4d5d0a80118241aba135744a00446a0d46a60d666ae712401035054310006c49926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502833504375a6ae854008c120d5d09aba2500223506a35306b3357389201035054310006c49926135573ca00226ea8004d5d09aba250022350663530673357389201035054310006849926135573ca00226ea8004d5d0a80219a8143ae35742a00666a05066aa0aaeb88004d5d0a801181d1aba135744a00446a0c46a60c666ae71241035054310006449926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180f981e1aba135573ca00646666ae68cdc3a801240084603c608c6ae84d55cf280211999ab9a3370ea00690011180f18189aba135573ca00a46666ae68cdc3a80224000460426eb8d5d09aab9e500623505d35305e3357389201035054310005f49926499264984d55cea80089baa001357426ae8940088d4158d4c15ccd5ce2490350543100058499261057135055353056335738920103505435000574984d55cf280089baa001135573a6ea80044d55cea80089baa0012212330010030022001222222222212333333333300100b00a00900800700600500400300220012212330010030022001122123300100300212001122123300100300212001122123300100300212001212222300400521222230030052122223002005212222300100520011232230023758002640026aa080446666aae7c004940388cd4034c010d5d080118019aba200203f23232323333573466e1cd55cea801a4000466600e6464646666ae68cdc39aab9d5002480008cc034c0c4d5d0a80119a8098169aba135744a00446a0846a608666ae712401035054310004449926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a01eeb8d5d09aba2500223503e35303f335738921035054310004049926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540e888c8cccd55cf80112804919a80419aa81718031aab9d5002300535573ca00460086ae8800c0e84d5d08008891001091091198008020018900089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a06a6a606c66ae7124010350543100037499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a05e6a606066ae71241035054310003149926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40acd4c0b0cd5ce2481035054310002d499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8171a981799ab9c490103505431000304992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4098d4c09ccd5ce2490350543100028499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0b0cd5ce2481035054310002d49926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4070d4c074cd5ce249035054310001e499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423501635301733573892010350543100018499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa010600c6ae854008c014d5d09aba25002235013353014335738921035054310001549926135573ca00226ea8004448848cc00400c00844800484888c00c01084888c00801048880048004488880104888800c488880084888800480048c8c8c8cccd5cd19b8735573aa006900011999111998068018010009bae35742a0066eb8d5d0a8011bad357426ae8940088d4018d4c01ccd5ce2481035054310000849926135744a00226aae7940044dd5000893090009000911091998008020018011000889191800800911980198010010009991999111919191991199991111991199911191919191919991119911919191919199999111119191919191999111999111999999991111111199119999911111991191919191991199119911919999111199119911991199119911919191919191991199119191919191919191919999111199119191191919191919111191919191919192999a983d80510a9999a9831805099835199a8342839183f8009a9aa83d280311000998351991199ab9a3371200400211202110026603860bea00460506a604802444444444400260bea00a660d46601aa00a60c4002a66a610a026603aa010603e002210e0226605260be66026a010603e00260bea0042c2660d46603aa010603e002660d4666a0d0a0e46a6aa0f4a00c440020fa6601aa00a60c40022660d4666a0d0a0e46a6aa0f4a00c440020fa660d46601aa00a60c4002660d46601866026a010603e00260c4002660086a05460bea004a00642a6666a60c60142c2660d46601866026a010a00660c4002660d46605260420026046024660086042002603e00226603aa010603e0022c2a6666a60c40122a66a6108026644666ae68cdc4801000843808440098082800a40042a66a6a0ec605401026102022c442a66a6a0f000226106022c46442a66a6a0f600226a6aa0fc6a6aa0fca0044400444a666a61040200242660e26602800660d2002660e2660606a06260cc0066054032660e2666a0de0ca605000290011a9aa840809a9aa84080a80291000912999a98428080090b10b0999a83883399814980d2805a4004603400442c2660e0666a0dc0c86604c602ea0109001180b8011a9aa840009a9aa84000a80211000912999a98420080090998399980b001983580099839998191a8199834001981600d999a8388339815000a400442c2c4426110022c266aa0fa601200660120022a66a6a0ec605401026104022c4646442a66a6a0f40022a666a60fe6a6aa0faa0064400242660dc66022a00660cc002660dc6605a6a05c60c6a006604e02c666a0d80c4604a002900110b0b1109844008b09a9aa83da80091001098038008b0b0b0a99a9a8369a9816003911a981800111111111111982300500590980e9a981e000910008b0a99a9a83a191a98170009111111111001a802898390b110a99a9a83b0008801110983b0b1191919191299a98438099815803241012179fa042660d86605660c26602aa014a0226054a004660d86605660c26602aa0146a6aa0f8a020440046054a0066605660c26602aa014002605466044660446604400ca004a0066a6aaa050a0084440022660d86605660c26602aa014a0226054a00a6605660c26602aa01400260546604400ca00a26a6aaa04ca00444400626a6aaa04aa0024440042666aaa04a660e40046a6aaa048a01c444002660e40046a6aa0f0a01844002660e40046a60440204444444440062660e20026a6aaa046a01a44400426a6aa0eaa002440042a66a6a0e2604a006260e02c442a66a6a0e60022600600444260e82c46a60766a60720024440064466a60ae0044c4a66a6a0d86a607800844400242a66a6a0da646a605e0024444444444a66a6a0f0666aa609824002a09e46a6aa1080200244a66a612202666ae68cdc7801007849808490089a83e8018a83e001109a83d9a9aa84200800910008a83ca80311919190a99a9a8389999999aba400423333573466e1d40092004233335573ea0084a0ea46666aae7cd5d128029299a9a83a98389aba150062135078308501001150762507607307223333573466e1d400d2002233335573ea00a4a0ec46666aae7cd5d128031299a9a83b18391aba150072135079308701001150772507707407323333573466e1d40112000233335573ea00c46a0f0108024a0ee0e84a0ec9324c93128399283992839928398381099aa83f18108050008b09aab9d5002135573ca00226ea800458584d4c0980048800888cc07cccc158008d4c068020888888888024ccd417dc51a980d004111111111003800a4004446603c6660aa004602e00e666a0bce28d4c06401c8888888880180052002135301600422222222200413535550175001222003135301400222222222200523322300200132233200132001333550023233503b22333503a0030010023503700133503a22230033002001200122337000029001000a400060662400266466aa603a2400244a66a60f06006004266a0d60040022002a0d446a6aaa02e002444660bc666a0b8042602c00c006666a0b80a400290011919a800a834a835091199aa829911a9aa83700111199aa82b911a9aa83900111299a983f999ab9a3370e002900004080840008801899805199aaa81080300100080180180080080191199aa980d890009119aa98060900091a9aa8360009119aa83780119aa98078900091a9aa8378009119aa839001199a9aa80700091980a24000002446602a004002466028002900000099aa98060900091a9aa8360009119aa837801199a9aa805800919aa98080900091a9aa8380009119aa8398011aa80900080091199aaa805011801000919aa98080900091a9aa8380009119aa8398011aa808000800999aaa80280f001000a8341a980f8011111111111199aa981289000911a981d0011111a981f8019119a982d8011299a984300999ab9a3371e0260021100210e02266a0f200a00e200e400ea0e4012222444666aa603624002a0ce66aa60142400246a6aa0d40024466aa0da0046aa018002666aa603624002446a6aa0d600444a66a60f0666aa606c240026466a07844666a6a016006440040040026a6a0120024400266a01244a66a60f400420f820020f246a6aa0dc002446601400400a00c2006266a0d6008006a0d000266aa60142400246a6aa0d4002446466aa0dc006600200a640026aa0f444a66a6a0d600226aa0180064426a6aa0e000444a66a60fa66018004010266aa02200e0022600c00600424424660020060042400222424446006008224424446600400a00822424446002008224002640026aa0da442244a66a6a0c00022a0c444266a0c6600800466aa600c240020080024466e0000800488d4c05400888888888894cd4d4178ccd54c0c84800540d494cd4c1d4ccd5cd19b8f00c0010770761350610011506000321077107523530220012220022353062001222003223370200400246a60c000244400246a600600244444444401046a60040024444444440044444444442466666666600201401201000e00c00a0080060044002222444246660020080060042224002400244666ae68cdc400100082f8300900091a9802000911a98040011111111111299a9a8289980f005005909a9810000911a9812000911199aa980a09000911a98148011111a9817004111a98180029119299a983b99a9826802919a98270021299a983c999ab9a3371e0040020f60f42a00620f440f4466a609c00840f44a66a60f2666ae68cdc780100083d83d0a801883d099a83500500488048a99a9a83000190a99a9a8308011099a9825801119a9826001119a9828001119a9828801119812001000903e919a9828801103e91981200100091103e91119a9827002103e911299a983f199ab9a3370e00c006100020fe2a66a60fc666ae68cdc38028010400083f89982b802000883f883f883c0a99a9a8300009083c083c283080789931a982799ab9c4901024c6600050498c8004d5417088448894cd4d41400044008884cc014008ccd54c01c4800401401000488ccd5cd19b8f00200105c05b2212330010030022001222222222212333333333300100b00a0090080070060050040030022001122123300100300212001122123300100300212001122123300100300212001121222300300411222002112220011200122533335300f0012150372150372150372133355300a12001500d2353005001225335304f5335304f333573466e3cd4c06000888008d4c060010880081441404ccd5cd19b873530180022200135301800422001051050105013503b0031503a003221233001003002200122212333001004003002200122123300100300220013200135504522112225335350390011350060032213335009005300400233355300712001005004001123535004001220011235350030012200213350022253353502b002210031001502a12212330010030021200121222230040052122223003005212222300200521222230010052001221233001003002200121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012122300200322212233300100500400320012122300200321223001003200122333573466e1c0080040ac0a88ccc00800522100488100222323230010053200135502c223353501d0014800088d4d54088008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b223353501c0014800088d4d54084008894cd4c0b8ccd5cd19b8f00200703002f100113006003112232001320013550292253353501a0011003221330060023004001235301f0012220021222200412222003122220021222200120011200112001225335301d0021001101e2323232323333333574800a46666ae68cdc39aab9d5005480008cccd55cfa8029280691999aab9f50052500e233335573ea00a4a01e46666aae7cd5d128031299a9a807a99a9a807a99a9a80798061aba150092135012223330240030020011501021533535010300d35742a012426a02660040022a0222a02042a66a6a020646666666ae900049404c9404c9404c8d4050dd6801128098081aba150082135013300200115011150102501000d00c00b00a2500c4989402c9402c9402c9402c0204d5d1280089aba25001135573ca00226ea80048ccccccd5d20009280312803128031280311a8039bae00200312001200112122300200311220011200112253335300c0022153335300d00221330050020012130161613015162153335300d0022130161621330050020011301516153335300c001213015162130151610172253353014333573466e3cd4c03c008888008d4c03c0048880080580544ccd5cd19b8735300f00222200135300f00122200101601510152233223370600400266e080092014001262611220021221223300100400312001112212330010030021120012122230030042122230020041222001200122212333001004003002200126262612200212200120011123230010012233003300200200133223322332233333333300248811cd5e6bf0500378d4f0da4e8dde6becec7621cd8cbf5cbb9b87013d4cc0048811c6bec713b08a2d7c64baa3596d200b41b560850919d72e634944f2d520048810853706163654275640048810b5370616365427564426964003335550044891c826d9fafe1b3acf15bd250de69c04e3fc92c4493785939e069932e8900483001920e209335500648811c88269f8b051a739300fe743a7b315026f4614ce1216a4bb45d7fd0f500482209d20882748203db810920a09c012222222221233333333300100a00900800700600500400300220011112221233300100400300211120011122123300100300211200110482d866820181d866820083581c5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9443330353301d8668200800581840000d866820380821a004c4b401a77359400f5a1190195a10045d866820080")
5250        //     .unwrap()
5251        // ).unwrap();
5252        // println!("{:?}", tx.to_json());
5253        // assert_eq!(false, true);
5254        // based on tx 18565ab3c960c000531e5b359432397907d663c0ac5f5dbae80e1bf88d25c8a0 on mainnet
5255
5256        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        // not the real private key used for the tx on Cardano mainnet
5264        let private_key = PrivateKey::from_normal_bytes(
5265            &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
5266                .unwrap(),
5267        )
5268        .unwrap();
5269
5270        // add input
5271        {
5272            let required_signers = vec![
5273                // real tx was using 5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9 instead
5274                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        // add change output
5310        {
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        // add user output
5333        {
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        // add collateral
5345        {
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        // metadata
5372        {
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        // not the real private key used for the tx on Cardano mainnet
5412        let private_key = &PrivateKey::from_normal_bytes(
5413            &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
5414                .unwrap(),
5415        )
5416        .unwrap();
5417
5418        // add input
5419        {
5420            let required_signers = vec![
5421                // real tx was using 5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9 instead
5422                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        // add change output
5458        {
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        // add user output
5481        {
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        // add collateral
5493        {
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        // metadata
5520        {
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        // variant of the tx 18565ab3c960c000531e5b359432397907d663c0ac5f5dbae80e1bf88d25c8a0 on mainnet
5581
5582        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        // not the real private key used for the tx on Cardano mainnet
5590        let private_key = PrivateKey::from_normal_bytes(
5591            &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a")
5592                .unwrap(),
5593        )
5594        .unwrap();
5595
5596        // add input
5597        {
5598            let required_signers = vec![
5599                // real tx was using 5627217786eb781fbfb51911a253f4d250fdbfdcf1198e70d35985a9 instead
5600                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        // add change output
5636        {
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        // add user output
5659        {
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        // add collateral
5671        {
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        // metadata
5698        {
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        //add collateral
5762        {
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}