extern crate alloc;
use alloc::collections::BTreeSet;
use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
use miden_core::{Felt, Word};
use miden_protocol::account::component::AccountComponentMetadata;
use miden_protocol::account::{Account, AccountComponent, AccountId, StorageSlot, StorageSlotName};
use miden_protocol::asset::{AssetAmount, TokenSymbol};
use miden_protocol::errors::AccountIdError;
use miden_protocol::note::NoteScriptRoot;
use miden_standards::account::access::{Authority, Ownable2Step};
use miden_standards::account::auth::AuthNetworkAccount;
use miden_standards::account::faucets::{FungibleFaucet, FungibleFaucetError, TokenName};
use miden_standards::account::policies::TokenPolicyManager;
pub use miden_standards::interop::eth::{
EthAddress,
EthAmount,
EthAmountError,
EthEmbeddedAccountId,
};
use miden_standards::note::{BurnNote, ConstantFeePolicyConfigNote, MintNote};
use thiserror::Error;
use super::agglayer_faucet_component_package;
pub use crate::{
AggLayerBridge,
B2AggNote,
ClaimNoteStorage,
ConfigAggBridgeNote,
ExitRoot,
GlobalIndex,
GlobalIndexError,
LeafData,
MetadataHash,
ProofData,
SmtNode,
UpdateGerNote,
};
include!(concat!(env!("OUT_DIR"), "/agglayer_constants.rs"));
#[derive(Debug, Clone)]
pub struct AggLayerFaucet {
faucet: FungibleFaucet,
}
impl AggLayerFaucet {
pub fn new(
symbol: TokenSymbol,
decimals: u8,
max_supply: Felt,
token_supply: Felt,
) -> Result<Self, FungibleFaucetError> {
let name = TokenName::new(symbol.to_string().as_str())
.expect("symbol fits within token name capacity");
let max_supply_amount = AssetAmount::try_from(max_supply).map_err(|_| {
FungibleFaucetError::MaxSupplyTooLarge {
actual: max_supply.as_canonical_u64(),
max: AssetAmount::MAX.as_u64(),
}
})?;
let token_supply_amount = AssetAmount::try_from(token_supply).map_err(|_| {
FungibleFaucetError::MaxSupplyTooLarge {
actual: token_supply.as_canonical_u64(),
max: AssetAmount::MAX.as_u64(),
}
})?;
let faucet = FungibleFaucet::builder()
.name(name)
.symbol(symbol)
.decimals(decimals)
.max_supply(max_supply_amount)
.token_supply(token_supply_amount)
.build()?;
Ok(Self { faucet })
}
pub fn with_token_supply(mut self, token_supply: Felt) -> Result<Self, FungibleFaucetError> {
let token_supply_amount = AssetAmount::try_from(token_supply).map_err(|_| {
FungibleFaucetError::MaxSupplyTooLarge {
actual: token_supply.as_canonical_u64(),
max: AssetAmount::MAX.as_u64(),
}
})?;
self.faucet = self.faucet.with_token_supply(token_supply_amount)?;
Ok(self)
}
pub fn token_config_slot() -> &'static StorageSlotName {
FungibleFaucet::token_config_slot()
}
pub fn owner_config_slot() -> &'static StorageSlotName {
Ownable2Step::slot_name()
}
pub fn allowed_notes() -> BTreeSet<NoteScriptRoot> {
let mut notes = BTreeSet::from([
MintNote::script_root(),
BurnNote::script_root(),
ConstantFeePolicyConfigNote::script_root(),
]);
notes.extend(AuthNetworkAccount::default_allowed_note_scripts());
notes
}
pub fn try_faucet_from_account(
faucet_account: &Account,
) -> Result<FungibleFaucet, AgglayerFaucetError> {
Self::assert_faucet_account(faucet_account)?;
FungibleFaucet::try_from(faucet_account.storage())
.map_err(AgglayerFaucetError::FungibleFaucetError)
}
pub fn owner_account_id(faucet_account: &Account) -> Result<AccountId, AgglayerFaucetError> {
Self::assert_faucet_account(faucet_account)?;
let ownership = Ownable2Step::try_from_storage(faucet_account.storage())
.map_err(AgglayerFaucetError::Ownable2StepError)?;
ownership.owner().ok_or(AgglayerFaucetError::OwnershipRenounced)
}
fn assert_faucet_account(account: &Account) -> Result<(), AgglayerFaucetError> {
Self::assert_storage_slots(account)?;
Self::assert_code_commitment(account)?;
Ok(())
}
fn assert_storage_slots(account: &Account) -> Result<(), AgglayerFaucetError> {
let account_storage_slot_names: Vec<&StorageSlotName> = account
.storage()
.slots()
.iter()
.map(|storage_slot| storage_slot.name())
.collect::<Vec<&StorageSlotName>>();
let are_slots_present = Self::slot_names()
.iter()
.all(|slot_name| account_storage_slot_names.contains(slot_name));
if !are_slots_present {
return Err(AgglayerFaucetError::StorageSlotsMismatch);
}
Ok(())
}
fn assert_code_commitment(account: &Account) -> Result<(), AgglayerFaucetError> {
if FAUCET_CODE_COMMITMENT != account.code().commitment() {
return Err(AgglayerFaucetError::CodeCommitmentMismatch);
}
Ok(())
}
fn slot_names() -> Vec<&'static StorageSlotName> {
vec![
FungibleFaucet::token_config_slot(),
Ownable2Step::slot_name(),
Authority::authority_slot(),
TokenPolicyManager::active_mint_policy_slot(),
TokenPolicyManager::active_burn_policy_slot(),
TokenPolicyManager::allowed_mint_policies_slot(),
TokenPolicyManager::allowed_burn_policies_slot(),
TokenPolicyManager::allowed_send_policies_slot(),
TokenPolicyManager::allowed_receive_policies_slot(),
]
}
}
impl From<AggLayerFaucet> for AccountComponent {
fn from(agglayer_faucet: AggLayerFaucet) -> Self {
agglayer_faucet_component(agglayer_faucet.faucet.into_storage_slots())
}
}
#[derive(Debug, Error)]
pub enum AgglayerFaucetError {
#[error(
"provided account does not have storage slots required for the AggLayer Faucet account"
)]
StorageSlotsMismatch,
#[error("provided account does not have procedures required for the AggLayer Faucet account")]
CodeCommitmentMismatch,
#[error("fungible faucet error")]
FungibleFaucetError(#[source] FungibleFaucetError),
#[error("account ID error")]
AccountIdError(#[source] AccountIdError),
#[error("ownable2step error")]
Ownable2StepError(#[source] miden_standards::account::access::Ownable2StepError),
#[error("faucet ownership has been renounced")]
OwnershipRenounced,
}
fn agglayer_faucet_component(storage_slots: Vec<StorageSlot>) -> AccountComponent {
let package = agglayer_faucet_component_package();
let metadata = AccountComponentMetadata::new("agglayer::faucet")
.with_description("AggLayer faucet component");
AccountComponent::new(package, storage_slots, metadata).expect(
"agglayer_faucet component should satisfy the requirements of a valid account component",
)
}