use alloc::vec::Vec;
use miden_protocol::account::AccountId;
use miden_protocol::assembly::Path;
use miden_protocol::crypto::rand::FeltRng;
use miden_protocol::errors::NoteError;
use miden_protocol::note::{
Note,
NoteAssets,
NoteAttachment,
NoteMetadata,
NoteRecipient,
NoteScript,
NoteStorage,
NoteTag,
NoteType,
};
use miden_protocol::utils::sync::LazyLock;
use miden_protocol::{Felt, MAX_NOTE_STORAGE_ITEMS, Word};
use crate::StandardsLib;
const MINT_SCRIPT_PATH: &str = "::miden::standards::notes::mint::main";
static MINT_SCRIPT: LazyLock<NoteScript> = LazyLock::new(|| {
let standards_lib = StandardsLib::default();
let path = Path::new(MINT_SCRIPT_PATH);
NoteScript::from_library_reference(standards_lib.as_ref(), path)
.expect("Standards library contains MINT note script procedure")
});
pub struct MintNote;
impl MintNote {
pub const NUM_STORAGE_ITEMS_PRIVATE: usize = 8;
pub fn script() -> NoteScript {
MINT_SCRIPT.clone()
}
pub fn script_root() -> Word {
MINT_SCRIPT.root()
}
pub fn create<R: FeltRng>(
faucet_id: AccountId,
sender: AccountId,
mint_storage: MintNoteStorage,
attachment: NoteAttachment,
rng: &mut R,
) -> Result<Note, NoteError> {
let note_script = Self::script();
let serial_num = rng.draw_word();
let note_type = NoteType::Public;
let storage = NoteStorage::from(mint_storage);
let tag = NoteTag::with_account_target(faucet_id);
let metadata =
NoteMetadata::new(sender, note_type).with_tag(tag).with_attachment(attachment);
let assets = NoteAssets::new(vec![])?; let recipient = NoteRecipient::new(serial_num, note_script, storage);
Ok(Note::new(assets, metadata, recipient))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MintNoteStorage {
Private {
recipient_digest: Word,
amount: Felt,
tag: Felt,
attachment: NoteAttachment,
},
Public {
recipient: NoteRecipient,
amount: Felt,
tag: Felt,
attachment: NoteAttachment,
},
}
impl MintNoteStorage {
pub fn new_private(recipient_digest: Word, amount: Felt, tag: Felt) -> Self {
Self::Private {
recipient_digest,
amount,
tag,
attachment: NoteAttachment::default(),
}
}
pub fn new_public(
recipient: NoteRecipient,
amount: Felt,
tag: Felt,
) -> Result<Self, NoteError> {
const FIXED_PUBLIC_STORAGE_ITEMS: usize = 16;
let total_storage_items =
FIXED_PUBLIC_STORAGE_ITEMS + recipient.storage().num_items() as usize;
if total_storage_items > MAX_NOTE_STORAGE_ITEMS {
return Err(NoteError::TooManyStorageItems(total_storage_items));
}
Ok(Self::Public {
recipient,
amount,
tag,
attachment: NoteAttachment::default(),
})
}
pub fn with_attachment(self, attachment: NoteAttachment) -> Self {
match self {
MintNoteStorage::Private {
recipient_digest,
amount,
tag,
attachment: _,
} => MintNoteStorage::Private {
recipient_digest,
amount,
tag,
attachment,
},
MintNoteStorage::Public { recipient, amount, tag, attachment: _ } => {
MintNoteStorage::Public { recipient, amount, tag, attachment }
},
}
}
}
impl From<MintNoteStorage> for NoteStorage {
fn from(mint_storage: MintNoteStorage) -> Self {
match mint_storage {
MintNoteStorage::Private {
recipient_digest,
amount,
tag,
attachment,
} => {
let attachment_scheme = Felt::from(attachment.attachment_scheme().as_u32());
let attachment_kind = Felt::from(attachment.attachment_kind().as_u8());
let attachment = attachment.content().to_word();
let mut storage_values = Vec::with_capacity(12);
storage_values.extend_from_slice(&[
tag,
amount,
attachment_kind,
attachment_scheme,
]);
storage_values.extend_from_slice(attachment.as_elements());
storage_values.extend_from_slice(recipient_digest.as_elements());
NoteStorage::new(storage_values)
.expect("number of storage items should not exceed max storage items")
},
MintNoteStorage::Public { recipient, amount, tag, attachment } => {
let attachment_scheme = Felt::from(attachment.attachment_scheme().as_u32());
let attachment_kind = Felt::from(attachment.attachment_kind().as_u8());
let attachment = attachment.content().to_word();
let mut storage_values = vec![tag, amount, attachment_kind, attachment_scheme];
storage_values.extend_from_slice(attachment.as_elements());
storage_values.extend_from_slice(recipient.script().root().as_elements());
storage_values.extend_from_slice(recipient.serial_num().as_elements());
storage_values.extend_from_slice(recipient.storage().items());
NoteStorage::new(storage_values)
.expect("number of storage items should not exceed max storage items")
},
}
}
}