Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
- Added an `AccountBuilder` extension trait to help build the schema commitment; added `AccountComponentMetadata` to `AccountComponent` ([#2269](https://github.com/0xMiden/miden-base/pull/2269)).
- Added `miden::standards::access::ownable` standard module for component ownership management, and integrated it into the `network_fungible` faucet (including new tests). ([#2228](https://github.com/0xMiden/miden-base/pull/2228)).
- [BREAKING] Add `leaf_value` to `CLAIM` note inputs ([#2290](https://github.com/0xMiden/miden-base/pull/2290)).
- Added `miden::standards::access::pausable` standard module for regulated management of assets a regulated faucet (including new tests). ([#2228](https://github.com/0xMiden/miden-base/pull/2291)).

### Changes

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# The MASM code of the Regulated Network Fungible Faucet Account Component.
#
# This component extends NetworkFungibleFaucet with pausable functionality.
# It uses both ownable and pausable modules, making it suitable for regulated
# assets like stablecoins that require pause controls.
#
# This component re-exports procedures from regulated_network_fungible.masm,
# which extends network_fungible.masm with pause checks in distribute and burn.

pub use ::miden::standards::faucets::regulated_network_fungible::distribute
pub use ::miden::standards::faucets::regulated_network_fungible::burn
pub use ::miden::standards::faucets::regulated_network_fungible::get_owner
pub use ::miden::standards::faucets::regulated_network_fungible::is_not_paused
pub use ::miden::standards::faucets::regulated_network_fungible::transfer_ownership
pub use ::miden::standards::faucets::regulated_network_fungible::renounce_ownership
pub use ::miden::standards::faucets::regulated_network_fungible::pause
pub use ::miden::standards::faucets::regulated_network_fungible::unpause
125 changes: 125 additions & 0 deletions crates/miden-standards/asm/standards/access/pausable.masm
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# miden::standards::access::pausable
#
# Provides pausable functionality for account components.
# This module can be imported and used by any component that needs pause controls.
#
# Usage:
# use miden::standards::access::pausable
#
# # In account procedures (not from note scripts):
# exec.pausable::is_not_paused
# exec.pausable::pause
# exec.pausable::unpause
#
# IMPORTANT CONTEXT REQUIREMENTS:
# - is_not_paused, pause, and unpause MUST be called from Account context (i.e., from account procedures)
# - They CANNOT be called directly from note scripts using exec
# - Note scripts should call account procedures, which then call these pausable procedures
#
# NOTE: The pause and unpause procedures do NOT check ownership. The caller must ensure
# that only_owner is called before calling these procedures.

use miden::protocol::active_account
use miden::protocol::native_account

# CONSTANTS
# ================================================================================================

# The slot in this component's storage layout where the paused state is stored.
# This must match the slot name used in the Rust code.
const PAUSABLE_SLOT=word("miden::standards::access::pausable::paused")

# ERRORS
# ================================================================================================

const ERR_IS_PAUSED="contract is paused"

# INTERNAL PROCEDURES
# ================================================================================================

#! Checks if the component is not paused.
#!
#! Inputs: []
#! Outputs: []
#!
#! Panics if:
#! - the component is paused (storage slot value is not zero).
#!
#! CONTEXT REQUIREMENT:
#! - MUST be called from Account context (from an account procedure)
#! - CANNOT be called directly from note scripts using exec
#! - Note scripts should call account procedures that internally call this procedure
#!
#! NOTE: Uses active_account::get_item which authenticates account origin via
#! authenticate_account_origin. This means it can only be called from account procedures,
#! not directly from note scripts. This is by design - pause checks should be integrated
#! into account procedures (e.g., distribute, burn) rather than called directly from notes.
pub proc is_not_paused
push.PAUSABLE_SLOT[0..2] exec.active_account::get_item
# => [paused_flag, 0, 0, 0]
# Paused state is stored as Word[1, 0, 0, 0] when paused, Word[0, 0, 0, 0] when unpaused
# get_item (mem_loadw_le) returns word[0] on top of stack
# Check the flag (top of stack) first, then clean up the remaining zeros
assertz.err=ERR_IS_PAUSED
# => [0, 0, 0] (if paused_flag was 0, otherwise it panics)
drop drop drop
# => []
end

# PUBLIC INTERFACE
# ================================================================================================

#! Pauses the component, preventing certain operations.
#!
#! Can only be called by the owner (requires owner check before calling this).
#!
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! NOTE: This procedure does NOT check ownership. The caller must ensure
#! that only_owner is called before this procedure.
#!
#! Invocation: call
pub proc pause
# Write Word[1, 0, 0, 0] to storage to indicate paused state
# push.0.0.0.1 pushes left-to-right, so 1 (last) ends up on top of stack
# set_item uses mem_storew_le: top of stack → word[0], so word[0] = 1
push.0.0.0.1
# => [1, 0, 0, 0, pad(16)]

push.PAUSABLE_SLOT[0..2]
# => [slot_suffix, slot_prefix, 1, 0, 0, 0, pad(16)]

exec.native_account::set_item
# => [OLD_PAUSED_WORD, pad(16)]

dropw
# => [pad(16)]
end

#! Unpauses the component, allowing operations to resume.
#!
#! Can only be called by the owner (requires owner check before calling this).
#!
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! NOTE: This procedure does NOT check ownership. The caller must ensure
#! that only_owner is called before this procedure.
#!
#! Invocation: call
pub proc unpause
# Write [0, 0, 0, 0] to storage to indicate unpaused state
# Using [0, 0, 0, 0] for consistency with [1, 0, 0, 0] paused state
push.0.0.0.0
# => [0, 0, 0, 0, pad(16)]

push.PAUSABLE_SLOT[0..2]
# => [slot_prefix, slot_suffix, 0, 0, 0, 0, pad(16)]

exec.native_account::set_item
# => [OLD_PAUSED_WORD, pad(16)]

dropw
# => [pad(16)]
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
use miden::standards::faucets
use miden::standards::access::ownable
use miden::standards::access::pausable

# PUBLIC INTERFACE
# ================================================================================================

#! Checks if the faucet is not paused.
#!
#! Inputs: []
#! Outputs: []
#!
#! Panics if:
#! - the faucet is paused (storage slot value is not zero).
#!
#! NOTE: Uses exec.pausable::is_not_paused to ensure it uses the same execution context
#! and storage slot (PAUSABLE_SLOT) as defined in pausable.masm.
pub use pausable::is_not_paused

#! Returns the owner AccountId.
#!
#! Inputs: []
#! Outputs: [owner_prefix, owner_suffix]
#!
#! Invocation: call
#!
#! NOTE: Reuses ownable::get_owner
pub use ownable::get_owner

#! Distributes freshly minted fungible assets to the provided recipient.
#!
#! This procedure first checks if the note sender is the owner of the faucet, then
#! checks if the faucet is not paused, and finally mints the asset and creates an
#! output note with that asset for the recipient.
#!
#! Inputs: [amount, tag, note_type, RECIPIENT, pad(9)]
#! Outputs: [note_idx, pad(15)]
#!
#! Where:
#! - amount is the amount to be minted and sent.
#! - tag is the tag to be included in the note.
#! - note_type is the type of the note that holds the asset.
#! - RECIPIENT is the recipient of the asset.
#! - note_idx is the index of the created note.
#!
#! Panics if:
#! - the note sender is not the owner of this faucet.
#! - the faucet is paused.
#! - any of the validations in faucets::distribute fail.
#!
#! Invocation: call
pub proc distribute
exec.ownable::verify_owner
# => [amount, tag, note_type, RECIPIENT, pad(9)]

exec.is_not_paused
# => [amount, tag, note_type, RECIPIENT, pad(9)]

exec.faucets::distribute
# => [note_idx, pad(15)]
end

#! Burns the fungible asset from the active note.
#!
#! This procedure retrieves the asset from the active note and burns it. The note must contain
#! exactly one asset, which must be a fungible asset issued by this faucet.
#!
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - the faucet is paused.
#! - the procedure is not called from a note context (active_note::get_assets will fail).
#! - the note does not contain exactly one asset.
#! - the transaction is executed against an account which is not a fungible asset faucet.
#! - the transaction is executed against a faucet which is not the origin of the specified asset.
#! - the amount about to be burned is greater than the outstanding supply of the asset.
#!
#! Invocation: call
pub proc burn
exec.pausable::is_not_paused
# => [pad(16)]

exec.faucets::burn
# => [pad(16)]
end

#! Transfers ownership to a new account.
#!
#! Can only be called by the current owner.
#!
#! Inputs: [new_owner_prefix, new_owner_suffix, pad(14)]
#! Outputs: [pad(16)]
#!
#! Where:
#! - new_owner_{prefix, suffix} are the prefix and suffix felts of the new owner AccountId.
#!
#! Panics if:
#! - the note sender is not the owner.
#!
#! Invocation: call
#!
#! NOTE: Reuses ownable::transfer_ownership
pub use ownable::transfer_ownership

#! Renounces ownership, leaving the component without an owner.
#!
#! Can only be called by the current owner.
#!
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - the note sender is not the owner.
#!
#! Invocation: call
#!
#! NOTE: Reuses ownable::renounce_ownership
pub use ownable::renounce_ownership

#! Pauses the faucet, preventing minting and burning operations.
#!
#! Can only be called by the owner.
#!
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - the note sender is not the owner.
#!
#! Invocation: call
pub proc pause
exec.ownable::verify_owner
# => [pad(16)]

exec.pausable::pause
# => [pad(16)]
end

#! Unpauses the faucet, allowing minting and burning operations to resume.
#!
#! Can only be called by the owner.
#!
#! Inputs: [pad(16)]
#! Outputs: [pad(16)]
#!
#! Panics if:
#! - the note sender is not the owner.
#!
#! Invocation: call
pub proc unpause
exec.ownable::verify_owner
# => [pad(16)]

exec.pausable::unpause
# => [pad(16)]
end
15 changes: 15 additions & 0 deletions crates/miden-standards/src/account/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,16 @@ static NETWORK_FUNGIBLE_FAUCET_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
Library::read_from_bytes(bytes).expect("Shipped Network Fungible Faucet library is well-formed")
});

// Initialize the Regulated Network Fungible Faucet library only once.
static REGULATED_NETWORK_FUNGIBLE_FAUCET_LIBRARY: LazyLock<Library> = LazyLock::new(|| {
let bytes = include_bytes!(concat!(
env!("OUT_DIR"),
"/assets/account_components/faucets/regulated_network_fungible_faucet.masl"
));
Library::read_from_bytes(bytes)
.expect("Shipped Regulated Network Fungible Faucet library is well-formed")
});

// METADATA LIBRARIES
// ================================================================================================

Expand Down Expand Up @@ -112,6 +122,11 @@ pub fn network_fungible_faucet_library() -> Library {
NETWORK_FUNGIBLE_FAUCET_LIBRARY.clone()
}

/// Returns the Regulated Network Fungible Faucet Library.
pub fn regulated_network_fungible_faucet_library() -> Library {
REGULATED_NETWORK_FUNGIBLE_FAUCET_LIBRARY.clone()
}

/// Returns the Storage Schema Library.
pub fn storage_schema_library() -> Library {
STORAGE_SCHEMA_LIBRARY.clone()
Expand Down
5 changes: 5 additions & 0 deletions crates/miden-standards/src/account/faucets/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,15 @@ use crate::account::access::Ownable2StepError;

mod basic_fungible;
mod network_fungible;
mod regulated_network_fungible;
mod token_metadata;

pub use basic_fungible::{BasicFungibleFaucet, create_basic_fungible_faucet};
pub use network_fungible::{NetworkFungibleFaucet, create_network_fungible_faucet};
pub use regulated_network_fungible::{
RegulatedNetworkFungibleFaucet,
create_regulated_network_fungible_faucet,
};
pub use token_metadata::TokenMetadata;

// FUNGIBLE FAUCET ERROR
Expand Down
Loading