Skip to main content

miden_standards/note/
mod.rs

1use alloc::vec::Vec;
2
3use miden_protocol::account::AccountId;
4use miden_protocol::asset::Asset;
5use miden_protocol::block::BlockNumber;
6use miden_protocol::crypto::rand::FeltRng;
7use miden_protocol::errors::NoteError;
8use miden_protocol::note::{
9    Note,
10    NoteAssets,
11    NoteAttachment,
12    NoteDetails,
13    NoteInputs,
14    NoteMetadata,
15    NoteRecipient,
16    NoteTag,
17    NoteType,
18};
19use miden_protocol::{Felt, Word};
20use utils::build_swap_tag;
21
22pub mod mint_inputs;
23pub mod utils;
24
25mod network_account_target;
26pub use network_account_target::{NetworkAccountTarget, NetworkAccountTargetError};
27
28mod well_known_note_attachment;
29pub use well_known_note_attachment::WellKnownNoteAttachment;
30
31mod well_known_note;
32pub use mint_inputs::MintNoteInputs;
33pub use well_known_note::{NoteConsumptionStatus, WellKnownNote};
34
35// STANDARDIZED SCRIPTS
36// ================================================================================================
37
38/// Generates a P2ID note - Pay-to-ID note.
39///
40/// This script enables the transfer of assets from the `sender` account to the `target` account
41/// by specifying the target's account ID.
42///
43/// The passed-in `rng` is used to generate a serial number for the note. The returned note's tag
44/// is set to the target's account ID.
45///
46/// # Errors
47/// Returns an error if deserialization or compilation of the `P2ID` script fails.
48pub fn create_p2id_note<R: FeltRng>(
49    sender: AccountId,
50    target: AccountId,
51    assets: Vec<Asset>,
52    note_type: NoteType,
53    attachment: NoteAttachment,
54    rng: &mut R,
55) -> Result<Note, NoteError> {
56    let serial_num = rng.draw_word();
57    let recipient = utils::build_p2id_recipient(target, serial_num)?;
58
59    let tag = NoteTag::with_account_target(target);
60
61    let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment);
62    let vault = NoteAssets::new(assets)?;
63
64    Ok(Note::new(vault, metadata, recipient))
65}
66
67/// Generates a P2IDE note - Pay-to-ID note with optional reclaim after a certain block height and
68/// optional timelock.
69///
70/// This script enables the transfer of assets from the `sender` account to the `target`
71/// account by specifying the target's account ID. It adds the optional possibility for the
72/// sender to reclaiming the assets if the note has not been consumed by the target within the
73/// specified timeframe and the optional possibility to add a timelock to the asset transfer.
74///
75/// The passed-in `rng` is used to generate a serial number for the note. The returned note's tag
76/// is set to the target's account ID.
77///
78/// # Errors
79/// Returns an error if deserialization or compilation of the `P2ID` script fails.
80pub fn create_p2ide_note<R: FeltRng>(
81    sender: AccountId,
82    target: AccountId,
83    assets: Vec<Asset>,
84    reclaim_height: Option<BlockNumber>,
85    timelock_height: Option<BlockNumber>,
86    note_type: NoteType,
87    attachment: NoteAttachment,
88    rng: &mut R,
89) -> Result<Note, NoteError> {
90    let serial_num = rng.draw_word();
91    let recipient =
92        utils::build_p2ide_recipient(target, reclaim_height, timelock_height, serial_num)?;
93    let tag = NoteTag::with_account_target(target);
94
95    let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment);
96    let vault = NoteAssets::new(assets)?;
97
98    Ok(Note::new(vault, metadata, recipient))
99}
100
101/// Generates a SWAP note - swap of assets between two accounts - and returns the note as well as
102/// [`NoteDetails`] for the payback note.
103///
104/// This script enables a swap of 2 assets between the `sender` account and any other account that
105/// is willing to consume the note. The consumer will receive the `offered_asset` and will create a
106/// new P2ID note with `sender` as target, containing the `requested_asset`.
107///
108/// # Errors
109/// Returns an error if deserialization or compilation of the `SWAP` script fails.
110pub fn create_swap_note<R: FeltRng>(
111    sender: AccountId,
112    offered_asset: Asset,
113    requested_asset: Asset,
114    swap_note_type: NoteType,
115    swap_note_attachment: NoteAttachment,
116    payback_note_type: NoteType,
117    payback_note_attachment: NoteAttachment,
118    rng: &mut R,
119) -> Result<(Note, NoteDetails), NoteError> {
120    if requested_asset == offered_asset {
121        return Err(NoteError::other("requested asset same as offered asset"));
122    }
123
124    let note_script = WellKnownNote::SWAP.script();
125
126    let payback_serial_num = rng.draw_word();
127    let payback_recipient = utils::build_p2id_recipient(sender, payback_serial_num)?;
128
129    let requested_asset_word: Word = requested_asset.into();
130    let payback_tag = NoteTag::with_account_target(sender);
131
132    let attachment_scheme = Felt::from(payback_note_attachment.attachment_scheme().as_u32());
133    let attachment_kind = Felt::from(payback_note_attachment.attachment_kind().as_u8());
134    let attachment = payback_note_attachment.content().to_word();
135
136    let mut inputs = Vec::with_capacity(16);
137    inputs.extend_from_slice(&[
138        payback_note_type.into(),
139        payback_tag.into(),
140        attachment_scheme,
141        attachment_kind,
142    ]);
143    inputs.extend_from_slice(attachment.as_elements());
144    inputs.extend_from_slice(requested_asset_word.as_elements());
145    inputs.extend_from_slice(payback_recipient.digest().as_elements());
146    let inputs = NoteInputs::new(inputs)?;
147
148    // build the tag for the SWAP use case
149    let tag = build_swap_tag(swap_note_type, &offered_asset, &requested_asset);
150    let serial_num = rng.draw_word();
151
152    // build the outgoing note
153    let metadata =
154        NoteMetadata::new(sender, swap_note_type, tag).with_attachment(swap_note_attachment);
155    let assets = NoteAssets::new(vec![offered_asset])?;
156    let recipient = NoteRecipient::new(serial_num, note_script, inputs);
157    let note = Note::new(assets, metadata, recipient);
158
159    // build the payback note details
160    let payback_assets = NoteAssets::new(vec![requested_asset])?;
161    let payback_note = NoteDetails::new(payback_assets, payback_recipient);
162
163    Ok((note, payback_note))
164}
165
166/// Generates a MINT note - a note that instructs a network faucet to mint fungible assets.
167///
168/// This script enables the creation of a PUBLIC note that, when consumed by a network faucet,
169/// will mint the specified amount of fungible assets and create either a PRIVATE or PUBLIC
170/// output note depending on the input configuration. The MINT note uses note-based authentication,
171/// checking if the note sender equals the faucet owner to authorize minting.
172///
173/// MINT notes are always PUBLIC (for network execution). Output notes can be either PRIVATE
174/// or PUBLIC depending on the MintNoteInputs variant used.
175///
176/// The passed-in `rng` is used to generate a serial number for the note. The note's tag
177/// is automatically set to the faucet's account ID for proper routing.
178///
179/// # Parameters
180/// - `faucet_id`: The account ID of the network faucet that will mint the assets
181/// - `sender`: The account ID of the note creator (must be the faucet owner)
182/// - `mint_inputs`: The input configuration specifying private or public output mode
183/// - `attachment`: The [`NoteAttachment`] of the MINT 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    mint_inputs: MintNoteInputs,
192    attachment: NoteAttachment,
193    rng: &mut R,
194) -> Result<Note, NoteError> {
195    let note_script = WellKnownNote::MINT.script();
196    let serial_num = rng.draw_word();
197
198    // MINT notes are always public for network execution
199    let note_type = NoteType::Public;
200
201    // Convert MintNoteInputs to NoteInputs
202    let inputs = NoteInputs::from(mint_inputs);
203
204    let tag = NoteTag::with_account_target(faucet_id);
205
206    let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment);
207    let assets = NoteAssets::new(vec![])?; // MINT notes have no assets
208    let recipient = NoteRecipient::new(serial_num, note_script, inputs);
209
210    Ok(Note::new(assets, metadata, recipient))
211}
212
213/// Generates a BURN note - a note that instructs a faucet to burn a fungible asset.
214///
215/// This script enables the creation of a PUBLIC note that, when consumed by a faucet (either basic
216/// or network), will burn the fungible assets contained in the note. Both basic and network
217/// fungible faucets export the same `burn` procedure with identical MAST roots, allowing
218/// a single BURN note script to work with either faucet type.
219///
220/// BURN notes are always PUBLIC for network execution.
221///
222/// The passed-in `rng` is used to generate a serial number for the note. The note's tag
223/// is automatically set to the faucet's account ID for proper routing.
224///
225/// # Parameters
226/// - `sender`: The account ID of the note creator
227/// - `faucet_id`: The account ID of the faucet that will burn the assets
228/// - `fungible_asset`: The fungible asset to be burned
229/// - `attachment`: The [`NoteAttachment`] of the BURN note
230/// - `rng`: Random number generator for creating the serial number
231///
232/// # Errors
233/// Returns an error if note creation fails.
234pub fn create_burn_note<R: FeltRng>(
235    sender: AccountId,
236    faucet_id: AccountId,
237    fungible_asset: Asset,
238    attachment: NoteAttachment,
239    rng: &mut R,
240) -> Result<Note, NoteError> {
241    let note_script = WellKnownNote::BURN.script();
242    let serial_num = rng.draw_word();
243
244    // BURN notes are always public
245    let note_type = NoteType::Public;
246
247    let inputs = NoteInputs::new(vec![])?;
248    let tag = NoteTag::with_account_target(faucet_id);
249
250    let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment);
251    let assets = NoteAssets::new(vec![fungible_asset])?; // BURN notes contain the asset to burn
252    let recipient = NoteRecipient::new(serial_num, note_script, inputs);
253
254    Ok(Note::new(assets, metadata, recipient))
255}