use miden_protocol::account::Account;
use miden_protocol::account::auth::AuthScheme;
use miden_protocol::asset::{Asset, AssetVault, FungibleAsset};
use miden_protocol::note::{Note, NoteRecipient, NoteStorage, NoteType};
use miden_protocol::testing::account_id::ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2;
use miden_protocol::transaction::RawOutputNote;
use miden_protocol::{Felt, Word};
use miden_standards::code_builder::CodeBuilder;
use miden_standards::note::TxFeeNote;
use miden_testing::{Auth, MockChain};
#[tokio::test]
async fn tx_fee_note_consumable_by_any_account() -> anyhow::Result<()> {
let fungible_asset_1: Asset = FungibleAsset::mock(123);
let fungible_asset_2: Asset =
FungibleAsset::new(ACCOUNT_ID_PUBLIC_FUNGIBLE_FAUCET_2.try_into()?, 456)?.into();
let mut builder = MockChain::builder();
let sender_account = builder.create_new_wallet(Auth::BasicAuth {
auth_scheme: AuthScheme::Falcon512Poseidon2,
})?;
let consumer_account = builder.add_existing_wallet(Auth::BasicAuth {
auth_scheme: AuthScheme::Falcon512Poseidon2,
})?;
let note =
builder.add_tx_fee_note(sender_account.id(), &[fungible_asset_1, fungible_asset_2])?;
assert_eq!(note.metadata().tag(), TxFeeNote::TAG);
assert_eq!(note.metadata().note_type(), NoteType::Public);
let mock_chain = builder.build()?;
let executed_transaction = mock_chain
.build_transaction(consumer_account.id())
.authenticated_input_note(note.id())
.build()?
.execute()
.await?;
let consumer_account_after: Account = Account::new_existing(
consumer_account.id(),
AssetVault::new(&[fungible_asset_1, fungible_asset_2]).unwrap(),
consumer_account.storage().clone(),
consumer_account.code().clone(),
Felt::new_unchecked(2),
);
assert_eq!(
executed_transaction.final_account().to_commitment(),
consumer_account_after.to_commitment()
);
Ok(())
}
#[tokio::test]
async fn test_tx_fee_create_output_note_constructor() -> anyhow::Result<()> {
let mut builder = MockChain::builder();
let sender_account = builder.add_existing_wallet_with_assets(
Auth::BasicAuth {
auth_scheme: AuthScheme::Falcon512Poseidon2,
},
[FungibleAsset::mock(100)],
)?;
let mock_chain = builder.build()?;
let serial_num = Word::from([1u32, 2u32, 3u32, 4u32]);
let expected_recipient =
NoteRecipient::new(serial_num, TxFeeNote::script(), NoteStorage::default());
let tx_script_src = format!(
r#"
use miden::standards::notes::tx_fee
@transaction_script
pub proc main
# Push inputs for tx_fee::create_output_note
push.{serial_num}
# => [SERIAL_NUM]
exec.tx_fee::create_output_note
# => [note_idx]
# Add an asset to the created note
push.{ASSET_VALUE}
push.{ASSET_ID}
call.::miden::standards::wallets::basic::move_asset_to_note
# Clean up stack
dropw dropw dropw dropw
end
"#,
serial_num = serial_num,
ASSET_ID = FungibleAsset::mock(50).to_id_word(),
ASSET_VALUE = FungibleAsset::mock(50).to_value_word(),
);
let tx_script = CodeBuilder::default().compile_tx_script(&tx_script_src)?;
let expected_output_note: Note = TxFeeNote::builder()
.sender(sender_account.id())
.asset(FungibleAsset::mock(50))
.serial_number(serial_num)
.build()?
.into();
let tx_context = mock_chain
.build_transaction(sender_account.id())
.expected_output_note(RawOutputNote::Full(expected_output_note))
.tx_script(tx_script)
.build()?;
let executed_transaction = tx_context.execute().await?;
assert_eq!(executed_transaction.output_notes().num_notes(), 1);
let output_note = executed_transaction.output_notes().get_note(0);
let created_recipient = output_note.recipient().expect("output note should have recipient");
assert_eq!(
created_recipient.digest(),
expected_recipient.digest(),
"The recipient created by tx_fee::create_output_note should match the Rust TxFeeNote \
implementation"
);
assert_eq!(output_note.metadata().tag(), TxFeeNote::TAG);
assert_eq!(output_note.metadata().note_type(), NoteType::Public);
Ok(())
}