1use alloc::boxed::Box;
2use alloc::string::ToString;
3use core::error::Error;
4
5use miden_protocol::Felt;
6use miden_protocol::account::AccountId;
7use miden_protocol::block::BlockNumber;
8use miden_protocol::note::{Note, NoteScript, NoteScriptRoot};
9
10pub mod costs;
11
12mod allowlist_config;
13pub use allowlist_config::{AllowlistConfig, AllowlistConfigNote};
14
15mod blocklist_config;
16pub use blocklist_config::{BlocklistConfig, BlocklistConfigNote};
17
18mod burn;
19pub use burn::BurnNote;
20
21mod constant_fee_policy_config;
22pub use constant_fee_policy_config::ConstantFeePolicyConfigNote;
23
24mod faucet_metadata_config;
25pub use faucet_metadata_config::{FaucetMetadataConfig, FaucetMetadataConfigNote};
26
27mod faucet_policy_config;
28pub use faucet_policy_config::{FaucetPolicyConfig, FaucetPolicyConfigNote};
29
30mod fee_sponsorship;
31pub use fee_sponsorship::{FeeSponsorshipNote, FeeSponsorshipNoteStorage};
32
33mod execution_hint;
34pub use execution_hint::NoteExecutionHint;
35
36mod file;
37pub use file::{NoteFile, NoteSyncHint};
38
39mod min_burn_amount_config;
40pub use min_burn_amount_config::MinBurnAmountConfigNote;
41
42mod mint;
43pub use mint::{MintNote, MintNoteStorage};
44
45mod network_account_config;
46pub use network_account_config::{NetworkAccountConfig, NetworkAccountConfigNote};
47
48mod owner_config;
49pub use owner_config::{OwnerConfig, OwnerConfigNote};
50
51mod p2id;
52pub use p2id::{P2idNote, P2idNoteStorage};
53
54mod p2ide;
55pub use p2ide::{P2ideNote, P2ideNoteStorage};
56
57mod pause_config;
58pub use pause_config::{PauseConfig, PauseConfigNote};
59
60mod pswap;
61pub use pswap::{PswapNote, PswapNoteAttachment, PswapNoteStorage};
62
63mod rbac_config;
64pub use rbac_config::{RbacConfig, RbacConfigNote};
65
66mod swap;
67pub use swap::{SwapNote, SwapNoteStorage, SwapPayback, payback_serial_from_swap};
68
69mod tx_fee;
70pub use tx_fee::TxFeeNote;
71
72mod network_account_target;
73pub use network_account_target::{NetworkAccountTarget, NetworkAccountTargetError};
74
75mod network_note;
76pub use network_note::{AccountTargetNetworkNote, NetworkNoteExt};
77
78mod standard_note_attachment;
79use miden_protocol::errors::NoteError;
80pub use standard_note_attachment::StandardNoteAttachment;
81#[allow(non_camel_case_types)]
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub enum StandardNote {
88 P2ID,
89 P2IDE,
90 SWAP,
91 PSWAP,
92 MINT,
93 BURN,
94 CONSTANT_FEE_POLICY_CONFIG,
95 FAUCET_POLICY_CONFIG,
96 FAUCET_METADATA_CONFIG,
97 MIN_BURN_AMOUNT_CONFIG,
98 ALLOWLIST_CONFIG,
99 BLOCKLIST_CONFIG,
100 PAUSE_CONFIG,
101 OWNER_CONFIG,
102 RBAC_CONFIG,
103 NETWORK_ACCOUNT_CONFIG,
104 FEE_SPONSORSHIP,
105 TX_FEE,
106}
107
108impl StandardNote {
109 pub fn from_script(script: &NoteScript) -> Option<Self> {
115 Self::from_script_root(script.root())
116 }
117
118 pub fn from_script_root(root: NoteScriptRoot) -> Option<Self> {
121 if root == P2idNote::script_root() {
122 return Some(Self::P2ID);
123 }
124 if root == P2ideNote::script_root() {
125 return Some(Self::P2IDE);
126 }
127 if root == SwapNote::script_root() {
128 return Some(Self::SWAP);
129 }
130 if root == PswapNote::script_root() {
131 return Some(Self::PSWAP);
132 }
133 if root == MintNote::script_root() {
134 return Some(Self::MINT);
135 }
136 if root == BurnNote::script_root() {
137 return Some(Self::BURN);
138 }
139 if root == ConstantFeePolicyConfigNote::script_root() {
140 return Some(Self::CONSTANT_FEE_POLICY_CONFIG);
141 }
142 if root == FaucetPolicyConfigNote::script_root() {
143 return Some(Self::FAUCET_POLICY_CONFIG);
144 }
145 if root == FaucetMetadataConfigNote::script_root() {
146 return Some(Self::FAUCET_METADATA_CONFIG);
147 }
148 if root == MinBurnAmountConfigNote::script_root() {
149 return Some(Self::MIN_BURN_AMOUNT_CONFIG);
150 }
151 if root == AllowlistConfigNote::script_root() {
152 return Some(Self::ALLOWLIST_CONFIG);
153 }
154 if root == BlocklistConfigNote::script_root() {
155 return Some(Self::BLOCKLIST_CONFIG);
156 }
157 if root == PauseConfigNote::script_root() {
158 return Some(Self::PAUSE_CONFIG);
159 }
160 if root == OwnerConfigNote::script_root() {
161 return Some(Self::OWNER_CONFIG);
162 }
163 if root == RbacConfigNote::script_root() {
164 return Some(Self::RBAC_CONFIG);
165 }
166 if root == NetworkAccountConfigNote::script_root() {
167 return Some(Self::NETWORK_ACCOUNT_CONFIG);
168 }
169 if root == FeeSponsorshipNote::script_root() {
170 return Some(Self::FEE_SPONSORSHIP);
171 }
172 if root == TxFeeNote::script_root() {
173 return Some(Self::TX_FEE);
174 }
175
176 None
177 }
178
179 pub fn name(&self) -> &'static str {
184 match self {
185 Self::P2ID => "P2ID",
186 Self::P2IDE => "P2IDE",
187 Self::SWAP => "SWAP",
188 Self::PSWAP => "PSWAP",
189 Self::MINT => "MINT",
190 Self::BURN => "BURN",
191 Self::CONSTANT_FEE_POLICY_CONFIG => "CONSTANT_FEE_POLICY_CONFIG",
192 Self::FAUCET_POLICY_CONFIG => "FAUCET_POLICY_CONFIG",
193 Self::FAUCET_METADATA_CONFIG => "FAUCET_METADATA_CONFIG",
194 Self::MIN_BURN_AMOUNT_CONFIG => "MIN_BURN_AMOUNT_CONFIG",
195 Self::ALLOWLIST_CONFIG => "ALLOWLIST_CONFIG",
196 Self::BLOCKLIST_CONFIG => "BLOCKLIST_CONFIG",
197 Self::PAUSE_CONFIG => "PAUSE_CONFIG",
198 Self::OWNER_CONFIG => "OWNER_CONFIG",
199 Self::RBAC_CONFIG => "RBAC_CONFIG",
200 Self::NETWORK_ACCOUNT_CONFIG => "NETWORK_ACCOUNT_CONFIG",
201 Self::FEE_SPONSORSHIP => "FEE_SPONSORSHIP",
202 Self::TX_FEE => "TX_FEE",
203 }
204 }
205
206 pub fn expected_num_storage_items(&self) -> usize {
208 match self {
209 Self::P2ID => P2idNote::NUM_STORAGE_ITEMS,
210 Self::P2IDE => P2ideNote::NUM_STORAGE_ITEMS,
211 Self::SWAP => SwapNote::NUM_STORAGE_ITEMS,
212 Self::PSWAP => PswapNote::NUM_STORAGE_ITEMS,
213 Self::MINT => MintNote::NUM_STORAGE_ITEMS_PRIVATE,
214 Self::BURN => BurnNote::NUM_STORAGE_ITEMS,
215 Self::CONSTANT_FEE_POLICY_CONFIG => ConstantFeePolicyConfigNote::NUM_STORAGE_ITEMS,
216 Self::FAUCET_POLICY_CONFIG => FaucetPolicyConfigNote::NUM_STORAGE_ITEMS,
217 Self::FAUCET_METADATA_CONFIG => FaucetMetadataConfigNote::MAX_NUM_STORAGE_ITEMS,
219 Self::MIN_BURN_AMOUNT_CONFIG => MinBurnAmountConfigNote::NUM_STORAGE_ITEMS,
220 Self::ALLOWLIST_CONFIG => AllowlistConfigNote::NUM_STORAGE_ITEMS,
221 Self::BLOCKLIST_CONFIG => BlocklistConfigNote::NUM_STORAGE_ITEMS,
222 Self::PAUSE_CONFIG => PauseConfigNote::NUM_STORAGE_ITEMS,
223 Self::OWNER_CONFIG => OwnerConfigNote::MAX_NUM_STORAGE_ITEMS,
225 Self::RBAC_CONFIG => RbacConfigNote::MAX_NUM_STORAGE_ITEMS,
227 Self::NETWORK_ACCOUNT_CONFIG => NetworkAccountConfigNote::NUM_STORAGE_ITEMS,
228 Self::FEE_SPONSORSHIP => FeeSponsorshipNote::NUM_STORAGE_ITEMS,
229 Self::TX_FEE => TxFeeNote::NUM_STORAGE_ITEMS,
230 }
231 }
232
233 pub fn script(&self) -> NoteScript {
235 match self {
236 Self::P2ID => P2idNote::script(),
237 Self::P2IDE => P2ideNote::script(),
238 Self::SWAP => SwapNote::script(),
239 Self::PSWAP => PswapNote::script(),
240 Self::MINT => MintNote::script(),
241 Self::BURN => BurnNote::script(),
242 Self::CONSTANT_FEE_POLICY_CONFIG => ConstantFeePolicyConfigNote::script(),
243 Self::FAUCET_POLICY_CONFIG => FaucetPolicyConfigNote::script(),
244 Self::FAUCET_METADATA_CONFIG => FaucetMetadataConfigNote::script(),
245 Self::MIN_BURN_AMOUNT_CONFIG => MinBurnAmountConfigNote::script(),
246 Self::ALLOWLIST_CONFIG => AllowlistConfigNote::script(),
247 Self::BLOCKLIST_CONFIG => BlocklistConfigNote::script(),
248 Self::PAUSE_CONFIG => PauseConfigNote::script(),
249 Self::OWNER_CONFIG => OwnerConfigNote::script(),
250 Self::RBAC_CONFIG => RbacConfigNote::script(),
251 Self::NETWORK_ACCOUNT_CONFIG => NetworkAccountConfigNote::script(),
252 Self::FEE_SPONSORSHIP => FeeSponsorshipNote::script(),
253 Self::TX_FEE => TxFeeNote::script(),
254 }
255 }
256
257 pub fn script_root(&self) -> NoteScriptRoot {
259 match self {
260 Self::P2ID => P2idNote::script_root(),
261 Self::P2IDE => P2ideNote::script_root(),
262 Self::SWAP => SwapNote::script_root(),
263 Self::PSWAP => PswapNote::script_root(),
264 Self::MINT => MintNote::script_root(),
265 Self::BURN => BurnNote::script_root(),
266 Self::CONSTANT_FEE_POLICY_CONFIG => ConstantFeePolicyConfigNote::script_root(),
267 Self::FAUCET_POLICY_CONFIG => FaucetPolicyConfigNote::script_root(),
268 Self::FAUCET_METADATA_CONFIG => FaucetMetadataConfigNote::script_root(),
269 Self::MIN_BURN_AMOUNT_CONFIG => MinBurnAmountConfigNote::script_root(),
270 Self::ALLOWLIST_CONFIG => AllowlistConfigNote::script_root(),
271 Self::BLOCKLIST_CONFIG => BlocklistConfigNote::script_root(),
272 Self::PAUSE_CONFIG => PauseConfigNote::script_root(),
273 Self::OWNER_CONFIG => OwnerConfigNote::script_root(),
274 Self::RBAC_CONFIG => RbacConfigNote::script_root(),
275 Self::NETWORK_ACCOUNT_CONFIG => NetworkAccountConfigNote::script_root(),
276 Self::FEE_SPONSORSHIP => FeeSponsorshipNote::script_root(),
277 Self::TX_FEE => TxFeeNote::script_root(),
278 }
279 }
280
281 pub fn is_consumable(
290 &self,
291 note: &Note,
292 target_account_id: AccountId,
293 block_ref: BlockNumber,
294 ) -> Option<NoteConsumptionStatus> {
295 match self.is_consumable_inner(note, target_account_id, block_ref) {
296 Ok(status) => status,
297 Err(err) => {
298 let err: Box<dyn Error + Send + Sync + 'static> = Box::from(err);
299 Some(NoteConsumptionStatus::NeverConsumable(err))
300 },
301 }
302 }
303
304 fn is_consumable_inner(
321 &self,
322 note: &Note,
323 target_account_id: AccountId,
324 block_ref: BlockNumber,
325 ) -> Result<Option<NoteConsumptionStatus>, NoteError> {
326 match self {
327 StandardNote::P2ID => {
328 let input_account_id = P2idNoteStorage::try_from(note.storage().items())
329 .map_err(|e| NoteError::other_with_source("invalid P2ID note storage", e))?;
330
331 if input_account_id.target() == target_account_id {
332 Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
333 } else {
334 Ok(Some(NoteConsumptionStatus::NeverConsumable("account ID provided to the P2ID note storage doesn't match the target account ID".into())))
335 }
336 },
337 StandardNote::P2IDE => {
338 let storage = P2ideNoteStorage::try_from(note.storage().items())
339 .map_err(|e| NoteError::other_with_source("invalid P2IDE note storage", e))?;
340
341 let reclaimer_account_id = storage.reclaimer();
342 let receiver_account_id = storage.target();
343
344 let current_block_height = block_ref.as_u32();
345 let reclaim_height = storage.reclaim_height().unwrap_or_default().as_u32();
346 let timelock_height = storage.timelock_height().unwrap_or_default().as_u32();
347
348 let consumable_after = reclaim_height.max(timelock_height);
350
351 if target_account_id == reclaimer_account_id {
353 if current_block_height >= consumable_after {
356 Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
357 } else {
358 Ok(Some(NoteConsumptionStatus::ConsumableAfter(BlockNumber::from(
359 consumable_after,
360 ))))
361 }
362 } else if target_account_id == receiver_account_id {
364 if current_block_height >= timelock_height {
368 Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
369 } else {
370 Ok(Some(NoteConsumptionStatus::ConsumableAfter(BlockNumber::from(
371 timelock_height,
372 ))))
373 }
374 } else {
377 Ok(Some(NoteConsumptionStatus::NeverConsumable(
378 "target account of the transaction does not match neither the receiver account specified by the P2IDE storage, nor the reclaimer account".into()
379 )))
380 }
381 },
382
383 StandardNote::TX_FEE => {
387 if usize::from(note.storage().num_items()) != TxFeeNote::NUM_STORAGE_ITEMS {
388 Ok(Some(NoteConsumptionStatus::NeverConsumable(
389 "TX_FEE note carries unexpected storage items".into(),
390 )))
391 } else {
392 Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
393 }
394 },
395
396 _ => Ok(None),
399 }
400 }
401}
402
403pub(crate) fn decode_optional_block_height(
410 item: Felt,
411 error_msg: &'static str,
412) -> Result<Option<BlockNumber>, NoteError> {
413 if item == Felt::ZERO {
414 return Ok(None);
415 }
416
417 let height: u32 = item
418 .as_canonical_u64()
419 .try_into()
420 .map_err(|e| NoteError::other_with_source(error_msg, e))?;
421
422 Ok(Some(BlockNumber::from(height)))
423}
424
425#[derive(Debug)]
434pub enum NoteConsumptionStatus {
435 Consumable,
437 ConsumableAfter(BlockNumber),
439 ConsumableWithAuthorization,
441 UnconsumableConditions,
444 NeverConsumable(Box<dyn Error + Send + Sync + 'static>),
446}
447
448impl Clone for NoteConsumptionStatus {
449 fn clone(&self) -> Self {
450 match self {
451 NoteConsumptionStatus::Consumable => NoteConsumptionStatus::Consumable,
452 NoteConsumptionStatus::ConsumableAfter(block_height) => {
453 NoteConsumptionStatus::ConsumableAfter(*block_height)
454 },
455 NoteConsumptionStatus::ConsumableWithAuthorization => {
456 NoteConsumptionStatus::ConsumableWithAuthorization
457 },
458 NoteConsumptionStatus::UnconsumableConditions => {
459 NoteConsumptionStatus::UnconsumableConditions
460 },
461 NoteConsumptionStatus::NeverConsumable(error) => {
462 let err = error.to_string();
463 NoteConsumptionStatus::NeverConsumable(err.into())
464 },
465 }
466 }
467}