Skip to main content

miden_standards/note/
mint.rs

1use alloc::vec::Vec;
2
3use miden_protocol::account::AccountId;
4use miden_protocol::assembly::Path;
5use miden_protocol::crypto::rand::FeltRng;
6use miden_protocol::errors::NoteError;
7use miden_protocol::note::{
8    Note,
9    NoteAssets,
10    NoteAttachment,
11    NoteMetadata,
12    NoteRecipient,
13    NoteScript,
14    NoteStorage,
15    NoteTag,
16    NoteType,
17};
18use miden_protocol::utils::sync::LazyLock;
19use miden_protocol::{Felt, MAX_NOTE_STORAGE_ITEMS, Word};
20
21use crate::StandardsLib;
22
23// NOTE SCRIPT
24// ================================================================================================
25
26/// Path to the MINT note script procedure in the standards library.
27const MINT_SCRIPT_PATH: &str = "::miden::standards::notes::mint::main";
28
29// Initialize the MINT note script only once
30static MINT_SCRIPT: LazyLock<NoteScript> = LazyLock::new(|| {
31    let standards_lib = StandardsLib::default();
32    let path = Path::new(MINT_SCRIPT_PATH);
33    NoteScript::from_library_reference(standards_lib.as_ref(), path)
34        .expect("Standards library contains MINT note script procedure")
35});
36
37// MINT NOTE
38// ================================================================================================
39
40/// TODO: add docs
41pub struct MintNote;
42
43impl MintNote {
44    // CONSTANTS
45    // --------------------------------------------------------------------------------------------
46
47    /// Expected number of storage items of the MINT note (private mode).
48    pub const NUM_STORAGE_ITEMS_PRIVATE: usize = 8;
49
50    // PUBLIC ACCESSORS
51    // --------------------------------------------------------------------------------------------
52
53    /// Returns the script of the MINT note.
54    pub fn script() -> NoteScript {
55        MINT_SCRIPT.clone()
56    }
57
58    /// Returns the MINT note script root.
59    pub fn script_root() -> Word {
60        MINT_SCRIPT.root()
61    }
62
63    // BUILDERS
64    // --------------------------------------------------------------------------------------------
65
66    /// Generates a MINT note - a note that instructs a network faucet to mint fungible assets.
67    ///
68    /// This script enables the creation of a PUBLIC note that, when consumed by a network faucet,
69    /// will mint the specified amount of fungible assets and create either a PRIVATE or PUBLIC
70    /// output note depending on the input configuration. The MINT note uses note-based
71    /// authentication, checking if the note sender equals the faucet owner to authorize
72    /// minting.
73    ///
74    /// MINT notes are always PUBLIC (for network execution). Output notes can be either PRIVATE
75    /// or PUBLIC depending on the MintNoteStorage variant used.
76    ///
77    /// The passed-in `rng` is used to generate a serial number for the note. The note's tag
78    /// is automatically set to the faucet's account ID for proper routing.
79    ///
80    /// # Parameters
81    /// - `faucet_id`: The account ID of the network faucet that will mint the assets
82    /// - `sender`: The account ID of the note creator (must be the faucet owner)
83    /// - `mint_storage`: The storage configuration specifying private or public output mode
84    /// - `attachment`: The [`NoteAttachment`] of the MINT note
85    /// - `rng`: Random number generator for creating the serial number
86    ///
87    /// # Errors
88    /// Returns an error if note creation fails.
89    pub fn create<R: FeltRng>(
90        faucet_id: AccountId,
91        sender: AccountId,
92        mint_storage: MintNoteStorage,
93        attachment: NoteAttachment,
94        rng: &mut R,
95    ) -> Result<Note, NoteError> {
96        let note_script = Self::script();
97        let serial_num = rng.draw_word();
98
99        // MINT notes are always public for network execution
100        let note_type = NoteType::Public;
101
102        // Convert MintNoteStorage to NoteStorage
103        let storage = NoteStorage::from(mint_storage);
104
105        let tag = NoteTag::with_account_target(faucet_id);
106
107        let metadata =
108            NoteMetadata::new(sender, note_type).with_tag(tag).with_attachment(attachment);
109        let assets = NoteAssets::new(vec![])?; // MINT notes have no assets
110        let recipient = NoteRecipient::new(serial_num, note_script, storage);
111
112        Ok(Note::new(assets, metadata, recipient))
113    }
114}
115
116// MINT NOTE STORAGE
117// ================================================================================================
118
119/// Represents the different storage formats for MINT notes.
120/// - Private: Creates a private output note using a precomputed recipient digest (12 MINT note
121///   storage items)
122/// - Public: Creates a public output note by providing script root, serial number, and
123///   variable-length storage (16+ MINT note storage items: 16 fixed + variable number of output
124///   note storage items)
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub enum MintNoteStorage {
127    Private {
128        recipient_digest: Word,
129        amount: Felt,
130        tag: Felt,
131        attachment: NoteAttachment,
132    },
133    Public {
134        recipient: NoteRecipient,
135        amount: Felt,
136        tag: Felt,
137        attachment: NoteAttachment,
138    },
139}
140
141impl MintNoteStorage {
142    pub fn new_private(recipient_digest: Word, amount: Felt, tag: Felt) -> Self {
143        Self::Private {
144            recipient_digest,
145            amount,
146            tag,
147            attachment: NoteAttachment::default(),
148        }
149    }
150
151    pub fn new_public(
152        recipient: NoteRecipient,
153        amount: Felt,
154        tag: Felt,
155    ) -> Result<Self, NoteError> {
156        // Calculate total number of storage items that will be created:
157        // 16 fixed items (tag, amount, attachment_kind, attachment_scheme, ATTACHMENT,
158        // SCRIPT_ROOT, SERIAL_NUM) + variable recipient number of storage items
159        const FIXED_PUBLIC_STORAGE_ITEMS: usize = 16;
160        let total_storage_items =
161            FIXED_PUBLIC_STORAGE_ITEMS + recipient.storage().num_items() as usize;
162
163        if total_storage_items > MAX_NOTE_STORAGE_ITEMS {
164            return Err(NoteError::TooManyStorageItems(total_storage_items));
165        }
166
167        Ok(Self::Public {
168            recipient,
169            amount,
170            tag,
171            attachment: NoteAttachment::default(),
172        })
173    }
174
175    /// Overwrites the [`NoteAttachment`] of the note storage.
176    pub fn with_attachment(self, attachment: NoteAttachment) -> Self {
177        match self {
178            MintNoteStorage::Private {
179                recipient_digest,
180                amount,
181                tag,
182                attachment: _,
183            } => MintNoteStorage::Private {
184                recipient_digest,
185                amount,
186                tag,
187                attachment,
188            },
189            MintNoteStorage::Public { recipient, amount, tag, attachment: _ } => {
190                MintNoteStorage::Public { recipient, amount, tag, attachment }
191            },
192        }
193    }
194}
195
196impl From<MintNoteStorage> for NoteStorage {
197    fn from(mint_storage: MintNoteStorage) -> Self {
198        match mint_storage {
199            MintNoteStorage::Private {
200                recipient_digest,
201                amount,
202                tag,
203                attachment,
204            } => {
205                let attachment_scheme = Felt::from(attachment.attachment_scheme().as_u32());
206                let attachment_kind = Felt::from(attachment.attachment_kind().as_u8());
207                let attachment = attachment.content().to_word();
208
209                let mut storage_values = Vec::with_capacity(12);
210                storage_values.extend_from_slice(&[
211                    tag,
212                    amount,
213                    attachment_kind,
214                    attachment_scheme,
215                ]);
216                storage_values.extend_from_slice(attachment.as_elements());
217                storage_values.extend_from_slice(recipient_digest.as_elements());
218                NoteStorage::new(storage_values)
219                    .expect("number of storage items should not exceed max storage items")
220            },
221            MintNoteStorage::Public { recipient, amount, tag, attachment } => {
222                let attachment_scheme = Felt::from(attachment.attachment_scheme().as_u32());
223                let attachment_kind = Felt::from(attachment.attachment_kind().as_u8());
224                let attachment = attachment.content().to_word();
225
226                let mut storage_values = vec![tag, amount, attachment_kind, attachment_scheme];
227                storage_values.extend_from_slice(attachment.as_elements());
228                storage_values.extend_from_slice(recipient.script().root().as_elements());
229                storage_values.extend_from_slice(recipient.serial_num().as_elements());
230                storage_values.extend_from_slice(recipient.storage().items());
231                NoteStorage::new(storage_values)
232                    .expect("number of storage items should not exceed max storage items")
233            },
234        }
235    }
236}