whisky_csl/tx_parser/
outputs.rs1use cardano_serialization_lib::{self as csl};
2use whisky_common::*;
3
4use super::CSLParser;
5
6impl CSLParser {
7 pub fn get_outputs(&self) -> &Vec<Output> {
8 &self.tx_body.outputs
9 }
10
11 pub fn extract_output_utxos(tx_hex: &str) -> Result<Vec<UTxO>, WError> {
12 let tx = csl::FixedTransaction::from_hex(tx_hex)
13 .map_err(WError::from_err("extract_output_utxos"))?;
14 let outputs = tx.body().outputs();
15 let tx_hash = tx.transaction_hash().to_hex();
16 let whisky_outputs = csl_outputs_to_outputs(&outputs)?;
17 let mut output_utxos = Vec::new();
18 for (index, output) in whisky_outputs.iter().enumerate() {
19 output_utxos.push(output_to_utxo(output, &tx_hash, index as u32)?);
20 }
21 Ok(output_utxos)
22 }
23
24 pub fn extract_output_cbors(tx_hex: &str) -> Result<Vec<String>, WError> {
25 let mut output_cbors = Vec::new();
26 let csl_tx = csl::FixedTransaction::from_hex(tx_hex)
27 .map_err(WError::from_err("extract_output_cbors"))?;
28 let csl_outputs = csl_tx.body().outputs();
29 let len = csl_outputs.len();
30 for i in 0..len {
31 let output = csl_outputs.get(i);
32 output_cbors.push(output.to_hex());
33 }
34 Ok(output_cbors)
35 }
36
37 pub(super) fn extract_outputs(&mut self) -> Result<(), WError> {
38 let outputs = self.csl_tx_body.outputs();
39 self.tx_body.outputs = csl_outputs_to_outputs(&outputs)?;
40 Ok(())
41 }
42}
43
44fn output_to_utxo(output: &Output, tx_hash: &String, index: u32) -> Result<UTxO, WError> {
45 let datum = match &output.datum {
46 Some(Datum::Inline(s)) => Some(s.clone()),
47 Some(Datum::Embedded(s)) => Some(s.clone()),
48 _ => None,
49 };
50 let data_hash = match &output.datum {
51 Some(Datum::Hash(s)) => Some(s.clone()),
52 _ => None,
53 };
54 let script_ref = match &output.reference_script {
55 Some(script) => Some(output_reference_script_to_script_source(&script)?),
56 None => None,
57 };
58 let script_hash = match &script_ref {
59 Some(script) => {
60 if script.is_native_script() {
61 Some(script.native_script().unwrap().hash().to_hex())
62 } else {
63 Some(script.plutus_script().unwrap().hash().to_hex())
64 }
65 }
66 None => None,
67 };
68 Ok(UTxO {
69 input: UtxoInput {
70 tx_hash: tx_hash.clone(),
71 output_index: index as u32,
72 },
73 output: UtxoOutput {
74 address: output.address.clone(),
75 amount: output.amount.clone(),
76 data_hash,
77 plutus_data: datum,
78 script_ref: script_ref.map(|script| script.to_hex()),
79 script_hash: script_hash,
80 },
81 })
82}
83
84fn output_reference_script_to_script_source(
85 output_reference_script: &OutputScriptSource,
86) -> Result<csl::ScriptRef, WError> {
87 match output_reference_script {
88 OutputScriptSource::ProvidedScriptSource(script) => {
89 let language_version = match script.language_version {
90 LanguageVersion::V1 => csl::Language::new_plutus_v1(),
91 LanguageVersion::V2 => csl::Language::new_plutus_v2(),
92 LanguageVersion::V3 => csl::Language::new_plutus_v3(),
93 };
94 let script_ref = csl::ScriptRef::new_plutus_script(
95 &csl::PlutusScript::from_hex_with_version(&script.script_cbor, &language_version)
96 .map_err(|e| {
97 WError::new(
98 "output_reference_script_to_script_source",
99 &format!("Failed to convert script to plutus script: {:?}", e),
100 )
101 })?,
102 );
103 Ok(script_ref)
104 }
105 OutputScriptSource::ProvidedSimpleScriptSource(script) => {
106 let script_ref = csl::ScriptRef::new_native_script(
107 &csl::NativeScript::from_hex(&script.script_cbor).map_err(|e| {
108 WError::new(
109 "output_reference_script_to_script_source",
110 &format!("Failed to convert script to native script: {:?}", e),
111 )
112 })?,
113 );
114 Ok(script_ref)
115 }
116 }
117}
118
119fn csl_outputs_to_outputs(outputs: &csl::TransactionOutputs) -> Result<Vec<Output>, WError> {
120 let mut result = Vec::new();
121
122 for i in 0..outputs.len() {
123 let output = outputs.get(i);
124 let mut value: Vec<Asset> = vec![];
125
126 value.push(Asset::new_from_str(
127 "lovelace",
128 &output.amount().coin().to_str(),
129 ));
130
131 if let Some(multi_asset) = output.amount().multiasset() {
132 for policy_id_index in 0..multi_asset.keys().len() {
133 let policy_id = multi_asset.keys().get(policy_id_index);
134 let assets = multi_asset.get(&policy_id).ok_or_else(|| {
135 WError::new(
136 "csl_outputs_to_outputs",
137 &format!("Failed to get assets for policy ID: {}", policy_id.to_hex()),
138 )
139 })?;
140 for asset_index in 0..assets.keys().len() {
141 let asset_name = assets.keys().get(asset_index);
142 let asset_quantity = assets.get(&asset_name).ok_or_else(|| {
143 WError::new(
144 "csl_outputs_to_outputs",
145 &format!(
146 "Failed to get quantity for asset: {}",
147 asset_name.to_string()
148 ),
149 )
150 })?;
151 let asset_name_hex = hex::encode(asset_name.name());
152 let concated_name = policy_id.to_hex() + &asset_name_hex;
153
154 value.push(Asset::new_from_str(
155 &concated_name,
156 &asset_quantity.to_str(),
157 ));
158 }
159 }
160 }
161
162 let datum: Option<Datum> = if let Some(csl_datum) = output.plutus_data() {
163 Some(Datum::Inline(csl_datum.to_hex()))
164 } else {
165 output
166 .data_hash()
167 .map(|csl_datum_hash| Datum::Hash(csl_datum_hash.to_hex()))
168 };
169
170 let reference_script: Option<OutputScriptSource> = match output.script_ref() {
171 Some(csl_script_ref) => {
172 if let Some(plutus_script) = csl_script_ref.plutus_script() {
173 let language_version = match plutus_script.language_version().kind() {
174 csl::LanguageKind::PlutusV1 => LanguageVersion::V1,
175 csl::LanguageKind::PlutusV2 => LanguageVersion::V2,
176 csl::LanguageKind::PlutusV3 => LanguageVersion::V3,
177 };
178 Some(OutputScriptSource::ProvidedScriptSource(
179 ProvidedScriptSource {
180 script_cbor: plutus_script.to_hex(),
181 language_version,
182 },
183 ))
184 } else if let Some(native_script) = csl_script_ref.native_script() {
185 Some(OutputScriptSource::ProvidedSimpleScriptSource(
186 ProvidedSimpleScriptSource {
187 script_cbor: native_script.to_hex(),
188 },
189 ))
190 } else {
191 None
192 }
193 }
194 None => None,
195 };
196
197 result.push(Output {
198 address: output.address().to_bech32(None).map_err(|e| {
199 WError::new(
200 "csl_outputs_to_outputs",
201 &format!("Failed to convert address to bech32: {:?}", e),
202 )
203 })?,
204 amount: value,
205 datum,
206 reference_script,
207 });
208 }
209 Ok(result)
210}