miden_standards/note/
min_burn_amount_config.rs1use alloc::vec::Vec;
2
3use miden_protocol::account::AccountId;
4use miden_protocol::assembly::Path;
5use miden_protocol::asset::AssetAmount;
6use miden_protocol::crypto::rand::FeltRng;
7use miden_protocol::errors::NoteError;
8use miden_protocol::note::{
9 Note,
10 NoteAssets,
11 NoteAttachment,
12 NoteAttachments,
13 NoteRecipient,
14 NoteScript,
15 NoteScriptRoot,
16 NoteStorage,
17 NoteTag,
18 NoteType,
19 PartialNoteMetadata,
20};
21use miden_protocol::utils::sync::LazyLock;
22use miden_protocol::{Felt, Word};
23
24use crate::StandardsLib;
25use crate::note::NetworkAccountTarget;
26use crate::note::costs::{MIN_BURN_AMOUNT_CONFIG_CONSUMPTION_CYCLES, NoteConsumptionCost};
27
28const MIN_BURN_AMOUNT_CONFIG_SCRIPT_PATH: &str =
33 "::miden::standards::notes::min_burn_amount_config::main";
34
35static MIN_BURN_AMOUNT_CONFIG_SCRIPT: LazyLock<NoteScript> = LazyLock::new(|| {
37 let standards_lib = StandardsLib::default();
38 let path = Path::new(MIN_BURN_AMOUNT_CONFIG_SCRIPT_PATH);
39 NoteScript::from_package_reference(standards_lib.as_ref(), path)
40 .expect("Standards library contains MIN_BURN_AMOUNT_CONFIG note script procedure")
41});
42
43#[derive(Debug, Clone)]
74pub struct MinBurnAmountConfigNote {
75 sender: AccountId,
76 target: AccountId,
77 min_burn_amount: AssetAmount,
78 serial_number: Word,
79 attachments: NoteAttachments,
80}
81
82#[bon::bon]
83impl MinBurnAmountConfigNote {
84 #[builder]
95 pub fn new(
96 #[builder(field)] mut attachments: Vec<NoteAttachment>,
97 sender: AccountId,
98 target: AccountId,
99 min_burn_amount: AssetAmount,
100 serial_number: Word,
101 ) -> Result<Self, NoteError> {
102 NetworkAccountTarget::ensure_presence(&mut attachments, target).map_err(|err| {
105 NoteError::other_with_source(
106 "failed to bind the MinBurnAmountConfig note to its target account",
107 err,
108 )
109 })?;
110
111 let attachments = NoteAttachments::new(attachments)?;
112
113 Ok(Self {
114 sender,
115 target,
116 min_burn_amount,
117 serial_number,
118 attachments,
119 })
120 }
121}
122
123impl MinBurnAmountConfigNote {
124 pub const NUM_STORAGE_ITEMS: usize = 1;
131
132 pub fn script() -> NoteScript {
137 MIN_BURN_AMOUNT_CONFIG_SCRIPT.clone()
138 }
139
140 pub fn script_root() -> NoteScriptRoot {
142 MIN_BURN_AMOUNT_CONFIG_SCRIPT.root()
143 }
144
145 pub fn sender(&self) -> AccountId {
147 self.sender
148 }
149
150 pub fn target(&self) -> AccountId {
153 self.target
154 }
155
156 pub fn min_burn_amount(&self) -> AssetAmount {
158 self.min_burn_amount
159 }
160
161 pub fn serial_number(&self) -> Word {
163 self.serial_number
164 }
165
166 pub fn attachments(&self) -> &NoteAttachments {
168 &self.attachments
169 }
170}
171
172impl<S: min_burn_amount_config_note_builder::State> MinBurnAmountConfigNoteBuilder<S> {
176 pub fn attachment(mut self, attachment: impl Into<NoteAttachment>) -> Self {
178 self.attachments.push(attachment.into());
179 self
180 }
181
182 pub fn attachments(
184 mut self,
185 attachments: impl IntoIterator<Item = impl Into<NoteAttachment>>,
186 ) -> Self {
187 self.attachments.extend(attachments.into_iter().map(Into::into));
188 self
189 }
190}
191
192impl<S: min_burn_amount_config_note_builder::State> MinBurnAmountConfigNoteBuilder<S>
193where
194 S::SerialNumber: min_burn_amount_config_note_builder::IsUnset,
195{
196 pub fn generate_serial_number(
198 self,
199 rng: &mut impl FeltRng,
200 ) -> MinBurnAmountConfigNoteBuilder<min_burn_amount_config_note_builder::SetSerialNumber<S>>
201 {
202 self.serial_number(rng.draw_word())
203 }
204}
205
206impl From<MinBurnAmountConfigNote> for Note {
210 fn from(note: MinBurnAmountConfigNote) -> Self {
211 let metadata = PartialNoteMetadata::new(note.sender, NoteType::Public)
214 .with_tag(NoteTag::with_account_target(note.target));
215 let storage = NoteStorage::new(vec![Felt::from(note.min_burn_amount)])
216 .expect("number of storage items should not exceed max storage items");
217 let recipient =
218 NoteRecipient::new(note.serial_number, MinBurnAmountConfigNote::script(), storage);
219
220 Note::with_attachments(NoteAssets::default(), metadata, recipient, note.attachments)
221 }
222}
223
224impl NoteConsumptionCost for MinBurnAmountConfigNote {
228 fn consumption_cycles() -> u32 {
229 MIN_BURN_AMOUNT_CONFIG_CONSUMPTION_CYCLES
230 }
231}
232
233#[cfg(test)]
237mod tests {
238 use miden_protocol::account::AccountType;
239 use miden_protocol::crypto::rand::RandomCoin;
240
241 use super::*;
242
243 fn account_id(seed: u8) -> AccountId {
244 AccountId::builder()
245 .account_type(AccountType::Public)
246 .build_with_seed([seed; 32])
247 }
248
249 #[test]
251 fn builder_builds_min_burn_amount_config_note() {
252 let mut rng = RandomCoin::new(Word::empty());
253 let faucet = account_id(1);
254 let sender = account_id(2);
255
256 let note = MinBurnAmountConfigNote::builder()
257 .sender(sender)
258 .target(faucet)
259 .min_burn_amount(AssetAmount::new(100).unwrap())
260 .generate_serial_number(&mut rng)
261 .build()
262 .unwrap();
263
264 assert_eq!(note.sender(), sender);
265 assert_eq!(note.target(), faucet);
266 assert_eq!(note.min_burn_amount(), AssetAmount::new(100).unwrap());
267
268 let note = Note::from(note);
269 assert_eq!(note.metadata().note_type(), NoteType::Public);
270 assert_eq!(note.metadata().tag(), NoteTag::with_account_target(faucet));
271 assert_eq!(note.assets().num_assets(), 0);
272 }
273
274 #[test]
277 fn note_is_bound_to_target_account() {
278 let faucet = account_id(1);
279 let note = MinBurnAmountConfigNote::builder()
280 .sender(account_id(2))
281 .target(faucet)
282 .min_burn_amount(AssetAmount::new(100).unwrap())
283 .serial_number(Word::empty())
284 .build()
285 .unwrap();
286
287 let built = Note::from(note);
288 let target = NetworkAccountTarget::try_from(built.attachments())
289 .expect("note should carry a network account target attachment");
290 assert_eq!(target.target_id(), faucet);
291 }
292
293 #[test]
295 fn storage_layout() {
296 let min_burn_amount = AssetAmount::new(777).unwrap();
297
298 let note = MinBurnAmountConfigNote::builder()
299 .sender(account_id(2))
300 .target(account_id(1))
301 .min_burn_amount(min_burn_amount)
302 .serial_number(Word::empty())
303 .build()
304 .unwrap();
305
306 let built = Note::from(note);
307 assert_eq!(built.storage().items(), [Felt::from(min_burn_amount)]);
308 assert_eq!(built.storage().items().len(), MinBurnAmountConfigNote::NUM_STORAGE_ITEMS);
309 }
310
311 #[test]
314 fn script_root_is_registered_standard_note() {
315 use crate::note::StandardNote;
316
317 let standard = StandardNote::from_script_root(MinBurnAmountConfigNote::script_root())
318 .expect("config note script root should be a registered standard note");
319 assert_eq!(standard.name(), "MIN_BURN_AMOUNT_CONFIG");
320 }
321}