use alloc::collections::BTreeSet;
use miden_protocol::Word;
use miden_protocol::account::component::{SchemaType, StorageSlotSchema};
use miden_protocol::account::{AccountId, StorageMap, StorageMapKey, StorageSlot, StorageSlotName};
use miden_protocol::block::account_tree::AccountIdKey;
use miden_protocol::utils::sync::LazyLock;
mod owner_controlled;
pub use owner_controlled::BlocklistOwnerControlled;
static BLOCKED_ACCOUNTS_SLOT_NAME: LazyLock<StorageSlotName> = LazyLock::new(|| {
StorageSlotName::new(
"miden::standards::faucets::policies::transfer::blocklist::blocked_accounts",
)
.expect("storage slot name should be valid")
});
#[derive(Debug, Clone, Default)]
pub struct BlocklistStorage {
blocked_accounts: BTreeSet<AccountId>,
}
impl BlocklistStorage {
pub fn new() -> Self {
Self::default()
}
pub fn with_blocked_accounts(blocked_accounts: impl IntoIterator<Item = AccountId>) -> Self {
Self {
blocked_accounts: blocked_accounts.into_iter().collect(),
}
}
pub fn blocked_accounts(&self) -> &BTreeSet<AccountId> {
&self.blocked_accounts
}
pub fn blocked_accounts_slot() -> &'static StorageSlotName {
&BLOCKED_ACCOUNTS_SLOT_NAME
}
pub fn blocked_accounts_slot_schema() -> (StorageSlotName, StorageSlotSchema) {
(
Self::blocked_accounts_slot().clone(),
StorageSlotSchema::map(
"Per-account blocked flag; zero word is not blocked, [1,0,0,0] is blocked",
SchemaType::native_word(),
SchemaType::bool(),
),
)
}
pub fn build_storage_map(&self) -> StorageMap {
let blocked_word = Word::from([1u32, 0, 0, 0]);
StorageMap::with_entries(
self.blocked_accounts
.iter()
.map(|id| (StorageMapKey::new(AccountIdKey::from(*id).as_word()), blocked_word)),
)
.expect("initial blocked accounts should have unique IDs")
}
pub fn into_slot(self) -> StorageSlot {
StorageSlot::with_map(BLOCKED_ACCOUNTS_SLOT_NAME.clone(), self.build_storage_map())
}
}