miden_client/transaction/request/builder.rs
1//! Contains structures and functions related to transaction creation.
2use alloc::collections::{BTreeMap, BTreeSet};
3use alloc::string::ToString;
4use alloc::vec::Vec;
5
6use miden_protocol::account::AccountId;
7use miden_protocol::asset::{Asset, FungibleAsset};
8use miden_protocol::block::BlockNumber;
9use miden_protocol::crypto::merkle::InnerNodeInfo;
10use miden_protocol::crypto::merkle::store::MerkleStore;
11use miden_protocol::crypto::rand::FeltRng;
12use miden_protocol::errors::NoteError;
13use miden_protocol::note::{
14 Note,
15 NoteAssets,
16 NoteAttachment,
17 NoteAttachments,
18 NoteDetails,
19 NoteDetailsCommitment,
20 NoteId,
21 NoteRecipient,
22 NoteScript,
23 NoteStorage,
24 NoteTag,
25 NoteType,
26 PartialNote,
27 PartialNoteMetadata,
28};
29use miden_protocol::transaction::TransactionScript;
30use miden_protocol::vm::AdviceMap;
31use miden_protocol::{Felt, Word};
32use miden_standards::note::{
33 P2idNote,
34 P2ideNote,
35 P2ideNoteStorage,
36 PswapNote,
37 PswapNoteStorage,
38 SwapNote,
39};
40
41use super::{
42 ForeignAccount,
43 NoteArgs,
44 TransactionRequest,
45 TransactionRequestError,
46 TransactionScriptTemplate,
47};
48use crate::ClientRng;
49
50// TRANSACTION REQUEST BUILDER
51// ================================================================================================
52
53/// A builder for a [`TransactionRequest`].
54///
55/// Use this builder to construct a [`TransactionRequest`] by adding input notes, specifying
56/// scripts, and setting other transaction parameters.
57#[derive(Clone, Debug)]
58pub struct TransactionRequestBuilder {
59 /// Notes to be consumed by the transaction.
60 /// Notes whose inclusion proof is present in the store are will be consumed as authenticated;
61 /// the ones that do not have proofs will be consumed as unauthenticated.
62 input_notes: Vec<Note>,
63 /// Optional arguments of the Notes to be consumed by the transaction. This
64 /// includes both authenticated and unauthenticated notes.
65 input_notes_args: Vec<(NoteId, Option<NoteArgs>)>,
66 /// Notes to be created by the transaction. The full note data is needed internally
67 /// to build the transaction script template.
68 own_output_notes: Vec<Note>,
69 /// A map of recipients of the output notes expected to be generated by the transaction.
70 expected_output_recipients: BTreeMap<Word, NoteRecipient>,
71 /// A map of details and tags of notes we expect to be created as part of future transactions
72 /// with their respective tags.
73 ///
74 /// For example, after a swap note is consumed, a payback note is expected to be created.
75 expected_future_notes: BTreeMap<NoteDetailsCommitment, (NoteDetails, NoteTag)>,
76 /// Custom transaction script to be used.
77 custom_script: Option<TransactionScript>,
78 /// Initial state of the `AdviceMap` that provides data during runtime.
79 advice_map: AdviceMap,
80 /// Initial state of the `MerkleStore` that provides data during runtime.
81 merkle_store: MerkleStore,
82 /// Foreign account data requirements. At execution time, account data will be retrieved from
83 /// the network, and injected as advice inputs. Additionally, the account's code will be
84 /// added to the executor and prover.
85 foreign_accounts: BTreeMap<AccountId, ForeignAccount>,
86 /// The number of blocks in relation to the transaction's reference block after which the
87 /// transaction will expire. If `None`, the transaction will not expire.
88 expiration_delta: Option<u16>,
89 /// Indicates whether to **silently** ignore invalid input notes when executing the
90 /// transaction. This will allow the transaction to be executed even if some input notes
91 /// are invalid.
92 ignore_invalid_input_notes: bool,
93 /// Optional [`Word`] that will be pushed to the operand stack before the transaction script
94 /// execution. If the advice map is extended with some user defined entries, this script
95 /// argument could be used as a key to access the corresponding value.
96 script_arg: Option<Word>,
97 /// Optional [`Word`] that will be pushed to the stack for the authentication procedure
98 /// during transaction execution.
99 auth_arg: Option<Word>,
100 /// Note scripts that the node's NTX builder will need in its script registry.
101 ///
102 /// See [`TransactionRequestBuilder::expected_ntx_scripts`] for details.
103 expected_ntx_scripts: Vec<NoteScript>,
104}
105
106impl TransactionRequestBuilder {
107 // CONSTRUCTORS
108 // --------------------------------------------------------------------------------------------
109
110 /// Creates a new, empty [`TransactionRequestBuilder`].
111 pub fn new() -> Self {
112 Self {
113 input_notes: vec![],
114 input_notes_args: vec![],
115 own_output_notes: Vec::new(),
116 expected_output_recipients: BTreeMap::new(),
117 expected_future_notes: BTreeMap::new(),
118 custom_script: None,
119 advice_map: AdviceMap::default(),
120 merkle_store: MerkleStore::default(),
121 expiration_delta: None,
122 foreign_accounts: BTreeMap::default(),
123 ignore_invalid_input_notes: false,
124 script_arg: None,
125 auth_arg: None,
126 expected_ntx_scripts: vec![],
127 }
128 }
129
130 /// Adds the specified notes as input notes to the transaction request.
131 #[must_use]
132 pub fn input_notes(
133 mut self,
134 notes: impl IntoIterator<Item = (Note, Option<NoteArgs>)>,
135 ) -> Self {
136 for (note, argument) in notes {
137 self.input_notes_args.push((note.id(), argument));
138 self.input_notes.push(note);
139 }
140 self
141 }
142
143 /// Specifies the output notes that should be created in the transaction script and will
144 /// be used as a transaction script template. These notes will also be added to the expected
145 /// output recipients of the transaction.
146 ///
147 /// If a transaction script template is already set (e.g. by calling `with_custom_script`), the
148 /// [`TransactionRequestBuilder::build`] method will return an error.
149 #[must_use]
150 pub fn own_output_notes(mut self, notes: impl IntoIterator<Item = Note>) -> Self {
151 for note in notes {
152 self.expected_output_recipients
153 .insert(note.recipient().digest(), note.recipient().clone());
154 self.own_output_notes.push(note);
155 }
156
157 self
158 }
159
160 /// Specifies a custom transaction script to be used.
161 ///
162 /// If a script template is already set (e.g. by calling `with_own_output_notes`), the
163 /// [`TransactionRequestBuilder::build`] method will return an error.
164 #[must_use]
165 pub fn custom_script(mut self, script: TransactionScript) -> Self {
166 self.custom_script = Some(script);
167 self
168 }
169
170 /// Specifies one or more foreign accounts (public or private) that contain data
171 /// utilized by the transaction.
172 ///
173 /// At execution, the client queries the node and retrieves the appropriate data,
174 /// depending on whether each foreign account is public or private:
175 ///
176 /// - **Public accounts**: the node retrieves the state and code for the account and injects
177 /// them as advice inputs. Public accounts can be omitted here, as they will be lazily loaded
178 /// through RPC calls. Undeclared accounts may trigger additional RPC calls for storage map
179 /// accesses during execution.
180 /// - **Private accounts**: the node retrieves a proof of the account's existence and injects
181 /// that as advice inputs. Private accounts must always be declared here with their
182 /// [`PartialAccount`](miden_protocol::account::PartialAccount) state.
183 #[must_use]
184 pub fn foreign_accounts(
185 mut self,
186 foreign_accounts: impl IntoIterator<Item = impl Into<ForeignAccount>>,
187 ) -> Self {
188 for account in foreign_accounts {
189 let foreign_account: ForeignAccount = account.into();
190 self.foreign_accounts.insert(foreign_account.account_id(), foreign_account);
191 }
192
193 self
194 }
195
196 /// Specifies a transaction's expected output note recipients.
197 ///
198 /// The set of specified recipients is treated as a subset of the recipients for notes that may
199 /// be created by a transaction. That is, the transaction must create notes for all the
200 /// specified expected recipients, but it may also create notes for other recipients not
201 /// included in this set.
202 #[must_use]
203 pub fn expected_output_recipients(mut self, recipients: Vec<NoteRecipient>) -> Self {
204 self.expected_output_recipients = recipients
205 .into_iter()
206 .map(|recipient| (recipient.digest(), recipient))
207 .collect::<BTreeMap<_, _>>();
208 self
209 }
210
211 /// Specifies a set of notes which may be created when a transaction's output notes are
212 /// consumed.
213 ///
214 /// For example, after a SWAP note is consumed, a payback note is expected to be created. This
215 /// allows the client to track this note accordingly.
216 #[must_use]
217 pub fn expected_future_notes(mut self, notes: Vec<(NoteDetails, NoteTag)>) -> Self {
218 self.expected_future_notes = notes
219 .into_iter()
220 .map(|note| (note.0.commitment(), note))
221 .collect::<BTreeMap<_, _>>();
222 self
223 }
224
225 /// Extends the advice map with the specified `([Word], Vec<[Felt]>)` pairs.
226 #[must_use]
227 pub fn extend_advice_map<I, V>(mut self, iter: I) -> Self
228 where
229 I: IntoIterator<Item = (Word, V)>,
230 V: AsRef<[Felt]>,
231 {
232 self.advice_map.extend(iter.into_iter().map(|(w, v)| (w, v.as_ref().to_vec())));
233 self
234 }
235
236 /// Extends the merkle store with the specified [`InnerNodeInfo`] elements.
237 #[must_use]
238 pub fn extend_merkle_store<T: IntoIterator<Item = InnerNodeInfo>>(mut self, iter: T) -> Self {
239 self.merkle_store.extend(iter);
240 self
241 }
242
243 /// The number of blocks in relation to the transaction's reference block after which the
244 /// transaction will expire. By default, the transaction will not expire.
245 ///
246 /// Setting transaction expiration delta defines an upper bound for transaction expiration,
247 /// but other code executed during the transaction may impose an even smaller transaction
248 /// expiration delta.
249 #[must_use]
250 pub fn expiration_delta(mut self, expiration_delta: u16) -> Self {
251 self.expiration_delta = Some(expiration_delta);
252 self
253 }
254
255 /// The resulting transaction will **silently** ignore invalid input notes when being executed.
256 /// By default, this will not happen.
257 #[must_use]
258 pub fn ignore_invalid_input_notes(mut self) -> Self {
259 self.ignore_invalid_input_notes = true;
260 self
261 }
262
263 /// Sets an optional [`Word`] that will be pushed to the operand stack before the transaction
264 /// script execution. If the advice map is extended with some user defined entries, this script
265 /// argument could be used as a key to access the corresponding value.
266 #[must_use]
267 pub fn script_arg(mut self, script_arg: Word) -> Self {
268 self.script_arg = Some(script_arg);
269 self
270 }
271
272 /// Sets an optional [`Word`] that will be pushed to the stack for the authentication
273 /// procedure during transaction execution.
274 #[must_use]
275 pub fn auth_arg(mut self, auth_arg: Word) -> Self {
276 self.auth_arg = Some(auth_arg);
277 self
278 }
279
280 /// Specifies note scripts that the node's network transaction (NTX) builder will need in
281 /// its script registry.
282 ///
283 /// When a transaction creates notes destined for a network account, the node's NTX builder
284 /// must have the scripts of any public output notes in its registry. If a required script
285 /// is missing, the NTX will silently fail on the node side.
286 ///
287 /// When this field is set, the client will check each script against the node before
288 /// executing the main transaction. For any script not yet registered, the client
289 /// automatically creates and submits a separate registration transaction (a public note
290 /// carrying that script) so the node's registry is populated before the NTX executes.
291 ///
292 /// Standard note scripts are ignored here — the NTX builder resolves them directly.
293 #[must_use]
294 pub fn expected_ntx_scripts(mut self, scripts: Vec<NoteScript>) -> Self {
295 self.expected_ntx_scripts = scripts;
296 self
297 }
298
299 // STANDARDIZED REQUESTS
300 // --------------------------------------------------------------------------------------------
301
302 /// Consumes the builder and returns a [`TransactionRequest`] for a transaction to consume the
303 /// specified notes.
304 ///
305 /// - `notes` is a list of notes to be consumed.
306 pub fn build_consume_notes(
307 self,
308 notes: Vec<Note>,
309 ) -> Result<TransactionRequest, TransactionRequestError> {
310 let input_notes = notes.into_iter().map(|id| (id, None));
311 self.input_notes(input_notes).build()
312 }
313
314 /// Consumes the builder and returns a [`TransactionRequest`] for a transaction to mint fungible
315 /// assets. This request must be executed against a fungible faucet account.
316 ///
317 /// - `asset` is the fungible asset to be minted.
318 /// - `target_id` is the account ID of the account to receive the minted asset.
319 /// - `note_type` determines the visibility of the note to be created.
320 /// - `rng` is the random number generator used to generate the serial number for the created
321 /// note.
322 ///
323 /// This function cannot be used with a previously set custom script.
324 pub fn build_mint_fungible_asset(
325 self,
326 asset: FungibleAsset,
327 target_id: AccountId,
328 note_type: NoteType,
329 rng: &mut ClientRng,
330 ) -> Result<TransactionRequest, TransactionRequestError> {
331 let created_note = P2idNote::create(
332 asset.faucet_id(),
333 target_id,
334 vec![asset.into()],
335 note_type,
336 NoteAttachments::empty(),
337 rng,
338 )?;
339
340 self.own_output_notes(vec![created_note]).build()
341 }
342
343 /// Consumes the builder and returns a [`TransactionRequest`] for a transaction to send a P2ID
344 /// or P2IDE note. This request must be executed against the wallet sender account.
345 ///
346 /// - `payment_data` is the data for the payment transaction that contains the asset to be
347 /// transferred, the sender account ID, and the target account ID. If the recall or timelock
348 /// heights are set, a P2IDE note will be created; otherwise, a P2ID note will be created.
349 /// - `note_type` determines the visibility of the note to be created.
350 /// - `rng` is the random number generator used to generate the serial number for the created
351 /// note.
352 ///
353 /// This function cannot be used with a previously set custom script.
354 pub fn build_pay_to_id(
355 self,
356 payment_data: PaymentNoteDescription,
357 note_type: NoteType,
358 rng: &mut ClientRng,
359 ) -> Result<TransactionRequest, TransactionRequestError> {
360 if payment_data
361 .assets()
362 .iter()
363 .all(|asset| asset.is_fungible() && asset.unwrap_fungible().amount().as_u64() == 0)
364 {
365 return Err(TransactionRequestError::P2IDNoteWithoutAsset);
366 }
367
368 let created_note = payment_data.into_note(note_type, rng)?;
369
370 self.own_output_notes(vec![created_note]).build()
371 }
372
373 /// Consumes the builder and returns a [`TransactionRequest`] for a transaction to send a SWAP
374 /// note. This request must be executed against the wallet sender account.
375 ///
376 /// - `swap_data` is the data for the swap transaction that contains the sender account ID, the
377 /// offered asset, and the requested asset.
378 /// - `note_type` determines the visibility of the note to be created.
379 /// - `payback_note_type` determines the visibility of the payback note.
380 /// - `rng` is the random number generator used to generate the serial number for the created
381 /// note.
382 ///
383 /// This function cannot be used with a previously set custom script.
384 pub fn build_swap(
385 self,
386 swap_data: &SwapTransactionData,
387 note_type: NoteType,
388 payback_note_type: NoteType,
389 rng: &mut ClientRng,
390 ) -> Result<TransactionRequest, TransactionRequestError> {
391 // The created note is the one that we need as the output of the tx, the other one is the
392 // one that we expect to receive and consume eventually.
393 let (created_note, payback_note_details) = SwapNote::create(
394 swap_data.account_id(),
395 swap_data.offered_asset(),
396 swap_data.requested_asset(),
397 note_type,
398 NoteAttachments::empty(),
399 payback_note_type,
400 rng,
401 )?;
402
403 let payback_tag = NoteTag::with_account_target(swap_data.account_id());
404
405 self.expected_future_notes(vec![(payback_note_details, payback_tag)])
406 .own_output_notes(vec![created_note])
407 .build()
408 }
409
410 /// Consumes the builder and returns a [`TransactionRequest`] for a transaction that registers
411 /// note scripts in the node's script registry.
412 ///
413 /// This creates one public output note per script, each with empty assets and storage. The
414 /// node indexes the script of every public note it processes, so submitting this transaction
415 /// makes the scripts available for future network transactions (NTX).
416 ///
417 /// - `sender_account_id` is the account executing the transaction.
418 /// - `scripts` is the list of note scripts to register.
419 /// - `rng` is used to generate serial numbers for the registration notes.
420 ///
421 /// This function cannot be used with a previously set custom script.
422 pub fn build_register_note_scripts(
423 self,
424 sender_account_id: AccountId,
425 scripts: Vec<NoteScript>,
426 rng: &mut ClientRng,
427 ) -> Result<TransactionRequest, TransactionRequestError> {
428 let registration_notes: Vec<Note> = scripts
429 .into_iter()
430 .map(|script| {
431 let serial_num = rng.draw_word();
432 let note_storage = NoteStorage::new(vec![])?;
433 let recipient = NoteRecipient::new(serial_num, script, note_storage);
434 let note_assets = NoteAssets::new(vec![])?;
435 let metadata = PartialNoteMetadata::new(sender_account_id, NoteType::Public);
436 Ok(Note::new(note_assets, metadata, recipient))
437 })
438 .collect::<Result<_, NoteError>>()?;
439
440 self.own_output_notes(registration_notes).build()
441 }
442
443 /// Consumes the builder and returns a [`TransactionRequest`] for a transaction that creates a
444 /// partial swap (PSWAP) note. This request must be executed against the creator account.
445 ///
446 /// - `pswap_data` is the data for the partial swap that contains the creator account ID, the
447 /// offered fungible asset, and the requested fungible asset.
448 /// - `note_type` determines the visibility of the PSWAP note itself.
449 /// - `payback_note_type` determines the visibility of the payback note that fillers emit back
450 /// to the creator. Typically [`NoteType::Private`] (cheaper; the fill amount is already
451 /// visible in the executing transaction).
452 /// - `note_attachment` is the optional attachment for the PSWAP note. Pass `None` when there is
453 /// nothing to attach.
454 /// - `rng` is the random number generator used to generate the serial number for the created
455 /// note.
456 ///
457 /// This function cannot be used with a previously set custom script.
458 pub fn build_pswap_create(
459 self,
460 pswap_data: &PswapTransactionData,
461 note_type: NoteType,
462 payback_note_type: NoteType,
463 note_attachment: Option<NoteAttachment>,
464 rng: &mut ClientRng,
465 ) -> Result<TransactionRequest, TransactionRequestError> {
466 let storage = PswapNoteStorage::builder()
467 .requested_asset(pswap_data.requested_asset())
468 .creator_account_id(pswap_data.creator_account_id())
469 .payback_note_type(payback_note_type)
470 .build();
471
472 let pswap_note = PswapNote::builder()
473 .sender(pswap_data.creator_account_id())
474 .storage(storage)
475 .serial_number(rng.draw_word())
476 .note_type(note_type)
477 .offered_asset(pswap_data.offered_asset())
478 .maybe_attachment(note_attachment)
479 .build()
480 .map_err(TransactionRequestError::NoteCreationError)?;
481
482 let note: Note = pswap_note.into();
483 self.own_output_notes(vec![note]).build()
484 }
485
486 /// Consumes the builder and returns a [`TransactionRequest`] for a transaction that consumes
487 /// (fills) a partial swap (PSWAP) note. This request must be executed against the consumer
488 /// account.
489 ///
490 /// - `pswap_note` is the PSWAP note being consumed.
491 /// - `consumer_account_id` is the account consuming the swap.
492 /// - `account_fill_amount` is the amount of the requested asset being provided by the consumer
493 /// account.
494 /// - `note_fill_amount` is any additional amount being provided by other (in-flight) notes.
495 ///
496 /// This function cannot be used with a previously set custom script.
497 pub fn build_pswap_consume(
498 self,
499 pswap_note: &Note,
500 consumer_account_id: AccountId,
501 account_fill_amount: u64,
502 note_fill_amount: u64,
503 ) -> Result<TransactionRequest, TransactionRequestError> {
504 let pswap = PswapNote::try_from(pswap_note)
505 .map_err(TransactionRequestError::NoteValidationError)?;
506
507 let requested_faucet_id = pswap.storage().requested_asset().faucet_id();
508
509 let account_fill_asset = FungibleAsset::new(requested_faucet_id, account_fill_amount)?;
510 let note_fill_asset = FungibleAsset::new(requested_faucet_id, note_fill_amount)?;
511
512 let (payback_note, remainder_pswap) = pswap
513 .execute(consumer_account_id, Some(account_fill_asset), Some(note_fill_asset))
514 .map_err(TransactionRequestError::NoteExecutionError)?;
515
516 let note_args = PswapNote::create_args(account_fill_amount, note_fill_amount)
517 .map_err(TransactionRequestError::NoteArgError)?;
518
519 // Register output notes as expected future notes (created by the script, not the account).
520 let payback_details = NoteDetails::from(&payback_note);
521 let payback_tag = payback_note.metadata().tag();
522 let payback_recipient = payback_note.recipient().clone();
523
524 let mut expected_future_notes = vec![(payback_details, payback_tag)];
525 let mut expected_recipients = vec![payback_recipient];
526
527 if let Some(remainder) = remainder_pswap {
528 let remainder_note: Note = remainder.into();
529 expected_future_notes
530 .push((NoteDetails::from(&remainder_note), remainder_note.metadata().tag()));
531 expected_recipients.push(remainder_note.recipient().clone());
532 }
533
534 self.input_notes(vec![(pswap_note.clone(), Some(note_args))])
535 .expected_future_notes(expected_future_notes)
536 .expected_output_recipients(expected_recipients)
537 .build()
538 }
539
540 /// Consumes the builder and returns a [`TransactionRequest`] for a transaction that cancels a
541 /// partial swap (PSWAP) note. This request must be executed against the creator account.
542 ///
543 /// - `pswap_note` is the PSWAP note to cancel.
544 /// - `creator_account_id` is the account that created the note. The note's stored creator must
545 /// match this ID; this is the account the resulting transaction must be executed against.
546 ///
547 /// This function cannot be used with a previously set custom script.
548 pub fn build_pswap_cancel(
549 self,
550 pswap_note: Note,
551 creator_account_id: AccountId,
552 ) -> Result<TransactionRequest, TransactionRequestError> {
553 let pswap = PswapNote::try_from(&pswap_note)
554 .map_err(TransactionRequestError::NoteValidationError)?;
555
556 let note_creator = pswap.storage().creator_account_id();
557 if note_creator != creator_account_id {
558 return Err(TransactionRequestError::PswapCancelCreatorMismatch {
559 expected: note_creator,
560 actual: creator_account_id,
561 });
562 }
563
564 self.input_notes(vec![(pswap_note, None)]).build()
565 }
566
567 // FINALIZE BUILDER
568 // --------------------------------------------------------------------------------------------
569
570 /// Consumes the builder and returns a [`TransactionRequest`].
571 ///
572 /// # Errors
573 /// - If both a custom script and own output notes are set.
574 /// - If an expiration delta is set when a custom script is set.
575 /// - If an invalid note variant is encountered in the own output notes.
576 pub fn build(self) -> Result<TransactionRequest, TransactionRequestError> {
577 let mut seen_input_notes = BTreeSet::new();
578 for (note_id, _) in &self.input_notes_args {
579 if !seen_input_notes.insert(note_id) {
580 return Err(TransactionRequestError::DuplicateInputNote(*note_id));
581 }
582 }
583
584 let script_template = match (self.custom_script, self.own_output_notes.is_empty()) {
585 (Some(_), false) => {
586 return Err(TransactionRequestError::ScriptTemplateError(
587 "Cannot set both a custom script and own output notes".to_string(),
588 ));
589 },
590 (Some(script), true) => {
591 if self.expiration_delta.is_some() {
592 return Err(TransactionRequestError::ScriptTemplateError(
593 "Cannot set expiration delta when a custom script is set".to_string(),
594 ));
595 }
596
597 Some(TransactionScriptTemplate::CustomScript(script))
598 },
599 (None, false) => {
600 let partial_notes: Vec<PartialNote> =
601 self.own_output_notes.into_iter().map(Into::into).collect();
602
603 Some(TransactionScriptTemplate::SendNotes(partial_notes))
604 },
605 (None, true) => None,
606 };
607
608 Ok(TransactionRequest {
609 input_notes: self.input_notes,
610 input_notes_args: self.input_notes_args,
611 script_template,
612 expected_output_recipients: self.expected_output_recipients,
613 expected_future_notes: self.expected_future_notes,
614 advice_map: self.advice_map,
615 merkle_store: self.merkle_store,
616 foreign_accounts: self.foreign_accounts,
617 expiration_delta: self.expiration_delta,
618 ignore_invalid_input_notes: self.ignore_invalid_input_notes,
619 script_arg: self.script_arg,
620 auth_arg: self.auth_arg,
621 expected_ntx_scripts: self.expected_ntx_scripts,
622 })
623 }
624}
625
626// PAYMENT NOTE DESCRIPTION
627// ================================================================================================
628
629/// Contains information needed to create a payment note.
630#[derive(Clone, Debug)]
631pub struct PaymentNoteDescription {
632 /// Assets that are meant to be sent to the target account.
633 assets: Vec<Asset>,
634 /// Account ID of the sender account.
635 sender_account_id: AccountId,
636 /// Account ID of the receiver account.
637 target_account_id: AccountId,
638 /// Optional reclaim height for the P2IDE note. It allows the possibility for the sender to
639 /// reclaim the assets if the note has not been consumed by the target before this height.
640 reclaim_height: Option<BlockNumber>,
641 /// Optional timelock height for the P2IDE note. It allows the possibility to add a timelock to
642 /// the asset transfer, meaning that the note can only be consumed after this height.
643 timelock_height: Option<BlockNumber>,
644}
645
646impl PaymentNoteDescription {
647 // CONSTRUCTORS
648 // --------------------------------------------------------------------------------------------
649
650 /// Creates a new [`PaymentNoteDescription`].
651 pub fn new(
652 assets: Vec<Asset>,
653 sender_account_id: AccountId,
654 target_account_id: AccountId,
655 ) -> PaymentNoteDescription {
656 PaymentNoteDescription {
657 assets,
658 sender_account_id,
659 target_account_id,
660 reclaim_height: None,
661 timelock_height: None,
662 }
663 }
664
665 /// Modifies the [`PaymentNoteDescription`] to set a reclaim height for payment note.
666 #[must_use]
667 pub fn with_reclaim_height(mut self, reclaim_height: BlockNumber) -> PaymentNoteDescription {
668 self.reclaim_height = Some(reclaim_height);
669 self
670 }
671
672 /// Modifies the [`PaymentNoteDescription`] to set a timelock height for payment note.
673 #[must_use]
674 pub fn with_timelock_height(mut self, timelock_height: BlockNumber) -> PaymentNoteDescription {
675 self.timelock_height = Some(timelock_height);
676 self
677 }
678
679 /// Returns the executor [`AccountId`].
680 pub fn account_id(&self) -> AccountId {
681 self.sender_account_id
682 }
683
684 /// Returns the target [`AccountId`].
685 pub fn target_account_id(&self) -> AccountId {
686 self.target_account_id
687 }
688
689 /// Returns the transaction's list of [`Asset`].
690 pub fn assets(&self) -> &Vec<Asset> {
691 &self.assets
692 }
693
694 /// Returns the reclaim height for the P2IDE note, if set.
695 pub fn reclaim_height(&self) -> Option<BlockNumber> {
696 self.reclaim_height
697 }
698
699 /// Returns the timelock height for the P2IDE note, if set.
700 pub fn timelock_height(&self) -> Option<BlockNumber> {
701 self.timelock_height
702 }
703
704 // CONVERSION
705 // --------------------------------------------------------------------------------------------
706
707 /// Converts the payment transaction data into a [`Note`] based on the specified fields. If the
708 /// reclaim and timelock heights are not set, a P2ID note is created; otherwise, a P2IDE note is
709 /// created.
710 pub(crate) fn into_note(
711 self,
712 note_type: NoteType,
713 rng: &mut ClientRng,
714 ) -> Result<Note, NoteError> {
715 if self.reclaim_height.is_none() && self.timelock_height.is_none() {
716 // Create a P2ID note
717 P2idNote::create(
718 self.sender_account_id,
719 self.target_account_id,
720 self.assets,
721 note_type,
722 NoteAttachments::empty(),
723 rng,
724 )
725 } else {
726 // Create a P2IDE note
727 P2ideNote::create(
728 self.sender_account_id,
729 P2ideNoteStorage::new(
730 self.target_account_id,
731 self.reclaim_height,
732 self.timelock_height,
733 ),
734 self.assets,
735 note_type,
736 NoteAttachments::empty(),
737 rng,
738 )
739 }
740 }
741}
742
743// SWAP TRANSACTION DATA
744// ================================================================================================
745
746/// Contains information related to a swap transaction.
747///
748/// A swap transaction involves creating a SWAP note, which will carry the offered asset and which,
749/// when consumed, will create a payback note that carries the requested asset taken from the
750/// consumer account's vault.
751#[derive(Clone, Debug)]
752pub struct SwapTransactionData {
753 /// Account ID of the sender account.
754 sender_account_id: AccountId,
755 /// Asset that is offered in the swap.
756 offered_asset: Asset,
757 /// Asset that is expected in the payback note generated as a result of the swap.
758 requested_asset: Asset,
759}
760
761impl SwapTransactionData {
762 // CONSTRUCTORS
763 // --------------------------------------------------------------------------------------------
764
765 /// Creates a new [`SwapTransactionData`].
766 pub fn new(
767 sender_account_id: AccountId,
768 offered_asset: Asset,
769 requested_asset: Asset,
770 ) -> SwapTransactionData {
771 SwapTransactionData {
772 sender_account_id,
773 offered_asset,
774 requested_asset,
775 }
776 }
777
778 /// Returns the executor [`AccountId`].
779 pub fn account_id(&self) -> AccountId {
780 self.sender_account_id
781 }
782
783 /// Returns the transaction offered [`Asset`].
784 pub fn offered_asset(&self) -> Asset {
785 self.offered_asset
786 }
787
788 /// Returns the transaction requested [`Asset`].
789 pub fn requested_asset(&self) -> Asset {
790 self.requested_asset
791 }
792}
793
794// PSWAP TRANSACTION DATA
795// ================================================================================================
796
797/// Contains information related to a partial swap (PSWAP) transaction.
798///
799/// A PSWAP transaction involves creating a PSWAP note that carries the offered fungible asset
800/// and, when consumed (filled), produces a payback note carrying the requested fungible asset
801/// taken from the filler's vault. Both legs are restricted to fungible assets so that fills can
802/// be denominated in arbitrary amounts.
803#[derive(Clone, Debug)]
804pub struct PswapTransactionData {
805 /// Account ID of the creator account.
806 creator_account_id: AccountId,
807 /// Fungible asset offered in the swap.
808 offered_asset: FungibleAsset,
809 /// Fungible asset expected in the payback note generated when the PSWAP is filled.
810 requested_asset: FungibleAsset,
811}
812
813impl PswapTransactionData {
814 // CONSTRUCTORS
815 // --------------------------------------------------------------------------------------------
816
817 /// Creates a new [`PswapTransactionData`].
818 pub fn new(
819 creator_account_id: AccountId,
820 offered_asset: FungibleAsset,
821 requested_asset: FungibleAsset,
822 ) -> PswapTransactionData {
823 PswapTransactionData {
824 creator_account_id,
825 offered_asset,
826 requested_asset,
827 }
828 }
829
830 /// Returns the creator [`AccountId`].
831 pub fn creator_account_id(&self) -> AccountId {
832 self.creator_account_id
833 }
834
835 /// Returns the offered [`FungibleAsset`].
836 pub fn offered_asset(&self) -> FungibleAsset {
837 self.offered_asset
838 }
839
840 /// Returns the requested [`FungibleAsset`].
841 pub fn requested_asset(&self) -> FungibleAsset {
842 self.requested_asset
843 }
844}