1use num_bigint::BigInt;
4use plutus_ledger_api::v3::{
5 address::Credential,
6 assoc_map::AssocMap,
7 crypto::{LedgerBytes, PaymentPubKeyHash},
8 datum::{Datum, DatumHash},
9 interval::Interval,
10 redeemer::Redeemer,
11 transaction::{
12 GovernanceActionId, POSIXTimeRange, ProposalProcedure, ScriptPurpose, TransactionHash,
13 TransactionInfo, TransactionInput, TransactionOutput, TxCert, TxInInfo, Vote, Voter,
14 },
15 value::{AssetClass, Lovelace, Value},
16};
17use std::collections::{BTreeMap, BTreeSet};
18
19pub struct TxScaffold {
21 inputs: BTreeMap<TransactionInput, TxScaffoldInput>,
22 reference_inputs: BTreeMap<TransactionInput, TransactionOutput>,
23 outputs: Vec<TransactionOutput>,
24 mint: Vec<(AssetClass, i64, Redeemer)>,
25 tx_certs: Vec<TxCert>,
26 withdrawals: BTreeMap<Credential, u64>,
27 valid_range: POSIXTimeRange,
28 signatories: BTreeSet<PaymentPubKeyHash>,
29 votes: BTreeMap<Voter, BTreeMap<GovernanceActionId, Vote>>,
30 proposal_procedures: Vec<ProposalProcedure>,
31 current_treasury_amount: Option<BigInt>,
32 treasury_donation: Option<BigInt>,
33}
34
35pub enum TxScaffoldInput {
37 PubKeyInput { output: TransactionOutput },
39 ScriptInput {
41 output: TransactionOutput,
42 datum: Option<DatumFromWitness>,
43 redeemer: Redeemer,
44 },
45}
46
47pub type DatumFromWitness = (DatumHash, Datum);
49
50impl TxScaffoldInput {
51 fn output(&self) -> TransactionOutput {
52 match self {
53 TxScaffoldInput::PubKeyInput { output } => output.clone(),
54 TxScaffoldInput::ScriptInput { output, .. } => output.clone(),
55 }
56 }
57}
58
59impl Default for TxScaffold {
60 fn default() -> Self {
61 TxScaffold {
62 inputs: BTreeMap::new(),
63 reference_inputs: BTreeMap::new(),
64 outputs: Vec::new(),
65 mint: Vec::new(),
66 tx_certs: Vec::new(),
67 withdrawals: BTreeMap::new(),
68 valid_range: Interval::Always.into(),
69 signatories: BTreeSet::new(),
70 votes: BTreeMap::new(),
71 proposal_procedures: Vec::new(),
72 current_treasury_amount: None,
73 treasury_donation: None,
74 }
75 }
76}
77
78impl TxScaffold {
79 pub fn new() -> Self {
81 Self::default()
82 }
83
84 pub fn add_input(mut self, reference: TransactionInput, input: TxScaffoldInput) -> Self {
86 self.inputs.insert(reference, input);
87 self
88 }
89
90 pub fn add_pub_key_input(
92 mut self,
93 reference: TransactionInput,
94 output: TransactionOutput,
95 ) -> Self {
96 self.inputs
97 .insert(reference, TxScaffoldInput::PubKeyInput { output });
98 self
99 }
100
101 pub fn add_script_input(
103 mut self,
104 reference: TransactionInput,
105 output: TransactionOutput,
106 datum: Option<DatumFromWitness>,
107 redeemer: Redeemer,
108 ) -> Self {
109 self.inputs.insert(
110 reference,
111 TxScaffoldInput::ScriptInput {
112 output,
113 datum,
114 redeemer,
115 },
116 );
117 self
118 }
119
120 pub fn add_inputs(mut self, mut inputs: BTreeMap<TransactionInput, TxScaffoldInput>) -> Self {
122 self.inputs.append(&mut inputs);
123 self
124 }
125
126 pub fn add_reference_input(
128 mut self,
129 reference: TransactionInput,
130 output: TransactionOutput,
131 ) -> Self {
132 self.reference_inputs.insert(reference, output);
133 self
134 }
135
136 pub fn add_reference_inputs(
138 mut self,
139 mut inputs: BTreeMap<TransactionInput, TransactionOutput>,
140 ) -> Self {
141 self.reference_inputs.append(&mut inputs);
142 self
143 }
144
145 pub fn add_output(mut self, output: TransactionOutput) -> Self {
148 self.outputs.push(output);
149 self
150 }
151
152 pub fn add_outputs(mut self, mut outputs: Vec<TransactionOutput>) -> Self {
154 self.outputs.append(&mut outputs);
155 self
156 }
157
158 pub fn add_mint(mut self, asset_class: AssetClass, amount: i64, redeemer: Redeemer) -> Self {
160 self.mint.push((asset_class, amount, redeemer));
161 self
162 }
163
164 pub fn add_txcert(mut self, dcert: TxCert) -> Self {
166 self.tx_certs.push(dcert);
167 self
168 }
169
170 pub fn add_dcerts(mut self, mut dcerts: Vec<TxCert>) -> Self {
172 self.tx_certs.append(&mut dcerts);
173 self
174 }
175
176 pub fn add_withdrawals(mut self, mut withdrawals: BTreeMap<Credential, u64>) -> Self {
178 self.withdrawals.append(&mut withdrawals);
179 self
180 }
181
182 pub fn add_withdrawal(mut self, staking_credential: Credential, amount: u64) -> Self {
184 self.withdrawals.insert(staking_credential, amount);
185 self
186 }
187
188 pub fn set_valid_range(mut self, valid_range: POSIXTimeRange) -> Self {
190 self.valid_range = valid_range;
191 self
192 }
193
194 pub fn add_signatory(mut self, signatory: PaymentPubKeyHash) -> Self {
196 self.signatories.insert(signatory);
197 self
198 }
199
200 pub fn add_signatories(mut self, mut signatories: BTreeSet<PaymentPubKeyHash>) -> Self {
202 self.signatories.append(&mut signatories);
203 self
204 }
205
206 pub fn add_votes(
207 mut self,
208 mut votes: BTreeMap<Voter, BTreeMap<GovernanceActionId, Vote>>,
209 ) -> Self {
210 self.votes.append(&mut votes);
211 self
212 }
213
214 pub fn add_proposal_procedures(
215 mut self,
216 mut proposal_procedures: Vec<ProposalProcedure>,
217 ) -> Self {
218 self.proposal_procedures.append(&mut proposal_procedures);
219 self
220 }
221
222 pub fn add_current_treasury_amount(mut self, current_treasury_amount: Option<BigInt>) -> Self {
223 self.current_treasury_amount = current_treasury_amount;
224 self
225 }
226 pub fn add_treasury_donation(mut self, treasury_donation: Option<BigInt>) -> Self {
227 self.treasury_donation = treasury_donation;
228 self
229 }
230
231 pub fn build(self) -> TransactionInfo {
233 TransactionInfo {
234 inputs: self
235 .inputs
236 .iter()
237 .map(|(reference, scaffold_in)| {
238 TxInInfo {
239 reference: reference.clone(),
240 output: scaffold_in.output(),
241 }
242 .clone()
243 })
244 .collect(),
245 reference_inputs: self
246 .reference_inputs
247 .iter()
248 .map(|(reference, output)| TxInInfo {
249 reference: reference.clone(),
250 output: output.clone(),
251 })
252 .collect(),
253 outputs: self.outputs,
254 fee: Lovelace(BigInt::ZERO),
255 mint: self
256 .mint
257 .iter()
258 .fold(Value::new(), |others, (asset_class, amount, _red)| {
259 others
260 + Value::token_value(
261 &asset_class.currency_symbol,
262 &asset_class.token_name,
263 &BigInt::from(*amount),
264 )
265 }),
266 tx_certs: self.tx_certs,
267 wdrl: AssocMap(
268 self.withdrawals
269 .into_iter()
270 .map(|(credential, amount)| (credential, Lovelace(BigInt::from(amount))))
271 .collect(),
272 ),
273 valid_range: self.valid_range,
274 signatories: self.signatories.into_iter().collect(),
275 redeemers: AssocMap(
276 self.inputs
277 .iter()
278 .filter_map(|(reference, scaffold_in)| match scaffold_in {
279 TxScaffoldInput::ScriptInput { redeemer, .. } => {
280 Some((ScriptPurpose::Spending(reference.clone()), redeemer.clone()))
281 }
282 TxScaffoldInput::PubKeyInput { .. } => None,
283 })
284 .chain(self.mint.iter().map(|(asset_class, _amount, red)| {
285 (
286 ScriptPurpose::Minting(asset_class.currency_symbol.clone()),
287 red.clone(),
288 )
289 }))
290 .collect(),
291 ),
292 datums: AssocMap(
293 self.inputs
294 .into_iter()
295 .filter_map(|(_reference, scaffold_in)| match scaffold_in {
296 TxScaffoldInput::ScriptInput { datum, .. } => datum,
297 TxScaffoldInput::PubKeyInput { .. } => None,
298 })
299 .collect(),
300 ),
301 id: TransactionHash(LedgerBytes(Vec::new())),
302 votes: AssocMap(
303 self.votes
304 .into_iter()
305 .map(|(voter, votes)| (voter, AssocMap(votes.into_iter().collect())))
306 .collect(),
307 ),
308 proposal_procedures: self.proposal_procedures,
309 current_treasury_amount: self.current_treasury_amount.map(Lovelace),
310 treasury_donation: self.treasury_donation.map(Lovelace),
311 }
312 }
313}