extern crate alloc;
use alloc::vec;
use miden_protocol::account::auth::{AuthScheme, PublicKeyCommitment};
use miden_protocol::account::{Account, AccountId, AccountType, AssetCallbackFlag};
use miden_protocol::asset::{AssetAmount, FungibleAsset};
use miden_protocol::note::NoteType;
use miden_protocol::{Felt, Word};
use miden_standards::account::auth::{Approver, GuardianConfig};
use miden_standards::account::faucets::{
FungibleFaucet,
TokenName,
create_guarded_user_fungible_faucet,
create_multisig_user_fungible_faucet,
};
use miden_standards::account::policies::{
AllowlistStorage,
BurnPolicy,
MintPolicy,
TokenPolicyManager,
TransferPolicy,
};
use miden_standards::errors::standards::ERR_ACCOUNT_IS_NOT_ALLOWED;
use miden_standards::testing::faucet::{user_faucet_guarded, user_faucet_multisig};
use miden_testing::{
Auth,
MockChain,
MockChainBuilder,
MockTransaction,
assert_transaction_executor_error,
};
fn sample_approvers(n: u32) -> vec::Vec<(PublicKeyCommitment, AuthScheme)> {
(0..n)
.map(|i| {
(
PublicKeyCommitment::from(Word::new([Felt::from(i + 1); 4])),
AuthScheme::Falcon512Poseidon2,
)
})
.collect()
}
fn allowlist_policy_manager(
initial_allowed: impl IntoIterator<Item = AccountId>,
) -> TokenPolicyManager {
let allow_list = AllowlistStorage::with_allowed_accounts(initial_allowed);
TokenPolicyManager::builder()
.active_mint_policy(MintPolicy::allow_all())
.active_burn_policy(BurnPolicy::allow_all())
.active_send_policy(TransferPolicy::with_basic_allowlist(allow_list.clone()))
.active_receive_policy(TransferPolicy::with_basic_allowlist(allow_list))
.build()
}
fn sample_faucet() -> anyhow::Result<FungibleFaucet> {
Ok(FungibleFaucet::builder()
.name(TokenName::new("SYM")?)
.symbol("SYM".try_into()?)
.decimals(8)
.max_supply(AssetAmount::new(1_000_000)?)
.build()?)
}
fn add_factory_faucet(builder: &mut MockChainBuilder, mut faucet: Account) -> anyhow::Result<()> {
faucet.set_nonce(Felt::ONE)?;
builder.add_account(faucet)
}
fn build_receive_asset_tx(
mut builder: MockChainBuilder,
faucet_id: AccountId,
recipient: AccountId,
) -> anyhow::Result<MockTransaction> {
let asset = FungibleAsset::new(faucet_id, 100)?;
let note = builder.add_p2id_note(faucet_id, recipient, &[asset.into()], NoteType::Public)?;
let mock_chain = builder.build()?;
let faucet_inputs = mock_chain.get_foreign_account_inputs(faucet_id)?;
mock_chain
.build_transaction(recipient)
.authenticated_input_note(note.id())
.foreign_accounts(vec![faucet_inputs])
.build()
}
#[tokio::test]
async fn multisig_factory_faucet_enforces_receive_allowlist() -> anyhow::Result<()> {
let mut builder = MockChain::builder();
let target_account = builder.add_existing_wallet(Auth::IncrNonce)?;
let faucet = create_multisig_user_fungible_faucet(
[43u8; 32],
sample_faucet()?,
user_faucet_multisig(sample_approvers(3), 2)?,
allowlist_policy_manager([]),
AccountType::Public,
)?;
assert_eq!(faucet.id().asset_callback_flag(), AssetCallbackFlag::Enabled);
let faucet_id = faucet.id();
add_factory_faucet(&mut builder, faucet)?;
let result = build_receive_asset_tx(builder, faucet_id, target_account.id())?.execute().await;
assert_transaction_executor_error!(result, ERR_ACCOUNT_IS_NOT_ALLOWED);
Ok(())
}
#[tokio::test]
async fn multisig_factory_faucet_allows_allowlisted_recipient() -> anyhow::Result<()> {
let mut builder = MockChain::builder();
let target_account = builder.add_existing_wallet(Auth::IncrNonce)?;
let faucet = create_multisig_user_fungible_faucet(
[43u8; 32],
sample_faucet()?,
user_faucet_multisig(sample_approvers(3), 2)?,
allowlist_policy_manager([target_account.id()]),
AccountType::Public,
)?;
let faucet_id = faucet.id();
add_factory_faucet(&mut builder, faucet)?;
build_receive_asset_tx(builder, faucet_id, target_account.id())?
.execute()
.await?;
Ok(())
}
#[tokio::test]
async fn guarded_factory_faucet_enforces_receive_allowlist() -> anyhow::Result<()> {
let mut builder = MockChain::builder();
let target_account = builder.add_existing_wallet(Auth::IncrNonce)?;
let guardian = GuardianConfig::new(Approver::new(
PublicKeyCommitment::from(Word::new([Felt::from(99_u32); 4])),
AuthScheme::Falcon512Poseidon2,
));
let faucet = create_guarded_user_fungible_faucet(
[44u8; 32],
sample_faucet()?,
user_faucet_guarded(sample_approvers(3), 2, guardian)?,
allowlist_policy_manager([]),
AccountType::Public,
)?;
assert_eq!(faucet.id().asset_callback_flag(), AssetCallbackFlag::Enabled);
let faucet_id = faucet.id();
add_factory_faucet(&mut builder, faucet)?;
let result = build_receive_asset_tx(builder, faucet_id, target_account.id())?.execute().await;
assert_transaction_executor_error!(result, ERR_ACCOUNT_IS_NOT_ALLOWED);
Ok(())
}