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}
161
162/// Generates a MINT note - a note that instructs a network faucet to mint fungible assets.
163///
164/// This script enables the creation of a PUBLIC note that, when consumed by a network faucet,
165/// will mint the specified amount of fungible assets and create a PRIVATE note with the given
166/// RECIPIENT. The MINT note uses note-based authentication, checking if the note sender equals
167/// the faucet owner to authorize minting.
168///
169/// MINT notes are always PUBLIC (for network execution) and output notes are always PRIVATE
170/// (TODO: enable public output note creation from MINT note consumption).
171///
172/// The passed-in `rng` is used to generate a serial number for the note. The note's tag
173/// is automatically set to the faucet's account ID for proper routing.
174///
175/// # Parameters
176/// - `faucet_id`: The account ID of the network faucet that will mint the assets
177/// - `sender`: The account ID of the note creator (must be the faucet owner)
178/// - `target_recipient`: The recipient digest for the output P2ID note that will receive the minted
179///   assets
180/// - `output_note_tag`: The tag for the output P2ID note
181/// - `amount`: The amount of fungible assets to mint
182/// - `aux`: Auxiliary data for the MINT note
183/// - `output_note_aux`: Auxiliary data for the output P2ID note
184/// - `rng`: Random number generator for creating the serial number
185///
186/// # Errors
187/// Returns an error if note creation fails.
188pub fn create_mint_note<R: FeltRng>(
189    faucet_id: AccountId,
190    sender: AccountId,
191    target_recipient: Word,
192    output_note_tag: Felt,
193    amount: Felt,
194    aux: Felt,
195    output_note_aux: Felt,
196    rng: &mut R,
197) -> Result<Note, NoteError> {
198    let note_script = WellKnownNote::MINT.script();
199    let serial_num = rng.draw_word();
200
201    // MINT notes are always public for network execution
202    let note_type = NoteType::Public;
203    // Output notes are always private (for now)
204    let output_note_type = NoteType::Private;
205
206    let execution_hint = NoteExecutionHint::always();
207
208    let inputs = NoteInputs::new(vec![
209        target_recipient[0],
210        target_recipient[1],
211        target_recipient[2],
212        target_recipient[3],
213        execution_hint.into(),
214        output_note_type.into(),
215        output_note_aux,
216        output_note_tag,
217        amount,
218    ])?;
219
220    let tag = NoteTag::from_account_id(faucet_id);
221
222    let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?;
223    let assets = NoteAssets::new(vec![])?; // MINT notes have no assets
224    let recipient = NoteRecipient::new(serial_num, note_script, inputs);
225
226    Ok(Note::new(assets, metadata, recipient))
227}
228
229/// Generates a BURN note - a note that instructs a faucet to burn a fungible asset.
230///
231/// This script enables the creation of a PUBLIC note that, when consumed by a faucet (either basic
232/// or network), will burn the fungible assets contained in the note. Both basic and network
233/// fungible faucets export the same `burn` procedure with identical MAST roots, allowing
234/// a single BURN note script to work with either faucet type.
235///
236/// BURN notes are always PUBLIC for network execution.
237///
238/// The passed-in `rng` is used to generate a serial number for the note. The note's tag
239/// is automatically set to the faucet's account ID for proper routing.
240///
241/// # Parameters
242/// - `sender`: The account ID of the note creator
243/// - `faucet_id`: The account ID of the faucet that will burn the assets
244/// - `fungible_asset`: The fungible asset to be burned
245/// - `aux`: Auxiliary data for the note
246/// - `rng`: Random number generator for creating the serial number
247///
248/// # Errors
249/// Returns an error if note creation fails.
250pub fn create_burn_note<R: FeltRng>(
251    sender: AccountId,
252    faucet_id: AccountId,
253    fungible_asset: Asset,
254    aux: Felt,
255    rng: &mut R,
256) -> Result<Note, NoteError> {
257    let note_script = WellKnownNote::BURN.script();
258    let serial_num = rng.draw_word();
259
260    // BURN notes are always public
261    let note_type = NoteType::Public;
262    // Use always execution hint for BURN notes
263    let execution_hint = NoteExecutionHint::always();
264
265    let inputs = NoteInputs::new(vec![])?;
266    let tag = NoteTag::from_account_id(faucet_id);
267
268    let metadata = NoteMetadata::new(sender, note_type, tag, execution_hint, aux)?;
269    let assets = NoteAssets::new(vec![fungible_asset])?; // BURN notes contain the asset to burn
270    let recipient = NoteRecipient::new(serial_num, note_script, inputs);
271
272    Ok(Note::new(assets, metadata, recipient))
273}