smplx_sdk/transaction/
final_transaction.rs1use simplicityhl::elements::AssetId;
2use simplicityhl::elements::pset::PartiallySignedTransaction;
3
4use crate::provider::SimplicityNetwork;
5use crate::utils::asset_entropy;
6
7use super::error::TransactionError;
8use super::partial_input::{IssuanceInput, PartialInput, ProgramInput, RequiredSignature};
9use super::partial_output::PartialOutput;
10
11pub const WITNESS_SCALE_FACTOR: usize = 4;
12
13#[derive(Clone)]
14pub struct FinalInput {
15 pub partial_input: PartialInput,
16 pub program_input: Option<ProgramInput>,
17 pub issuance_input: Option<IssuanceInput>,
18 pub required_sig: RequiredSignature,
19}
20
21#[derive(Clone)]
22pub struct FinalTransaction {
23 pub network: SimplicityNetwork,
24 inputs: Vec<FinalInput>,
25 outputs: Vec<PartialOutput>,
26}
27
28impl FinalTransaction {
29 pub fn new(network: SimplicityNetwork) -> Self {
30 Self {
31 network,
32 inputs: Vec::new(),
33 outputs: Vec::new(),
34 }
35 }
36
37 pub fn add_input(
38 &mut self,
39 partial_input: PartialInput,
40 required_sig: RequiredSignature,
41 ) -> Result<(), TransactionError> {
42 if let RequiredSignature::Witness(_) = required_sig {
43 return Err(TransactionError::SignatureRequest(
44 "Requested signature is not NativeEcdsa or None".to_string(),
45 ));
46 }
47
48 self.inputs.push(FinalInput {
49 partial_input,
50 program_input: None,
51 issuance_input: None,
52 required_sig,
53 });
54
55 Ok(())
56 }
57
58 pub fn add_program_input(
59 &mut self,
60 partial_input: PartialInput,
61 program_input: ProgramInput,
62 required_sig: RequiredSignature,
63 ) -> Result<(), TransactionError> {
64 if let RequiredSignature::NativeEcdsa = required_sig {
65 return Err(TransactionError::SignatureRequest(
66 "Requested signature is not Witness or None".to_string(),
67 ));
68 }
69
70 self.inputs.push(FinalInput {
71 partial_input,
72 program_input: Some(program_input),
73 issuance_input: None,
74 required_sig,
75 });
76
77 Ok(())
78 }
79
80 pub fn add_issuance_input(
81 &mut self,
82 partial_input: PartialInput,
83 issuance_input: IssuanceInput,
84 required_sig: RequiredSignature,
85 ) -> Result<AssetId, TransactionError> {
86 if let RequiredSignature::Witness(_) = required_sig {
87 return Err(TransactionError::SignatureRequest(
88 "Requested signature is not NativeEcdsa or None".to_string(),
89 ));
90 }
91
92 let asset_id = AssetId::from_entropy(asset_entropy(&partial_input.outpoint(), issuance_input.asset_entropy));
93
94 self.inputs.push(FinalInput {
95 partial_input,
96 program_input: None,
97 issuance_input: Some(issuance_input),
98 required_sig,
99 });
100
101 Ok(asset_id)
102 }
103
104 pub fn add_program_issuance_input(
105 &mut self,
106 partial_input: PartialInput,
107 program_input: ProgramInput,
108 issuance_input: IssuanceInput,
109 required_sig: RequiredSignature,
110 ) -> Result<AssetId, TransactionError> {
111 if let RequiredSignature::NativeEcdsa = required_sig {
112 return Err(TransactionError::SignatureRequest(
113 "Requested signature is not Witness or None".to_string(),
114 ));
115 }
116
117 let asset_id = AssetId::from_entropy(asset_entropy(&partial_input.outpoint(), issuance_input.asset_entropy));
118
119 self.inputs.push(FinalInput {
120 partial_input,
121 program_input: Some(program_input),
122 issuance_input: Some(issuance_input),
123 required_sig,
124 });
125
126 Ok(asset_id)
127 }
128
129 pub fn remove_input(&mut self, index: usize) -> Option<FinalInput> {
130 if self.inputs.get(index).is_some() {
131 return Some(self.inputs.remove(index));
132 }
133
134 None
135 }
136
137 pub fn add_output(&mut self, partial_output: PartialOutput) {
138 self.outputs.push(partial_output);
139 }
140
141 pub fn remove_output(&mut self, index: usize) -> Option<PartialOutput> {
142 if self.outputs.get(index).is_some() {
143 return Some(self.outputs.remove(index));
144 }
145
146 None
147 }
148
149 pub fn inputs(&self) -> &[FinalInput] {
150 &self.inputs
151 }
152
153 pub fn inputs_mut(&mut self) -> &mut [FinalInput] {
154 &mut self.inputs
155 }
156
157 pub fn outputs(&self) -> &[PartialOutput] {
158 &self.outputs
159 }
160
161 pub fn outputs_mut(&mut self) -> &mut [PartialOutput] {
162 &mut self.outputs
163 }
164
165 pub fn n_inputs(&self) -> usize {
166 self.inputs.len()
167 }
168
169 pub fn n_outputs(&self) -> usize {
170 self.outputs.len()
171 }
172
173 pub fn calculate_fee_delta(&self) -> i64 {
174 let available_amount = self
175 .inputs
176 .iter()
177 .filter(|input| input.partial_input.asset.unwrap() == self.network.policy_asset())
178 .fold(0_u64, |acc, input| acc + input.partial_input.amount.unwrap());
179
180 let consumed_amount = self
181 .outputs
182 .iter()
183 .filter(|output| output.asset == self.network.policy_asset())
184 .fold(0_u64, |acc, output| acc + output.amount);
185
186 available_amount as i64 - consumed_amount as i64
187 }
188
189 pub fn calculate_fee(&self, weight: usize, fee_rate: f32) -> u64 {
190 let vsize = weight.div_ceil(WITNESS_SCALE_FACTOR);
191
192 (vsize as f32 * fee_rate / 1000.0).ceil() as u64
193 }
194
195 pub fn extract_pst(&self) -> PartiallySignedTransaction {
196 let mut pst = PartiallySignedTransaction::new_v2();
197
198 self.inputs.iter().for_each(|el| {
199 let mut input = el.partial_input.input();
200
201 if el.issuance_input.is_some() {
203 let issue = el.issuance_input.clone().unwrap().input();
204
205 input.issuance_value_amount = issue.issuance_value_amount;
206 input.issuance_asset_entropy = issue.issuance_asset_entropy;
207 input.issuance_inflation_keys = issue.issuance_inflation_keys;
208 input.blinded_issuance = issue.blinded_issuance;
209 }
210
211 pst.add_input(input);
212 });
213
214 self.outputs.iter().for_each(|el| {
215 pst.add_output(el.to_output());
216 });
217
218 pst
219 }
220}