use alloc::vec::Vec;
use miden_protocol::account::AccountId;
use miden_protocol::assembly::Path;
use miden_protocol::asset::{Asset, FungibleAsset};
use miden_protocol::crypto::rand::FeltRng;
use miden_protocol::errors::NoteError;
use miden_protocol::note::{
Note,
NoteAssets,
NoteAttachments,
NoteRecipient,
NoteScript,
NoteScriptRoot,
NoteStorage,
NoteTag,
NoteType,
PartialNoteMetadata,
};
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 = 13;
pub const MIN_NUM_STORAGE_ITEMS_PUBLIC: usize = 20;
pub fn script() -> NoteScript {
MINT_SCRIPT.clone()
}
pub fn script_root() -> NoteScriptRoot {
MINT_SCRIPT.root()
}
pub fn create<R: FeltRng>(
faucet_id: AccountId,
sender: AccountId,
mint_storage: MintNoteStorage,
attachments: NoteAttachments,
rng: &mut R,
) -> Result<Note, NoteError> {
if faucet_id != mint_storage.asset().faucet_id() {
return Err(NoteError::other(
"faucet_id must equal the faucet ID of the asset embedded in mint_storage",
));
}
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 = PartialNoteMetadata::new(sender, note_type).with_tag(tag);
let assets = NoteAssets::new(vec![])?; let recipient = NoteRecipient::new(serial_num, note_script, storage);
Ok(Note::with_attachments(assets, metadata, recipient, attachments))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MintNoteStorage {
Private {
recipient_digest: Word,
asset: FungibleAsset,
tag: Felt,
},
Public {
recipient: NoteRecipient,
asset: FungibleAsset,
tag: Felt,
},
}
impl MintNoteStorage {
pub fn new_private(recipient_digest: Word, asset: FungibleAsset, tag: Felt) -> Self {
Self::Private { recipient_digest, asset, tag }
}
pub fn new_public(
recipient: NoteRecipient,
asset: FungibleAsset,
tag: Felt,
) -> Result<Self, NoteError> {
let total_storage_items =
MintNote::MIN_NUM_STORAGE_ITEMS_PUBLIC + 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, asset, tag })
}
pub fn asset(&self) -> FungibleAsset {
match self {
Self::Private { asset, .. } | Self::Public { asset, .. } => *asset,
}
}
}
impl From<MintNoteStorage> for NoteStorage {
fn from(mint_storage: MintNoteStorage) -> Self {
match mint_storage {
MintNoteStorage::Private { recipient_digest, asset, tag } => {
let mut storage_values = Vec::with_capacity(MintNote::NUM_STORAGE_ITEMS_PRIVATE);
storage_values.extend_from_slice(recipient_digest.as_elements());
storage_values.extend_from_slice(&Asset::from(asset).as_elements());
storage_values.push(tag);
NoteStorage::new(storage_values)
.expect("number of storage items should not exceed max storage items")
},
MintNoteStorage::Public { recipient, asset, tag } => {
let mut storage_values = Vec::new();
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(&Asset::from(asset).as_elements());
storage_values.extend_from_slice(&[tag, Felt::ZERO, Felt::ZERO, Felt::ZERO]);
storage_values.extend_from_slice(recipient.storage().items());
NoteStorage::new(storage_values)
.expect("number of storage items should not exceed max storage items")
},
}
}
}