cardano_serialization_lib/builders/
withdrawals_builder.rs

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