miden_lib/account/faucets/
mod.rs1use miden_objects::{
2 AccountError, Felt, FieldElement, Word,
3 account::{
4 Account, AccountBuilder, AccountComponent, AccountIdAnchor, AccountStorageMode,
5 AccountType, StorageSlot,
6 },
7 asset::{FungibleAsset, TokenSymbol},
8};
9
10use super::AuthScheme;
11use crate::account::{auth::RpoFalcon512, components::basic_fungible_faucet_library};
12
13pub struct BasicFungibleFaucet {
33 symbol: TokenSymbol,
34 decimals: u8,
35 max_supply: Felt,
36}
37
38impl BasicFungibleFaucet {
39 pub const MAX_DECIMALS: u8 = 12;
44
45 pub fn new(symbol: TokenSymbol, decimals: u8, max_supply: Felt) -> Result<Self, AccountError> {
50 if decimals > Self::MAX_DECIMALS {
52 return Err(AccountError::FungibleFaucetTooManyDecimals {
53 actual: decimals,
54 max: Self::MAX_DECIMALS,
55 });
56 } else if max_supply.as_int() > FungibleAsset::MAX_AMOUNT {
57 return Err(AccountError::FungibleFaucetMaxSupplyTooLarge {
58 actual: max_supply.as_int(),
59 max: FungibleAsset::MAX_AMOUNT,
60 });
61 }
62
63 Ok(Self { symbol, decimals, max_supply })
64 }
65}
66
67impl From<BasicFungibleFaucet> for AccountComponent {
68 fn from(faucet: BasicFungibleFaucet) -> Self {
69 let metadata =
72 [faucet.max_supply, Felt::from(faucet.decimals), faucet.symbol.into(), Felt::ZERO];
73
74 AccountComponent::new(basic_fungible_faucet_library(), vec![StorageSlot::Value(metadata)])
75 .expect("basic fungible faucet component should satisfy the requirements of a valid account component")
76 .with_supported_type(AccountType::FungibleFaucet)
77 }
78}
79
80pub fn create_basic_fungible_faucet(
99 init_seed: [u8; 32],
100 id_anchor: AccountIdAnchor,
101 symbol: TokenSymbol,
102 decimals: u8,
103 max_supply: Felt,
104 account_storage_mode: AccountStorageMode,
105 auth_scheme: AuthScheme,
106) -> Result<(Account, Word), AccountError> {
107 let auth_component: RpoFalcon512 = match auth_scheme {
110 AuthScheme::RpoFalcon512 { pub_key } => RpoFalcon512::new(pub_key),
111 };
112
113 let (account, account_seed) = AccountBuilder::new(init_seed)
114 .anchor(id_anchor)
115 .account_type(AccountType::FungibleFaucet)
116 .storage_mode(account_storage_mode)
117 .with_component(auth_component)
118 .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply)?)
119 .build()?;
120
121 Ok((account, account_seed))
122}
123
124#[cfg(test)]
128mod tests {
129 use miden_objects::{
130 FieldElement, ONE, block::BlockHeader, crypto::dsa::rpo_falcon512, digest,
131 };
132 use vm_processor::Word;
133
134 use super::{AccountStorageMode, AuthScheme, Felt, TokenSymbol, create_basic_fungible_faucet};
135
136 #[test]
137 fn faucet_contract_creation() {
138 let pub_key = rpo_falcon512::PublicKey::new([ONE; 4]);
139 let auth_scheme: AuthScheme = AuthScheme::RpoFalcon512 { pub_key };
140
141 let init_seed: [u8; 32] = [
143 90, 110, 209, 94, 84, 105, 250, 242, 223, 203, 216, 124, 22, 159, 14, 132, 215, 85,
144 183, 204, 149, 90, 166, 68, 100, 73, 106, 168, 125, 237, 138, 16,
145 ];
146
147 let max_supply = Felt::new(123);
148 let token_symbol_string = "POL";
149 let token_symbol = TokenSymbol::try_from(token_symbol_string).unwrap();
150 let decimals = 2u8;
151 let storage_mode = AccountStorageMode::Private;
152
153 let anchor_block_header_mock = BlockHeader::mock(
154 0,
155 Some(digest!("0xaa")),
156 Some(digest!("0xbb")),
157 &[],
158 digest!("0xcc"),
159 );
160
161 let (faucet_account, _) = create_basic_fungible_faucet(
162 init_seed,
163 (&anchor_block_header_mock).try_into().unwrap(),
164 token_symbol,
165 decimals,
166 max_supply,
167 storage_mode,
168 auth_scheme,
169 )
170 .unwrap();
171
172 assert_eq!(faucet_account.storage().get_item(0).unwrap(), Word::default().into());
174
175 assert_eq!(faucet_account.storage().get_item(1).unwrap(), Word::from(pub_key).into());
178
179 assert_eq!(
182 faucet_account.storage().get_item(2).unwrap(),
183 [Felt::new(123), Felt::new(2), token_symbol.into(), Felt::ZERO].into()
184 );
185
186 assert!(faucet_account.is_faucet());
187 }
188}