use miden_protocol::account::component::{
AccountComponentCode,
AccountComponentMetadata,
FeltSchema,
StorageSchema,
StorageSlotSchema,
};
use miden_protocol::account::{
AccountComponent,
AccountComponentName,
AccountId,
AccountStorage,
StorageSlot,
StorageSlotName,
};
use miden_protocol::errors::AccountIdError;
use miden_protocol::utils::sync::LazyLock;
use miden_protocol::{Felt, Word};
use super::account_id_from_felt_pair;
use crate::account::account_component_code;
account_component_code!(WARDEN_CODE, "miden-standards-access-warden.masp");
static WARDEN_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
StorageSlotName::new("miden::standards::access::warden::warden_id")
.expect("storage slot name should be valid")
});
pub struct Warden {
warden: Option<AccountId>,
}
impl Warden {
pub const NAME: &'static str = "miden::standards::access::warden";
pub const fn name() -> AccountComponentName {
AccountComponentName::from_static_str(Self::NAME)
}
pub fn code() -> &'static AccountComponentCode {
&WARDEN_CODE
}
pub fn new(warden: AccountId) -> Self {
Self { warden: Some(warden) }
}
pub fn unassigned() -> Self {
Self { warden: None }
}
pub fn try_from_storage(storage: &AccountStorage) -> Result<Self, WardenError> {
let word: Word =
storage.get_item(Self::slot_name()).map_err(WardenError::StorageLookupFailed)?;
Self::try_from_word(word)
}
pub fn try_from_word(word: Word) -> Result<Self, WardenError> {
let warden =
account_id_from_felt_pair(word[0], word[1]).map_err(WardenError::InvalidWardenId)?;
Ok(Self { warden })
}
pub fn slot_name() -> &'static StorageSlotName {
&WARDEN_SLOT_NAME
}
pub fn slot_schema() -> (StorageSlotName, StorageSlotSchema) {
(
Self::slot_name().clone(),
StorageSlotSchema::value(
"Warden account ID",
[
FeltSchema::felt("warden_suffix"),
FeltSchema::felt("warden_prefix"),
FeltSchema::felt("unused_0"),
FeltSchema::felt("unused_1"),
],
),
)
}
pub fn account_id(&self) -> Option<AccountId> {
self.warden
}
pub fn to_storage_slot(&self) -> StorageSlot {
StorageSlot::with_value(Self::slot_name().clone(), self.to_word())
}
pub fn to_word(&self) -> Word {
let (warden_suffix, warden_prefix) = match self.warden {
Some(id) => (id.suffix(), id.prefix().as_felt()),
None => (Felt::ZERO, Felt::ZERO),
};
[warden_suffix, warden_prefix, Felt::ZERO, Felt::ZERO].into()
}
pub fn component_metadata() -> AccountComponentMetadata {
let storage_schema =
StorageSchema::new([Self::slot_schema()]).expect("storage schema should be valid");
AccountComponentMetadata::new(Self::NAME)
.with_description("Standalone warden actor component")
.with_storage_schema(storage_schema)
}
}
impl From<Warden> for AccountComponent {
fn from(warden: Warden) -> Self {
let storage_slot = warden.to_storage_slot();
let metadata = Warden::component_metadata();
AccountComponent::new(Warden::code().clone(), vec![storage_slot], metadata)
.expect("Warden component should satisfy the requirements of a valid account component")
}
}
#[derive(Debug, thiserror::Error)]
pub enum WardenError {
#[error("failed to read warden slot from storage")]
StorageLookupFailed(#[source] miden_protocol::errors::AccountError),
#[error("invalid warden account ID in storage")]
InvalidWardenId(#[source] AccountIdError),
}