miden_standards/note/
burn.rs1use alloc::vec::Vec;
2
3use miden_protocol::Word;
4use miden_protocol::account::AccountId;
5use miden_protocol::assembly::Path;
6use miden_protocol::asset::Asset;
7use miden_protocol::crypto::rand::FeltRng;
8use miden_protocol::errors::NoteError;
9use miden_protocol::note::{
10 Note,
11 NoteAssets,
12 NoteAttachment,
13 NoteAttachments,
14 NoteRecipient,
15 NoteScript,
16 NoteScriptRoot,
17 NoteStorage,
18 NoteTag,
19 NoteType,
20 PartialNoteMetadata,
21};
22use miden_protocol::utils::sync::LazyLock;
23
24use crate::StandardsLib;
25
26const BURN_SCRIPT_PATH: &str = "::miden::standards::notes::burn::main";
31
32static BURN_SCRIPT: LazyLock<NoteScript> = LazyLock::new(|| {
34 let standards_lib = StandardsLib::default();
35 let path = Path::new(BURN_SCRIPT_PATH);
36 NoteScript::from_library_reference(standards_lib.as_ref(), path)
37 .expect("Standards library contains BURN note script procedure")
38});
39
40#[derive(Debug, Clone)]
54pub struct BurnNote {
55 sender: AccountId,
56 serial_number: Word,
57 asset: Asset,
58 attachments: NoteAttachments,
59}
60
61#[bon::bon]
62impl BurnNote {
63 #[builder]
72 pub fn new(
73 #[builder(field)] attachments: Vec<NoteAttachment>,
74 sender: AccountId,
75 #[builder(into)] asset: Asset,
76 serial_number: Word,
77 ) -> Result<Self, NoteError> {
78 let attachments = NoteAttachments::new(attachments)?;
79
80 Ok(Self {
81 sender,
82 serial_number,
83 asset,
84 attachments,
85 })
86 }
87}
88
89impl BurnNote {
90 pub const NUM_STORAGE_ITEMS: usize = 0;
95
96 pub fn script() -> NoteScript {
101 BURN_SCRIPT.clone()
102 }
103
104 pub fn script_root() -> NoteScriptRoot {
106 BURN_SCRIPT.root()
107 }
108
109 pub fn sender(&self) -> AccountId {
111 self.sender
112 }
113
114 pub fn faucet_id(&self) -> AccountId {
116 self.asset.faucet_id()
117 }
118
119 pub fn serial_number(&self) -> Word {
121 self.serial_number
122 }
123
124 pub fn asset(&self) -> Asset {
126 self.asset
127 }
128
129 pub fn attachments(&self) -> &NoteAttachments {
131 &self.attachments
132 }
133}
134
135impl<S: burn_note_builder::State> BurnNoteBuilder<S> {
139 pub fn attachment(mut self, attachment: impl Into<NoteAttachment>) -> Self {
141 self.attachments.push(attachment.into());
142 self
143 }
144
145 pub fn attachments(
147 mut self,
148 attachments: impl IntoIterator<Item = impl Into<NoteAttachment>>,
149 ) -> Self {
150 self.attachments.extend(attachments.into_iter().map(Into::into));
151 self
152 }
153}
154
155impl<S: burn_note_builder::State> BurnNoteBuilder<S>
156where
157 S::SerialNumber: burn_note_builder::IsUnset,
158{
159 pub fn generate_serial_number(
161 self,
162 rng: &mut impl FeltRng,
163 ) -> BurnNoteBuilder<burn_note_builder::SetSerialNumber<S>> {
164 self.serial_number(rng.draw_word())
165 }
166}
167
168impl From<BurnNote> for Note {
172 fn from(note: BurnNote) -> Self {
173 let metadata = PartialNoteMetadata::new(note.sender, NoteType::Public)
176 .with_tag(NoteTag::with_account_target(note.asset.faucet_id()));
177 let recipient =
178 NoteRecipient::new(note.serial_number, BurnNote::script(), NoteStorage::default());
179
180 let assets = NoteAssets::new(vec![note.asset])
181 .expect("a single asset never exceeds the note asset limit");
182 Note::with_attachments(assets, metadata, recipient, note.attachments)
183 }
184}
185
186#[cfg(test)]
190mod tests {
191 use miden_protocol::account::AccountType;
192 use miden_protocol::asset::FungibleAsset;
193 use miden_protocol::crypto::rand::RandomCoin;
194
195 use super::*;
196
197 fn sender() -> AccountId {
198 AccountId::builder().account_type(AccountType::Private).build_with_seed([1; 32])
199 }
200
201 fn faucet() -> AccountId {
202 AccountId::builder().account_type(AccountType::Public).build_with_seed([2; 32])
203 }
204
205 #[test]
207 fn builder_builds_public_burn_note() {
208 let mut rng = RandomCoin::new(Word::empty());
209 let asset = FungibleAsset::new(faucet(), 100).unwrap();
210
211 let burn_note = BurnNote::builder()
212 .sender(sender())
213 .asset(asset)
214 .generate_serial_number(&mut rng)
215 .build()
216 .unwrap();
217
218 assert_eq!(burn_note.sender(), sender());
219 assert_eq!(burn_note.faucet_id(), faucet());
220 assert_eq!(burn_note.asset(), asset.into());
221 assert_ne!(burn_note.serial_number(), Word::empty());
222
223 let note = Note::from(burn_note);
224 assert_eq!(note.metadata().note_type(), NoteType::Public);
225 assert_eq!(note.metadata().tag(), NoteTag::with_account_target(faucet()));
226 assert_eq!(note.assets().num_assets(), 1);
227 }
228}