cardano_serialization_lib/builders/
output_builder.rs

1use crate::*;
2
3/// We introduce a builder-pattern format for creating transaction outputs
4/// This is because:
5/// 1. Some fields (i.e. data hash) are optional, and we can't easily expose Option<> in WASM
6/// 2. Some fields like amounts have many ways it could be set (some depending on other field values being known)
7/// 3. Easier to adapt as the output format gets more complicated in future Cardano releases
8
9#[wasm_bindgen]
10#[derive(Clone, Debug)]
11pub struct TransactionOutputBuilder {
12    address: Option<Address>,
13    data: Option<DataOption>,
14    script_ref: Option<ScriptRef>,
15}
16
17#[wasm_bindgen]
18impl TransactionOutputBuilder {
19    pub fn new() -> Self {
20        Self {
21            address: None,
22            data: None,
23            script_ref: None,
24        }
25    }
26
27    pub fn with_address(&self, address: &Address) -> Self {
28        let mut cfg = self.clone();
29        cfg.address = Some(address.clone());
30        cfg
31    }
32
33    pub fn with_data_hash(&self, data_hash: &DataHash) -> Self {
34        let mut cfg = self.clone();
35        cfg.data = Some(DataOption::DataHash(data_hash.clone()));
36        cfg
37    }
38
39    pub fn with_plutus_data(&self, data: &PlutusData) -> Self {
40        let mut cfg = self.clone();
41        cfg.data = Some(DataOption::Data(data.clone()));
42        cfg
43    }
44
45    pub fn with_script_ref(&self, script_ref: &ScriptRef) -> Self {
46        let mut cfg = self.clone();
47        cfg.script_ref = Some(script_ref.clone());
48        cfg
49    }
50
51    pub fn next(&self) -> Result<TransactionOutputAmountBuilder, JsError> {
52        Ok(TransactionOutputAmountBuilder {
53            address: self.address.clone().ok_or(JsError::from_str(
54                "TransactionOutputBaseBuilder: Address missing",
55            ))?,
56            amount: None,
57            data: self.data.clone(),
58            script_ref: self.script_ref.clone(),
59        })
60    }
61}
62
63#[wasm_bindgen]
64#[derive(Clone, Debug)]
65pub struct TransactionOutputAmountBuilder {
66    address: Address,
67    amount: Option<Value>,
68    data: Option<DataOption>,
69    script_ref: Option<ScriptRef>,
70}
71
72#[wasm_bindgen]
73impl TransactionOutputAmountBuilder {
74    pub fn with_value(&self, amount: &Value) -> Self {
75        let mut cfg = self.clone();
76        cfg.amount = Some(amount.clone());
77        cfg
78    }
79
80    pub fn with_coin(&self, coin: &Coin) -> Self {
81        let mut cfg = self.clone();
82
83        cfg.amount = Some(Value::new(coin));
84        cfg
85    }
86
87    pub fn with_coin_and_asset(&self, coin: &Coin, multiasset: &MultiAsset) -> Self {
88        let mut cfg = self.clone();
89
90        let mut val = Value::new(coin);
91        val.set_multiasset(multiasset);
92        cfg.amount = Some(val.clone());
93        cfg
94    }
95
96    pub fn with_asset_and_min_required_coin_by_utxo_cost(
97        &self,
98        multiasset: &MultiAsset,
99        data_cost: &DataCost,
100    ) -> Result<TransactionOutputAmountBuilder, JsError> {
101        // TODO: double ada calculation needs to check if it redundant
102        let mut calc = MinOutputAdaCalculator::new_empty(data_cost)?;
103        if let Some(data) = &self.data {
104            match data {
105                DataOption::DataHash(data_hash) => calc.set_data_hash(data_hash),
106                DataOption::Data(datum) => calc.set_plutus_data(datum),
107            };
108        }
109        if let Some(script_ref) = &self.script_ref {
110            calc.set_script_ref(script_ref);
111        }
112        let min_possible_coin = calc.calculate_ada()?;
113        let mut value = Value::new(&min_possible_coin);
114        value.set_multiasset(multiasset);
115
116        let mut calc = MinOutputAdaCalculator::new_empty(data_cost)?;
117        calc.set_amount(&value);
118        if let Some(data) = &self.data {
119            match data {
120                DataOption::DataHash(data_hash) => calc.set_data_hash(data_hash),
121                DataOption::Data(datum) => calc.set_plutus_data(datum),
122            };
123        }
124        if let Some(script_ref) = &self.script_ref {
125            calc.set_script_ref(script_ref);
126        }
127        let required_coin = calc.calculate_ada()?;
128
129        Ok(self.with_coin_and_asset(&required_coin, &multiasset))
130    }
131
132    pub fn build(&self) -> Result<TransactionOutput, JsError> {
133        Ok(TransactionOutput {
134            address: self.address.clone(),
135            amount: self.amount.clone().ok_or(JsError::from_str(
136                "TransactionOutputAmountBuilder: amount missing",
137            ))?,
138            plutus_data: self.data.clone(),
139            script_ref: self.script_ref.clone(),
140            serialization_format: None,
141        })
142    }
143}