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: 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 match required_sig {
43 RequiredSignature::Witness(_) => {
44 return Err(TransactionError::SignatureRequest(
45 "Requested signature is not NativeEcdsa or None".to_string(),
46 ));
47 }
48 _ => {}
49 }
50
51 self.inputs.push(FinalInput {
52 partial_input: partial_input,
53 program_input: None,
54 issuance_input: None,
55 required_sig: required_sig,
56 });
57
58 Ok(())
59 }
60
61 pub fn add_program_input(
62 &mut self,
63 partial_input: PartialInput,
64 program_input: ProgramInput,
65 required_sig: RequiredSignature,
66 ) -> Result<(), TransactionError> {
67 match required_sig {
68 RequiredSignature::NativeEcdsa => {
69 return Err(TransactionError::SignatureRequest(
70 "Requested signature is not Witness or None".to_string(),
71 ));
72 }
73 _ => {}
74 }
75
76 self.inputs.push(FinalInput {
77 partial_input: partial_input,
78 program_input: Some(program_input),
79 issuance_input: None,
80 required_sig: required_sig,
81 });
82
83 Ok(())
84 }
85
86 pub fn add_issuance_input(
87 &mut self,
88 partial_input: PartialInput,
89 issuance_input: IssuanceInput,
90 required_sig: RequiredSignature,
91 ) -> Result<AssetId, TransactionError> {
92 match required_sig {
93 RequiredSignature::Witness(_) => {
94 return Err(TransactionError::SignatureRequest(
95 "Requested signature is not NativeEcdsa or None".to_string(),
96 ));
97 }
98 _ => {}
99 }
100
101 let asset_id = AssetId::from_entropy(asset_entropy(&partial_input.outpoint(), issuance_input.asset_entropy));
102
103 self.inputs.push(FinalInput {
104 partial_input: partial_input,
105 program_input: None,
106 issuance_input: Some(issuance_input),
107 required_sig: required_sig,
108 });
109
110 Ok(asset_id)
111 }
112
113 pub fn add_program_issuance_input(
114 &mut self,
115 partial_input: PartialInput,
116 program_input: ProgramInput,
117 issuance_input: IssuanceInput,
118 required_sig: RequiredSignature,
119 ) -> Result<AssetId, TransactionError> {
120 match required_sig {
121 RequiredSignature::NativeEcdsa => {
122 return Err(TransactionError::SignatureRequest(
123 "Requested signature is not Witness or None".to_string(),
124 ));
125 }
126 _ => {}
127 }
128
129 let asset_id = AssetId::from_entropy(asset_entropy(&partial_input.outpoint(), issuance_input.asset_entropy));
130
131 self.inputs.push(FinalInput {
132 partial_input: partial_input,
133 program_input: Some(program_input),
134 issuance_input: Some(issuance_input),
135 required_sig: required_sig,
136 });
137
138 Ok(asset_id)
139 }
140
141 pub fn remove_input(&mut self, index: usize) -> Option<FinalInput> {
142 if self.inputs.get(index).is_some() {
143 return Some(self.inputs.remove(index));
144 }
145
146 None
147 }
148
149 pub fn add_output(&mut self, partial_output: PartialOutput) {
150 self.outputs.push(partial_output);
151 }
152
153 pub fn remove_output(&mut self, index: usize) -> Option<PartialOutput> {
154 if self.outputs.get(index).is_some() {
155 return Some(self.outputs.remove(index));
156 }
157
158 None
159 }
160
161 pub fn inputs(&self) -> &[FinalInput] {
162 &self.inputs
163 }
164
165 pub fn inputs_mut(&mut self) -> &mut [FinalInput] {
166 &mut self.inputs
167 }
168
169 pub fn outputs(&self) -> &[PartialOutput] {
170 &self.outputs
171 }
172
173 pub fn outputs_mut(&mut self) -> &mut [PartialOutput] {
174 &mut self.outputs
175 }
176
177 pub fn n_inputs(&self) -> usize {
178 self.inputs.len()
179 }
180
181 pub fn n_outputs(&self) -> usize {
182 self.outputs.len()
183 }
184
185 pub fn calculate_fee_delta(&self) -> i64 {
186 let available_amount = self
187 .inputs
188 .iter()
189 .filter(|input| input.partial_input.asset.clone().unwrap() == self.network.policy_asset())
190 .fold(0 as u64, |acc, input| acc + input.partial_input.amount.clone().unwrap());
191
192 let consumed_amount = self
193 .outputs
194 .iter()
195 .filter(|output| output.asset == self.network.policy_asset())
196 .fold(0 as u64, |acc, output| acc + output.amount);
197
198 available_amount as i64 - consumed_amount as i64
199 }
200
201 pub fn calculate_fee(&self, weight: usize, fee_rate: f32) -> u64 {
202 let vsize = weight.div_ceil(WITNESS_SCALE_FACTOR);
203
204 (vsize as f32 * fee_rate / 1000.0).ceil() as u64
205 }
206
207 pub fn extract_pst(&self) -> PartiallySignedTransaction {
208 let mut pst = PartiallySignedTransaction::new_v2();
209
210 self.inputs.iter().for_each(|el| {
211 let mut input = el.partial_input.input();
212
213 if el.issuance_input.is_some() {
215 let issue = el.issuance_input.clone().unwrap().input();
216
217 input.issuance_value_amount = issue.issuance_value_amount;
218 input.issuance_asset_entropy = issue.issuance_asset_entropy;
219 input.issuance_inflation_keys = issue.issuance_inflation_keys;
220 input.blinded_issuance = issue.blinded_issuance;
221 }
222
223 pst.add_input(input);
224 });
225
226 self.outputs.iter().for_each(|el| {
227 pst.add_output(el.to_output());
228 });
229
230 pst
231 }
232}