use alloc::boxed::Box;
use alloc::string::ToString;
use core::error::Error;
use miden_protocol::Felt;
use miden_protocol::account::AccountId;
use miden_protocol::block::BlockNumber;
use miden_protocol::note::{Note, NoteScript, NoteScriptRoot};
pub mod costs;
mod allowlist_config;
pub use allowlist_config::{AllowlistConfig, AllowlistConfigNote};
mod blocklist_config;
pub use blocklist_config::{BlocklistConfig, BlocklistConfigNote};
mod burn;
pub use burn::BurnNote;
mod constant_fee_policy_config;
pub use constant_fee_policy_config::ConstantFeePolicyConfigNote;
mod faucet_metadata_config;
pub use faucet_metadata_config::{FaucetMetadataConfig, FaucetMetadataConfigNote};
mod faucet_policy_config;
pub use faucet_policy_config::{FaucetPolicyConfig, FaucetPolicyConfigNote};
mod fee_sponsorship;
pub use fee_sponsorship::{FeeSponsorshipNote, FeeSponsorshipNoteStorage};
mod execution_hint;
pub use execution_hint::NoteExecutionHint;
mod file;
pub use file::{NoteFile, NoteSyncHint};
mod min_burn_amount_config;
pub use min_burn_amount_config::MinBurnAmountConfigNote;
mod mint;
pub use mint::{MintNote, MintNoteStorage};
mod network_account_config;
pub use network_account_config::{NetworkAccountConfig, NetworkAccountConfigNote};
mod owner_config;
pub use owner_config::{OwnerConfig, OwnerConfigNote};
mod p2id;
pub use p2id::{P2idNote, P2idNoteStorage};
mod p2ide;
pub use p2ide::{P2ideNote, P2ideNoteStorage};
mod pause_config;
pub use pause_config::{PauseConfig, PauseConfigNote};
mod pswap;
pub use pswap::{PswapNote, PswapNoteAttachment, PswapNoteStorage};
mod rbac_config;
pub use rbac_config::{RbacConfig, RbacConfigNote};
mod swap;
pub use swap::{SwapNote, SwapNoteStorage, SwapPayback, payback_serial_from_swap};
mod tx_fee;
pub use tx_fee::TxFeeNote;
mod network_account_target;
pub use network_account_target::{NetworkAccountTarget, NetworkAccountTargetError};
mod network_note;
pub use network_note::{AccountTargetNetworkNote, NetworkNoteExt};
mod standard_note_attachment;
use miden_protocol::errors::NoteError;
pub use standard_note_attachment::StandardNoteAttachment;
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StandardNote {
P2ID,
P2IDE,
SWAP,
PSWAP,
MINT,
BURN,
CONSTANT_FEE_POLICY_CONFIG,
FAUCET_POLICY_CONFIG,
FAUCET_METADATA_CONFIG,
MIN_BURN_AMOUNT_CONFIG,
ALLOWLIST_CONFIG,
BLOCKLIST_CONFIG,
PAUSE_CONFIG,
OWNER_CONFIG,
RBAC_CONFIG,
NETWORK_ACCOUNT_CONFIG,
FEE_SPONSORSHIP,
TX_FEE,
}
impl StandardNote {
pub fn from_script(script: &NoteScript) -> Option<Self> {
Self::from_script_root(script.root())
}
pub fn from_script_root(root: NoteScriptRoot) -> Option<Self> {
if root == P2idNote::script_root() {
return Some(Self::P2ID);
}
if root == P2ideNote::script_root() {
return Some(Self::P2IDE);
}
if root == SwapNote::script_root() {
return Some(Self::SWAP);
}
if root == PswapNote::script_root() {
return Some(Self::PSWAP);
}
if root == MintNote::script_root() {
return Some(Self::MINT);
}
if root == BurnNote::script_root() {
return Some(Self::BURN);
}
if root == ConstantFeePolicyConfigNote::script_root() {
return Some(Self::CONSTANT_FEE_POLICY_CONFIG);
}
if root == FaucetPolicyConfigNote::script_root() {
return Some(Self::FAUCET_POLICY_CONFIG);
}
if root == FaucetMetadataConfigNote::script_root() {
return Some(Self::FAUCET_METADATA_CONFIG);
}
if root == MinBurnAmountConfigNote::script_root() {
return Some(Self::MIN_BURN_AMOUNT_CONFIG);
}
if root == AllowlistConfigNote::script_root() {
return Some(Self::ALLOWLIST_CONFIG);
}
if root == BlocklistConfigNote::script_root() {
return Some(Self::BLOCKLIST_CONFIG);
}
if root == PauseConfigNote::script_root() {
return Some(Self::PAUSE_CONFIG);
}
if root == OwnerConfigNote::script_root() {
return Some(Self::OWNER_CONFIG);
}
if root == RbacConfigNote::script_root() {
return Some(Self::RBAC_CONFIG);
}
if root == NetworkAccountConfigNote::script_root() {
return Some(Self::NETWORK_ACCOUNT_CONFIG);
}
if root == FeeSponsorshipNote::script_root() {
return Some(Self::FEE_SPONSORSHIP);
}
if root == TxFeeNote::script_root() {
return Some(Self::TX_FEE);
}
None
}
pub fn name(&self) -> &'static str {
match self {
Self::P2ID => "P2ID",
Self::P2IDE => "P2IDE",
Self::SWAP => "SWAP",
Self::PSWAP => "PSWAP",
Self::MINT => "MINT",
Self::BURN => "BURN",
Self::CONSTANT_FEE_POLICY_CONFIG => "CONSTANT_FEE_POLICY_CONFIG",
Self::FAUCET_POLICY_CONFIG => "FAUCET_POLICY_CONFIG",
Self::FAUCET_METADATA_CONFIG => "FAUCET_METADATA_CONFIG",
Self::MIN_BURN_AMOUNT_CONFIG => "MIN_BURN_AMOUNT_CONFIG",
Self::ALLOWLIST_CONFIG => "ALLOWLIST_CONFIG",
Self::BLOCKLIST_CONFIG => "BLOCKLIST_CONFIG",
Self::PAUSE_CONFIG => "PAUSE_CONFIG",
Self::OWNER_CONFIG => "OWNER_CONFIG",
Self::RBAC_CONFIG => "RBAC_CONFIG",
Self::NETWORK_ACCOUNT_CONFIG => "NETWORK_ACCOUNT_CONFIG",
Self::FEE_SPONSORSHIP => "FEE_SPONSORSHIP",
Self::TX_FEE => "TX_FEE",
}
}
pub fn expected_num_storage_items(&self) -> usize {
match self {
Self::P2ID => P2idNote::NUM_STORAGE_ITEMS,
Self::P2IDE => P2ideNote::NUM_STORAGE_ITEMS,
Self::SWAP => SwapNote::NUM_STORAGE_ITEMS,
Self::PSWAP => PswapNote::NUM_STORAGE_ITEMS,
Self::MINT => MintNote::NUM_STORAGE_ITEMS_PRIVATE,
Self::BURN => BurnNote::NUM_STORAGE_ITEMS,
Self::CONSTANT_FEE_POLICY_CONFIG => ConstantFeePolicyConfigNote::NUM_STORAGE_ITEMS,
Self::FAUCET_POLICY_CONFIG => FaucetPolicyConfigNote::NUM_STORAGE_ITEMS,
Self::FAUCET_METADATA_CONFIG => FaucetMetadataConfigNote::MAX_NUM_STORAGE_ITEMS,
Self::MIN_BURN_AMOUNT_CONFIG => MinBurnAmountConfigNote::NUM_STORAGE_ITEMS,
Self::ALLOWLIST_CONFIG => AllowlistConfigNote::NUM_STORAGE_ITEMS,
Self::BLOCKLIST_CONFIG => BlocklistConfigNote::NUM_STORAGE_ITEMS,
Self::PAUSE_CONFIG => PauseConfigNote::NUM_STORAGE_ITEMS,
Self::OWNER_CONFIG => OwnerConfigNote::MAX_NUM_STORAGE_ITEMS,
Self::RBAC_CONFIG => RbacConfigNote::MAX_NUM_STORAGE_ITEMS,
Self::NETWORK_ACCOUNT_CONFIG => NetworkAccountConfigNote::NUM_STORAGE_ITEMS,
Self::FEE_SPONSORSHIP => FeeSponsorshipNote::NUM_STORAGE_ITEMS,
Self::TX_FEE => TxFeeNote::NUM_STORAGE_ITEMS,
}
}
pub fn script(&self) -> NoteScript {
match self {
Self::P2ID => P2idNote::script(),
Self::P2IDE => P2ideNote::script(),
Self::SWAP => SwapNote::script(),
Self::PSWAP => PswapNote::script(),
Self::MINT => MintNote::script(),
Self::BURN => BurnNote::script(),
Self::CONSTANT_FEE_POLICY_CONFIG => ConstantFeePolicyConfigNote::script(),
Self::FAUCET_POLICY_CONFIG => FaucetPolicyConfigNote::script(),
Self::FAUCET_METADATA_CONFIG => FaucetMetadataConfigNote::script(),
Self::MIN_BURN_AMOUNT_CONFIG => MinBurnAmountConfigNote::script(),
Self::ALLOWLIST_CONFIG => AllowlistConfigNote::script(),
Self::BLOCKLIST_CONFIG => BlocklistConfigNote::script(),
Self::PAUSE_CONFIG => PauseConfigNote::script(),
Self::OWNER_CONFIG => OwnerConfigNote::script(),
Self::RBAC_CONFIG => RbacConfigNote::script(),
Self::NETWORK_ACCOUNT_CONFIG => NetworkAccountConfigNote::script(),
Self::FEE_SPONSORSHIP => FeeSponsorshipNote::script(),
Self::TX_FEE => TxFeeNote::script(),
}
}
pub fn script_root(&self) -> NoteScriptRoot {
match self {
Self::P2ID => P2idNote::script_root(),
Self::P2IDE => P2ideNote::script_root(),
Self::SWAP => SwapNote::script_root(),
Self::PSWAP => PswapNote::script_root(),
Self::MINT => MintNote::script_root(),
Self::BURN => BurnNote::script_root(),
Self::CONSTANT_FEE_POLICY_CONFIG => ConstantFeePolicyConfigNote::script_root(),
Self::FAUCET_POLICY_CONFIG => FaucetPolicyConfigNote::script_root(),
Self::FAUCET_METADATA_CONFIG => FaucetMetadataConfigNote::script_root(),
Self::MIN_BURN_AMOUNT_CONFIG => MinBurnAmountConfigNote::script_root(),
Self::ALLOWLIST_CONFIG => AllowlistConfigNote::script_root(),
Self::BLOCKLIST_CONFIG => BlocklistConfigNote::script_root(),
Self::PAUSE_CONFIG => PauseConfigNote::script_root(),
Self::OWNER_CONFIG => OwnerConfigNote::script_root(),
Self::RBAC_CONFIG => RbacConfigNote::script_root(),
Self::NETWORK_ACCOUNT_CONFIG => NetworkAccountConfigNote::script_root(),
Self::FEE_SPONSORSHIP => FeeSponsorshipNote::script_root(),
Self::TX_FEE => TxFeeNote::script_root(),
}
}
pub fn is_consumable(
&self,
note: &Note,
target_account_id: AccountId,
block_ref: BlockNumber,
) -> Option<NoteConsumptionStatus> {
match self.is_consumable_inner(note, target_account_id, block_ref) {
Ok(status) => status,
Err(err) => {
let err: Box<dyn Error + Send + Sync + 'static> = Box::from(err);
Some(NoteConsumptionStatus::NeverConsumable(err))
},
}
}
fn is_consumable_inner(
&self,
note: &Note,
target_account_id: AccountId,
block_ref: BlockNumber,
) -> Result<Option<NoteConsumptionStatus>, NoteError> {
match self {
StandardNote::P2ID => {
let input_account_id = P2idNoteStorage::try_from(note.storage().items())
.map_err(|e| NoteError::other_with_source("invalid P2ID note storage", e))?;
if input_account_id.target() == target_account_id {
Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
} else {
Ok(Some(NoteConsumptionStatus::NeverConsumable("account ID provided to the P2ID note storage doesn't match the target account ID".into())))
}
},
StandardNote::P2IDE => {
let storage = P2ideNoteStorage::try_from(note.storage().items())
.map_err(|e| NoteError::other_with_source("invalid P2IDE note storage", e))?;
let reclaimer_account_id = storage.reclaimer();
let receiver_account_id = storage.target();
let current_block_height = block_ref.as_u32();
let reclaim_height = storage.reclaim_height().unwrap_or_default().as_u32();
let timelock_height = storage.timelock_height().unwrap_or_default().as_u32();
let consumable_after = reclaim_height.max(timelock_height);
if target_account_id == reclaimer_account_id {
if current_block_height >= consumable_after {
Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
} else {
Ok(Some(NoteConsumptionStatus::ConsumableAfter(BlockNumber::from(
consumable_after,
))))
}
} else if target_account_id == receiver_account_id {
if current_block_height >= timelock_height {
Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
} else {
Ok(Some(NoteConsumptionStatus::ConsumableAfter(BlockNumber::from(
timelock_height,
))))
}
} else {
Ok(Some(NoteConsumptionStatus::NeverConsumable(
"target account of the transaction does not match neither the receiver account specified by the P2IDE storage, nor the reclaimer account".into()
)))
}
},
StandardNote::TX_FEE => {
if usize::from(note.storage().num_items()) != TxFeeNote::NUM_STORAGE_ITEMS {
Ok(Some(NoteConsumptionStatus::NeverConsumable(
"TX_FEE note carries unexpected storage items".into(),
)))
} else {
Ok(Some(NoteConsumptionStatus::ConsumableWithAuthorization))
}
},
_ => Ok(None),
}
}
}
pub(crate) fn decode_optional_block_height(
item: Felt,
error_msg: &'static str,
) -> Result<Option<BlockNumber>, NoteError> {
if item == Felt::ZERO {
return Ok(None);
}
let height: u32 = item
.as_canonical_u64()
.try_into()
.map_err(|e| NoteError::other_with_source(error_msg, e))?;
Ok(Some(BlockNumber::from(height)))
}
#[derive(Debug)]
pub enum NoteConsumptionStatus {
Consumable,
ConsumableAfter(BlockNumber),
ConsumableWithAuthorization,
UnconsumableConditions,
NeverConsumable(Box<dyn Error + Send + Sync + 'static>),
}
impl Clone for NoteConsumptionStatus {
fn clone(&self) -> Self {
match self {
NoteConsumptionStatus::Consumable => NoteConsumptionStatus::Consumable,
NoteConsumptionStatus::ConsumableAfter(block_height) => {
NoteConsumptionStatus::ConsumableAfter(*block_height)
},
NoteConsumptionStatus::ConsumableWithAuthorization => {
NoteConsumptionStatus::ConsumableWithAuthorization
},
NoteConsumptionStatus::UnconsumableConditions => {
NoteConsumptionStatus::UnconsumableConditions
},
NoteConsumptionStatus::NeverConsumable(error) => {
let err = error.to_string();
NoteConsumptionStatus::NeverConsumable(err.into())
},
}
}
}