cardano_serialization_lib/builders/
withdrawals_builder.rs

1use std::collections::BTreeMap;
2use crate::*;
3
4#[wasm_bindgen]
5#[derive(Clone, Debug)]
6pub struct WithdrawalsBuilder {
7    withdrawals: BTreeMap<RewardAddress, (Coin, Option<ScriptWitnessType>)>,
8}
9
10#[wasm_bindgen]
11impl WithdrawalsBuilder {
12    pub fn new() -> Self {
13        Self {
14            withdrawals: BTreeMap::new(),
15        }
16    }
17
18    pub fn add(&mut self, address: &RewardAddress, coin: &Coin) -> Result<(), JsError> {
19        if address.payment_cred().has_script_hash() {
20            return Err(JsError::from_str(
21                "Your address has a required script witness.\
22                Please use .add_with_plutus_witness or .add_with_native_script instead.",
23            ));
24        }
25
26        self.withdrawals
27            .insert(address.clone(), (coin.clone(), None));
28
29        Ok(())
30    }
31
32    pub fn add_with_plutus_witness(
33        &mut self,
34        address: &RewardAddress,
35        coin: &Coin,
36        witness: &PlutusWitness,
37    ) -> Result<(), JsError> {
38        if !address.payment_cred().has_script_hash() {
39            return Err(JsError::from_str(
40                "Your address does not have a required script witness.\
41                Please use .add instead.",
42            ));
43        }
44
45        self.withdrawals.insert(
46            address.clone(),
47            (
48                coin.clone(),
49                Some(ScriptWitnessType::PlutusScriptWitness(witness.clone())),
50            ),
51        );
52        Ok(())
53    }
54
55    pub fn add_with_native_script(
56        &mut self,
57        address: &RewardAddress,
58        coin: &Coin,
59        native_script_source: &NativeScriptSource,
60    ) -> Result<(), JsError> {
61        if !address.payment_cred().has_script_hash() {
62            return Err(JsError::from_str(
63                "Your address does not have a required script witness.\
64                Please use .add instead.",
65            ));
66        }
67
68        self.withdrawals.insert(
69            address.clone(),
70            (
71                coin.clone(),
72                Some(ScriptWitnessType::NativeScriptWitness(
73                    native_script_source.0.clone(),
74                )),
75            ),
76        );
77        Ok(())
78    }
79
80    pub(crate) fn get_required_signers(&self) -> Ed25519KeyHashes {
81        let mut set = Ed25519KeyHashes::new();
82        for (address, (_, script_wit)) in &self.withdrawals {
83            let req_signature = address.payment_cred().to_keyhash();
84            if let Some(req_signature) = req_signature {
85                set.add_move(req_signature);
86            }
87
88            if let Some(script_wit) = script_wit {
89                if let Some(script_wit) = script_wit.get_required_signers() {
90                    set.extend_move(script_wit);
91                }
92            }
93        }
94        set
95    }
96
97    pub fn get_plutus_witnesses(&self) -> PlutusWitnesses {
98        let tag = RedeemerTag::new_reward();
99        let mut scripts = PlutusWitnesses::new();
100        for (i, (_, (_, script_wit))) in self.withdrawals.iter().enumerate() {
101            if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit {
102                let index = BigNum::from(i);
103                scripts.add(&s.clone_with_redeemer_index_and_tag(&index, &tag));
104            }
105        }
106        scripts
107    }
108
109    pub fn get_ref_inputs(&self) -> TransactionInputs {
110        let mut inputs = Vec::new();
111        for (_, (_, script_wit)) in self.withdrawals.iter() {
112            match script_wit {
113                Some(script_witness) => {
114                    if let Some(input) = script_witness.get_script_ref_input() {
115                        inputs.push(input);
116                    }
117                    if let Some(input) = script_witness.get_datum_ref_input() {
118                        inputs.push(input);
119                    }
120                }
121                None => {}
122            }
123        }
124        TransactionInputs::from_vec(inputs)
125    }
126
127    pub fn get_native_scripts(&self) -> NativeScripts {
128        let mut scripts = NativeScripts::new();
129        for (_, (_, script_wit)) in self.withdrawals.iter() {
130            if let Some(ScriptWitnessType::NativeScriptWitness(
131                NativeScriptSourceEnum::NativeScript(script, _),
132            )) = script_wit
133            {
134                scripts.add(script);
135            }
136        }
137        scripts
138    }
139
140    pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet<Language> {
141        let mut used_langs = BTreeSet::new();
142        for (_, (_, script_wit)) in &self.withdrawals {
143            if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit {
144                used_langs.insert(s.script.language());
145            }
146        }
147        used_langs
148    }
149
150    pub fn get_total_withdrawals(&self) -> Result<Value, JsError> {
151        let mut total = Coin::zero();
152        for (_, (coin, _)) in &self.withdrawals {
153            total = total.checked_add(coin)?;
154        }
155        Ok(Value::new(&total))
156    }
157
158    pub fn has_plutus_scripts(&self) -> bool {
159        for (_, (_, script_wit)) in &self.withdrawals {
160            if let Some(ScriptWitnessType::PlutusScriptWitness(_)) = script_wit {
161                return true;
162            }
163        }
164        false
165    }
166
167    //return only ref inputs that are script refs with added size
168    //used for calculating the fee for the transaction
169    //another ref input and also script ref input without size are filtered out
170    pub(crate) fn get_script_ref_inputs_with_size(
171        &self,
172    ) -> impl Iterator<Item = (&TransactionInput, usize)> {
173        self.withdrawals.iter()
174            .filter_map(|(_, (_, script_wit))| script_wit.as_ref())
175            .filter_map(|script_wit| script_wit.get_script_ref_input_with_size())
176    }
177
178    pub fn build(&self) -> Withdrawals {
179        let map = self
180            .withdrawals
181            .iter()
182            .map(|(k, (v, _))| (k.clone(), v.clone()))
183            .collect();
184        Withdrawals(map)
185    }
186}