miden_lib/note/
mod.rs

1use alloc::vec::Vec;
2
3use miden_objects::account::AccountId;
4use miden_objects::asset::Asset;
5use miden_objects::block::BlockNumber;
6use miden_objects::crypto::rand::FeltRng;
7use miden_objects::note::{
8    Note,
9    NoteAssets,
10    NoteDetails,
11    NoteExecutionHint,
12    NoteInputs,
13    NoteMetadata,
14    NoteRecipient,
15    NoteTag,
16    NoteType,
17};
18use miden_objects::{Felt, NoteError, Word};
19use utils::build_swap_tag;
20
21pub mod utils;
22
23mod well_known_note;
24pub use well_known_note::{NoteConsumptionStatus, WellKnownNote};
25
26// STANDARDIZED SCRIPTS
27// ================================================================================================
28
29/// Generates a P2ID note - Pay-to-ID note.
30///
31/// This script enables the transfer of assets from the `sender` account to the `target` account
32/// by specifying the target's account ID.
33///
34/// The passed-in `rng` is used to generate a serial number for the note. The returned note's tag
35/// is set to the target's account ID.
36///
37/// # Errors
38/// Returns an error if deserialization or compilation of the `P2ID` script fails.
39pub fn create_p2id_note<R: FeltRng>(
40    sender: AccountId,
41    target: AccountId,
42    assets: Vec<Asset>,
43    note_type: NoteType,
44    aux: Felt,
45    rng: &mut R,
46) -> Result<Note, NoteError> {
47    let serial_num = rng.draw_word();
48    let recipient = utils::build_p2id_recipient(target, serial_num)?;
49
50    let tag = NoteTag::from_account_id(target);
51
52    let metadata = NoteMetadata::new(sender, note_type, tag, NoteExecutionHint::always(), aux)?;
53    let vault = NoteAssets::new(assets)?;
54
55    Ok(Note::new(vault, metadata, recipient))
56}
57
58/// Generates a P2IDE note - Pay-to-ID note with optional reclaim after a certain block height and
59/// optional timelock.
60///
61/// This script enables the transfer of assets from the `sender` account to the `target`
62/// account by specifying the target's account ID. It adds the optional possibility for the
63/// sender to reclaiming the assets if the note has not been consumed by the target within the
64/// specified timeframe and the optional possibility to add a timelock to the asset transfer.
65///
66/// The passed-in `rng` is used to generate a serial number for the note. The returned note's tag
67/// is set to the target's account ID.
68///
69/// # Errors
70/// Returns an error if deserialization or compilation of the `P2ID` script fails.
71pub fn create_p2ide_note<R: FeltRng>(
72    sender: AccountId,
73    target: AccountId,
74    assets: Vec<Asset>,
75    reclaim_height: Option<BlockNumber>,
76    timelock_height: Option<BlockNumber>,
77    note_type: NoteType,
78    aux: Felt,
79    rng: &mut R,
80) -> Result<Note, NoteError> {
81    let serial_num = rng.draw_word();
82    let recipient =
83        utils::build_p2ide_recipient(target, reclaim_height, timelock_height, serial_num)?;
84    let tag = NoteTag::from_account_id(target);
85
86    let execution_hint = match timelock_height {
87        Some(height) => NoteExecutionHint::after_block(height)?,
88        None => NoteExecutionHint::always(),
89    };
90
91    let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?;
92    let vault = NoteAssets::new(assets)?;
93
94    Ok(Note::new(vault, metadata, recipient))
95}
96
97/// Generates a SWAP note - swap of assets between two accounts - and returns the note as well as
98/// [NoteDetails] for the payback note.
99///
100/// This script enables a swap of 2 assets between the `sender` account and any other account that
101/// is willing to consume the note. The consumer will receive the `offered_asset` and will create a
102/// new P2ID note with `sender` as target, containing the `requested_asset`.
103///
104/// # Errors
105/// Returns an error if deserialization or compilation of the `SWAP` script fails.
106pub fn create_swap_note<R: FeltRng>(
107    sender: AccountId,
108    offered_asset: Asset,
109    requested_asset: Asset,
110    swap_note_type: NoteType,
111    swap_note_aux: Felt,
112    payback_note_type: NoteType,
113    payback_note_aux: Felt,
114    rng: &mut R,
115) -> Result<(Note, NoteDetails), NoteError> {
116    if requested_asset == offered_asset {
117        return Err(NoteError::other("requested asset same as offered asset"));
118    }
119
120    let note_script = WellKnownNote::SWAP.script();
121
122    let payback_serial_num = rng.draw_word();
123    let payback_recipient = utils::build_p2id_recipient(sender, payback_serial_num)?;
124
125    let payback_recipient_word: Word = payback_recipient.digest();
126    let requested_asset_word: Word = requested_asset.into();
127    let payback_tag = NoteTag::from_account_id(sender);
128
129    let inputs = NoteInputs::new(vec![
130        requested_asset_word[0],
131        requested_asset_word[1],
132        requested_asset_word[2],
133        requested_asset_word[3],
134        payback_recipient_word[0],
135        payback_recipient_word[1],
136        payback_recipient_word[2],
137        payback_recipient_word[3],
138        NoteExecutionHint::always().into(),
139        payback_note_type.into(),
140        payback_note_aux,
141        payback_tag.into(),
142    ])?;
143
144    // build the tag for the SWAP use case
145    let tag = build_swap_tag(swap_note_type, &offered_asset, &requested_asset)?;
146    let serial_num = rng.draw_word();
147
148    // build the outgoing note
149    let metadata =
150        NoteMetadata::new(sender, swap_note_type, tag, NoteExecutionHint::always(), swap_note_aux)?;
151    let assets = NoteAssets::new(vec![offered_asset])?;
152    let recipient = NoteRecipient::new(serial_num, note_script, inputs);
153    let note = Note::new(assets, metadata, recipient);
154
155    // build the payback note details
156    let payback_assets = NoteAssets::new(vec![requested_asset])?;
157    let payback_note = NoteDetails::new(payback_assets, payback_recipient);
158
159    Ok((note, payback_note))
160}