use miden_objects::account::{
Account,
AccountBuilder,
AccountComponent,
AccountId,
AccountStorage,
AccountStorageMode,
AccountType,
StorageSlot,
};
use miden_objects::asset::TokenSymbol;
use miden_objects::{Felt, FieldElement, Word};
use super::{BasicFungibleFaucet, FungibleFaucetError};
use crate::account::auth::NoAuth;
use crate::account::components::network_fungible_faucet_library;
use crate::account::interface::{AccountComponentInterface, AccountInterface};
use crate::procedure_digest;
procedure_digest!(
NETWORK_FUNGIBLE_FAUCET_DISTRIBUTE,
NetworkFungibleFaucet::DISTRIBUTE_PROC_NAME,
network_fungible_faucet_library
);
procedure_digest!(
NETWORK_FUNGIBLE_FAUCET_BURN,
NetworkFungibleFaucet::BURN_PROC_NAME,
network_fungible_faucet_library
);
pub struct NetworkFungibleFaucet {
faucet: BasicFungibleFaucet,
owner_account_id: AccountId,
}
impl NetworkFungibleFaucet {
pub const MAX_DECIMALS: u8 = 12;
const DISTRIBUTE_PROC_NAME: &str = "distribute";
const BURN_PROC_NAME: &str = "burn";
pub fn new(
symbol: TokenSymbol,
decimals: u8,
max_supply: Felt,
owner_account_id: AccountId,
) -> Result<Self, FungibleFaucetError> {
let faucet = BasicFungibleFaucet::new(symbol, decimals, max_supply)?;
Ok(Self { faucet, owner_account_id })
}
fn try_from_interface(
interface: AccountInterface,
storage: &AccountStorage,
) -> Result<Self, FungibleFaucetError> {
for component in interface.components().iter() {
if let AccountComponentInterface::NetworkFungibleFaucet(offset) = component {
let faucet_metadata = storage
.get_item(*offset)
.map_err(|_| FungibleFaucetError::InvalidStorageOffset(*offset))?;
let [max_supply, decimals, token_symbol, _] = *faucet_metadata;
let owner_account_id_word: Word = storage
.get_item(*offset + 1)
.map_err(|_| FungibleFaucetError::InvalidStorageOffset(*offset + 1))?;
let prefix = owner_account_id_word[3];
let suffix = owner_account_id_word[2];
let owner_account_id = AccountId::new_unchecked([prefix, suffix]);
let token_symbol = TokenSymbol::try_from(token_symbol)
.map_err(FungibleFaucetError::InvalidTokenSymbol)?;
let decimals = decimals.as_int().try_into().map_err(|_| {
FungibleFaucetError::TooManyDecimals {
actual: decimals.as_int(),
max: Self::MAX_DECIMALS,
}
})?;
let faucet = BasicFungibleFaucet::new(token_symbol, decimals, max_supply)?;
return Ok(Self { faucet, owner_account_id });
}
}
Err(FungibleFaucetError::NoAvailableInterface)
}
pub fn symbol(&self) -> TokenSymbol {
self.faucet.symbol()
}
pub fn decimals(&self) -> u8 {
self.faucet.decimals()
}
pub fn max_supply(&self) -> Felt {
self.faucet.max_supply()
}
pub fn owner_account_id(&self) -> AccountId {
self.owner_account_id
}
pub fn distribute_digest() -> Word {
*NETWORK_FUNGIBLE_FAUCET_DISTRIBUTE
}
pub fn burn_digest() -> Word {
*NETWORK_FUNGIBLE_FAUCET_BURN
}
}
impl From<NetworkFungibleFaucet> for AccountComponent {
fn from(network_faucet: NetworkFungibleFaucet) -> Self {
let metadata = Word::new([
network_faucet.faucet.max_supply(),
Felt::from(network_faucet.faucet.decimals()),
network_faucet.faucet.symbol().into(),
Felt::ZERO,
]);
let owner_account_id_word: Word = [
Felt::new(0),
Felt::new(0),
network_faucet.owner_account_id.suffix(),
network_faucet.owner_account_id.prefix().as_felt(),
]
.into();
let owner_slot = StorageSlot::Value(owner_account_id_word);
AccountComponent::new(
network_fungible_faucet_library(),
vec![StorageSlot::Value(metadata), owner_slot]
)
.expect("network fungible faucet component should satisfy the requirements of a valid account component")
.with_supported_type(AccountType::FungibleFaucet)
}
}
impl TryFrom<Account> for NetworkFungibleFaucet {
type Error = FungibleFaucetError;
fn try_from(account: Account) -> Result<Self, Self::Error> {
let account_interface = AccountInterface::from(&account);
NetworkFungibleFaucet::try_from_interface(account_interface, account.storage())
}
}
impl TryFrom<&Account> for NetworkFungibleFaucet {
type Error = FungibleFaucetError;
fn try_from(account: &Account) -> Result<Self, Self::Error> {
let account_interface = AccountInterface::from(account);
NetworkFungibleFaucet::try_from_interface(account_interface, account.storage())
}
}
pub fn create_network_fungible_faucet(
init_seed: [u8; 32],
symbol: TokenSymbol,
decimals: u8,
max_supply: Felt,
owner_account_id: AccountId,
) -> Result<Account, FungibleFaucetError> {
let auth_component: AccountComponent = NoAuth::new().into();
let account = AccountBuilder::new(init_seed)
.account_type(AccountType::FungibleFaucet)
.storage_mode(AccountStorageMode::Network)
.with_auth_component(auth_component)
.with_component(NetworkFungibleFaucet::new(symbol, decimals, max_supply, owner_account_id)?)
.build()
.map_err(FungibleFaucetError::AccountError)?;
Ok(account)
}