Skip to main content

miden_usdcx/note/xreserve_admin/
min_burn_amount.rs

1//! The min-burn admin note: the standard min-burn-amount config note, constrained so the burn
2//! floor cannot be lowered to zero.
3//!
4//! The note itself is entirely standard — its script asserts the note's target binding, then calls
5//! the standard burn policy's `set_min_burn_amount`, which authorizes the note sender through the
6//! account-wide authority. Nothing about it is faucet-specific and nothing here reimplements it:
7//! the standard [`MinBurnAmountConfigNote`] supplies the script, its root, and the builder
8//! directly, and the allowlist and callable-surface checks use it as-is.
9//!
10//! What is faucet-specific is which notes can be created. The faucet seeds its burn floor at
11//! least [`MIN_BURN_SIZE_FLOOR`] and the builder rejects anything lower, but the standard setter
12//! validates nothing about its value — a zero floor would admit zero-amount burn notes. So
13//! [`XReserveMinBurnAmountNote::builder`] refuses to build such a note in the first place — the
14//! one guard the standard note cannot express.
15//!
16//! That refusal is a guard against operator error, not an authorization boundary.
17
18use core::fmt;
19
20use miden_protocol::account::AccountId;
21use miden_protocol::asset::AssetAmount;
22use miden_protocol::crypto::rand::FeltRng;
23use miden_protocol::errors::NoteError;
24use miden_protocol::note::{Note, NoteScript, NoteScriptRoot};
25use miden_standards::note::config::MinBurnAmountConfigNote;
26
27use crate::account::xreserve::MIN_BURN_SIZE_FLOOR;
28
29/// Why a min-burn admin note could not be built.
30#[derive(Debug)]
31#[non_exhaustive]
32pub enum XReserveMinBurnAmountNoteError {
33    /// The note would have lowered the burn floor below [`MIN_BURN_SIZE_FLOOR`].
34    MinBurnAmountTooSmall { min_burn_amount: u64 },
35    /// The standard note could not be assembled.
36    Note(NoteError),
37}
38
39impl fmt::Display for XReserveMinBurnAmountNoteError {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            Self::MinBurnAmountTooSmall { min_burn_amount } => write!(
43                f,
44                "refusing to build a note that sets the burn floor to {min_burn_amount}, below \
45                 the floor {MIN_BURN_SIZE_FLOOR}; a zero floor would admit zero-amount burn notes"
46            ),
47            Self::Note(_) => {
48                write!(f, "the standard min-burn-amount config note could not be built")
49            },
50        }
51    }
52}
53
54impl core::error::Error for XReserveMinBurnAmountNoteError {
55    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
56        match self {
57            Self::Note(source) => Some(source),
58            Self::MinBurnAmountTooSmall { .. } => None,
59        }
60    }
61}
62
63impl From<NoteError> for XReserveMinBurnAmountNoteError {
64    fn from(source: NoteError) -> Self {
65        Self::Note(source)
66    }
67}
68
69/// The min-burn admin note factory: the standard [`MinBurnAmountConfigNote`] constrained by the
70/// zero-floor refusal. There is no faucet-owned script behind this type.
71pub struct XReserveMinBurnAmountNote;
72
73#[bon::bon]
74impl XReserveMinBurnAmountNote {
75    /// The `miden-standards` min-burn-amount config note script.
76    pub fn script() -> NoteScript {
77        MinBurnAmountConfigNote::script()
78    }
79
80    /// The `miden-standards` min-burn-amount config note script root.
81    pub fn script_root() -> NoteScriptRoot {
82        MinBurnAmountConfigNote::script_root()
83    }
84
85    /// Builds a note that sets `target`'s burn floor to `min_burn_amount` over the standard
86    /// [`MinBurnAmountConfigNote`].
87    ///
88    /// # Errors
89    ///
90    /// Returns [`XReserveMinBurnAmountNoteError::MinBurnAmountTooSmall`] if `min_burn_amount` is
91    /// below [`MIN_BURN_SIZE_FLOOR`]: a zero floor would admit zero-amount burn notes. The
92    /// refusal is a construction-time gate only — on chain the note runs the unmodified
93    /// `miden-standards` script, which does not validate the value. Returns
94    /// [`XReserveMinBurnAmountNoteError::Note`] if the standard note cannot be assembled.
95    #[builder]
96    pub fn new<R: FeltRng>(
97        sender: AccountId,
98        target: AccountId,
99        min_burn_amount: AssetAmount,
100        generate_serial_number: &mut R,
101    ) -> Result<Note, XReserveMinBurnAmountNoteError> {
102        if min_burn_amount.as_u64() < MIN_BURN_SIZE_FLOOR {
103            return Err(XReserveMinBurnAmountNoteError::MinBurnAmountTooSmall {
104                min_burn_amount: min_burn_amount.as_u64(),
105            });
106        }
107        let note = MinBurnAmountConfigNote::builder()
108            .sender(sender)
109            .target(target)
110            .min_burn_amount(min_burn_amount)
111            .generate_serial_number(generate_serial_number)
112            .build()
113            .map_err(XReserveMinBurnAmountNoteError::Note)?;
114        Ok(Note::from(note))
115    }
116}