1use alloc::collections::{BTreeMap, BTreeSet};
2use alloc::vec::Vec;
3
4use anyhow::Context;
5
6const DEFAULT_FAUCET_DECIMALS: u8 = 10;
11
12const DEFAULT_VALIDATOR_COUNT: usize = 3;
16
17use itertools::Itertools;
21use miden_processor::crypto::random::RandomCoin;
22use miden_protocol::account::{
23 Account,
24 AccountBuilder,
25 AccountComponent,
26 AccountId,
27 AccountPatch,
28 AccountType,
29 AccountUpdateDetails,
30 AssetCallbackFlag,
31 StorageSlot,
32};
33use miden_protocol::asset::{Asset, AssetAmount, FungibleAsset, TokenSymbol};
34use miden_protocol::block::account_tree::AccountTree;
35use miden_protocol::block::nullifier_tree::NullifierTree;
36use miden_protocol::block::{
37 BlockAccountUpdate,
38 BlockBody,
39 BlockHeader,
40 BlockNoteTree,
41 BlockNumber,
42 BlockProof,
43 BlockSignatures,
44 Blockchain,
45 FeeParameters,
46 OutputNoteBatch,
47 ProvenBlock,
48 ValidatorKeys,
49};
50use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SigningKey;
51use miden_protocol::crypto::merkle::smt::Smt;
52use miden_protocol::errors::NoteError;
53use miden_protocol::note::{Note, NoteDetails, NoteScriptRoot, NoteType};
54use miden_protocol::testing::account_id::ACCOUNT_ID_FEE_FAUCET;
55use miden_protocol::testing::random_secret_key::random_secret_key;
56use miden_protocol::transaction::{OrderedTransactionHeaders, RawOutputNote, TransactionKernel};
57use miden_protocol::{MAX_OUTPUT_NOTES_PER_BATCH, Word};
58use miden_standards::account::access::{AccessControl, Authority, Pausable, PausableManager};
59use miden_standards::account::auth::SponsorshipPolicy;
60use miden_standards::account::faucets::{FungibleFaucet, NonFungibleFaucet, TokenName};
61use miden_standards::account::fees::{BasicConstantFeePolicy, FeePolicyManager};
62use miden_standards::account::policies::{
63 BurnPolicy,
64 MintPolicy,
65 TokenPolicyManager,
66 TransferPolicy,
67};
68use miden_standards::account::wallets::{BasicWallet, NoteCreator};
69use miden_standards::note::{
70 BurnNote,
71 MintNote,
72 NetworkAccountConfigNote,
73 P2idNote,
74 P2ideNote,
75 SwapNote,
76 TxFeeNote,
77};
78use miden_standards::testing::account_component::MockAccountComponent;
79use rand::RngExt;
80
81use crate::mock_chain::chain::AccountAuthenticator;
82use crate::utils::{create_p2any_note, create_spawn_note};
83use crate::{AccountState, Auth, MockChain};
84
85#[derive(Debug, Clone)]
129pub struct MockChainBuilder {
130 accounts: BTreeMap<AccountId, Account>,
131 account_authenticators: BTreeMap<AccountId, AccountAuthenticator>,
132 notes: Vec<RawOutputNote>,
133 rng: RandomCoin,
134 fee_faucet_id: AccountId,
136 verification_base_fee: u32,
137}
138
139impl MockChainBuilder {
140 pub fn new() -> Self {
150 let fee_faucet_id = ACCOUNT_ID_FEE_FAUCET.try_into().expect("account ID should be valid");
151
152 Self {
153 accounts: BTreeMap::new(),
154 account_authenticators: BTreeMap::new(),
155 notes: Vec::new(),
156 rng: RandomCoin::new(Default::default()),
157 fee_faucet_id,
158 verification_base_fee: 0,
159 }
160 }
161
162 pub fn with_accounts(accounts: impl IntoIterator<Item = Account>) -> anyhow::Result<Self> {
171 let mut builder = Self::new();
172
173 for account in accounts {
174 builder.add_account(account)?;
175 }
176
177 Ok(builder)
178 }
179
180 pub fn fee_faucet_id(mut self, fee_faucet_id: AccountId) -> Self {
188 self.fee_faucet_id = fee_faucet_id;
189 self
190 }
191
192 pub fn verification_base_fee(mut self, verification_base_fee: u32) -> Self {
196 self.verification_base_fee = verification_base_fee;
197 self
198 }
199
200 pub fn build(self) -> anyhow::Result<MockChain> {
202 let block_account_updates: Vec<BlockAccountUpdate> = self
204 .accounts
205 .into_values()
206 .map(|account| {
207 let account_id = account.id();
208 let account_commitment = account.to_commitment();
209 let account_patch = AccountPatch::try_from(account)
210 .expect("chain builder should only store existing accounts without seeds");
211 let update_details = AccountUpdateDetails::Public(account_patch);
212
213 BlockAccountUpdate::new(account_id, account_commitment, update_details)
214 })
215 .collect();
216
217 let account_tree = AccountTree::with_entries(
218 block_account_updates
219 .iter()
220 .map(|account| (account.account_id(), account.final_state_commitment())),
221 )
222 .context("failed to create genesis account tree")?;
223
224 let full_notes: Vec<Note> = self
226 .notes
227 .iter()
228 .filter_map(|note| match note {
229 RawOutputNote::Full(n) => Some(n.clone()),
230 _ => None,
231 })
232 .collect();
233
234 let proven_notes: Vec<_> = self
235 .notes
236 .into_iter()
237 .map(|note| note.into_output_note().expect("genesis note should be valid"))
238 .collect();
239 let note_chunks = proven_notes.into_iter().chunks(MAX_OUTPUT_NOTES_PER_BATCH);
240 let output_note_batches: Vec<OutputNoteBatch> = note_chunks
241 .into_iter()
242 .map(|batch_notes| batch_notes.into_iter().enumerate().collect::<Vec<_>>())
243 .collect();
244
245 let created_nullifiers = Vec::new();
246 let transactions = OrderedTransactionHeaders::new_unchecked(Vec::new());
247
248 let note_tree = BlockNoteTree::from_note_batches(&output_note_batches)
249 .context("failed to create block note tree")?;
250
251 let version = 0;
252 let prev_block_commitment = Word::empty();
253 let block_num = BlockNumber::from(0u32);
254 let chain_commitment = Blockchain::new().commitment();
255 let account_root = account_tree.root();
256 let nullifier_root = NullifierTree::<Smt>::default().root();
257 let note_root = note_tree.root();
258 let tx_commitment = transactions.commitment();
259 let tx_kernel_commitment = TransactionKernel.to_commitment();
260 let timestamp = MockChain::TIMESTAMP_START_SECS;
261 let fee_parameters = FeeParameters::new(self.fee_faucet_id, self.verification_base_fee);
262 let validator_secret_keys: Vec<SigningKey> =
263 (0..DEFAULT_VALIDATOR_COUNT).map(|_| random_secret_key()).collect();
264 let validator_keys =
265 ValidatorKeys::new(validator_secret_keys.iter().map(|sk| sk.public_key()).collect())
266 .expect("randomly generated genesis validator keys should be distinct");
267
268 let header = BlockHeader::new(
269 version,
270 prev_block_commitment,
271 block_num,
272 chain_commitment,
273 account_root,
274 nullifier_root,
275 note_root,
276 tx_commitment,
277 tx_kernel_commitment,
278 validator_keys.clone(),
279 fee_parameters,
280 timestamp,
281 );
282
283 let body = BlockBody::new_unchecked(
284 block_account_updates,
285 output_note_batches,
286 created_nullifiers,
287 transactions,
288 );
289
290 let signatures = BlockSignatures::new(
293 validator_keys
294 .as_keys()
295 .iter()
296 .map(|key| {
297 let signer = validator_secret_keys
298 .iter()
299 .find(|sk| &sk.public_key() == key)
300 .expect("a signer should exist for every validator key");
301 signer.sign(header.commitment())
302 })
303 .collect(),
304 )
305 .expect("signature count same as validator key count");
306 let block_proof = BlockProof::new_dummy();
307 let genesis_block = ProvenBlock::new_unchecked(header, body, signatures, block_proof);
308
309 MockChain::from_genesis_block(
310 genesis_block,
311 account_tree,
312 self.account_authenticators,
313 validator_secret_keys,
314 full_notes,
315 )
316 }
317
318 pub fn create_new_wallet(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
327 let account_builder = AccountBuilder::new(self.rng.random())
328 .account_type(AccountType::Public)
329 .with_component(BasicWallet);
330
331 self.add_account_from_builder(auth_method, account_builder, AccountState::New)
332 }
333
334 pub fn add_existing_wallet(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
337 self.add_existing_wallet_with_assets(auth_method, [])
338 }
339
340 pub fn add_existing_wallet_with_assets(
343 &mut self,
344 auth_method: Auth,
345 assets: impl IntoIterator<Item = Asset>,
346 ) -> anyhow::Result<Account> {
347 let account_builder = Account::builder(self.rng.random())
348 .account_type(AccountType::Public)
349 .with_component(BasicWallet)
350 .with_assets(assets);
351
352 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
353 }
354
355 pub fn add_existing_note_creator(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
361 let account_builder = Account::builder(self.rng.random())
362 .account_type(AccountType::Public)
363 .with_component(NoteCreator);
364
365 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
366 }
367
368 fn add_existing_network_fungible_faucet(
371 &mut self,
372 allowed_script_roots: BTreeSet<NoteScriptRoot>,
373 faucet: FungibleFaucet,
374 account_type: AccountType,
375 access_control: AccessControl,
376 token_policy_manager: TokenPolicyManager,
377 assets: Vec<Asset>,
378 ) -> anyhow::Result<Account> {
379 let mut basic_constant_fee_policy = BasicConstantFeePolicy::new();
384 for note_script in &allowed_script_roots {
385 basic_constant_fee_policy =
386 basic_constant_fee_policy.with_fee(*note_script, AssetAmount::ZERO);
387 }
388 basic_constant_fee_policy = basic_constant_fee_policy
391 .with_fee(NetworkAccountConfigNote::script_root(), AssetAmount::ZERO);
392
393 let fee_policy_manager = FeePolicyManager::builder()
394 .active_fee_policy(basic_constant_fee_policy.into())
395 .fee_faucet_id(self.fee_faucet_id)
396 .build();
397
398 let account_builder = AccountBuilder::new(self.rng.random())
399 .account_type(account_type)
400 .with_component(faucet)
401 .with_components(access_control)
402 .with_asset_callbacks(AssetCallbackFlag::from(
403 token_policy_manager.has_transfer_policy(),
404 ))
405 .with_components(token_policy_manager)
406 .with_component(Pausable::unpaused())
407 .with_component(PausableManager)
408 .with_assets(assets);
409
410 let auth = Auth::NetworkAccount {
411 allowed_script_roots,
412 allowed_tx_script_roots: BTreeSet::new(),
413 fee_policy_manager,
414 sponsorship_policy: SponsorshipPolicy::default(),
415 };
416
417 self.add_account_from_builder(auth, account_builder, AccountState::Exists)
418 }
419
420 pub fn add_existing_basic_faucet(
430 &mut self,
431 auth_method: Auth,
432 token_symbol: &str,
433 max_supply: u64,
434 token_supply: Option<u64>,
435 ) -> anyhow::Result<Account> {
436 let token_supply = token_supply.unwrap_or(0);
437 let name = TokenName::new(token_symbol)?;
438 let symbol = TokenSymbol::new(token_symbol)
439 .with_context(|| format!("invalid token symbol: {token_symbol}"))?;
440 let max_supply = AssetAmount::new(max_supply).context("invalid max_supply")?;
441 let token_supply = AssetAmount::new(token_supply).context("invalid token_supply")?;
442 let faucet = FungibleFaucet::builder()
443 .name(name)
444 .symbol(symbol)
445 .decimals(DEFAULT_FAUCET_DECIMALS)
446 .max_supply(max_supply)
447 .token_supply(token_supply)
448 .build()
449 .context("failed to build FungibleFaucet")?;
450
451 let token_policy_manager = TokenPolicyManager::builder()
452 .active_mint_policy(MintPolicy::allow_all())
453 .active_burn_policy(BurnPolicy::allow_all())
454 .build();
455
456 let account_builder = AccountBuilder::new(self.rng.random())
457 .account_type(AccountType::Public)
458 .with_component(faucet)
459 .with_component(Authority::AuthControlled)
460 .with_asset_callbacks(AssetCallbackFlag::Disabled)
461 .with_components(token_policy_manager)
462 .with_component(Pausable::unpaused())
463 .with_component(PausableManager);
464
465 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
466 }
467
468 pub fn add_existing_non_fungible_faucet(
474 &mut self,
475 auth_method: Auth,
476 token_symbol: &str,
477 ) -> anyhow::Result<Account> {
478 let name = TokenName::new(token_symbol)?;
479 let symbol = TokenSymbol::new(token_symbol)
480 .with_context(|| format!("invalid token symbol: {token_symbol}"))?;
481 let faucet = NonFungibleFaucet::builder().name(name).symbol(symbol).build();
482
483 let token_policy_manager = TokenPolicyManager::builder()
484 .active_mint_policy(MintPolicy::allow_all())
485 .active_burn_policy(BurnPolicy::allow_all())
486 .active_send_policy(TransferPolicy::allow_all())
487 .active_receive_policy(TransferPolicy::allow_all())
488 .build();
489
490 let account_builder = AccountBuilder::new(self.rng.random())
491 .account_type(AccountType::Public)
492 .with_component(faucet)
493 .with_component(Authority::AuthControlled)
494 .with_asset_callbacks(AssetCallbackFlag::Enabled)
495 .with_components(token_policy_manager)
496 .with_component(Pausable::unpaused());
497
498 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
499 }
500
501 pub fn add_existing_network_faucet(
512 &mut self,
513 token_symbol: &str,
514 max_supply: u64,
515 owner_account_id: AccountId,
516 token_supply: Option<u64>,
517 mint_policy: MintPolicy,
518 allowed_script_roots: impl IntoIterator<Item = NoteScriptRoot>,
519 ) -> anyhow::Result<Account> {
520 self.add_existing_network_faucet_with_assets(
521 token_symbol,
522 max_supply,
523 owner_account_id,
524 token_supply,
525 mint_policy,
526 allowed_script_roots,
527 [],
528 )
529 }
530
531 #[allow(clippy::too_many_arguments)]
534 pub fn add_existing_network_faucet_with_assets(
535 &mut self,
536 token_symbol: &str,
537 max_supply: u64,
538 owner_account_id: AccountId,
539 token_supply: Option<u64>,
540 mint_policy: MintPolicy,
541 allowed_script_roots: impl IntoIterator<Item = NoteScriptRoot>,
542 assets: impl IntoIterator<Item = Asset>,
543 ) -> anyhow::Result<Account> {
544 let token_supply = token_supply.unwrap_or(0);
545 let name = TokenName::new(token_symbol)?;
546 let symbol = TokenSymbol::new(token_symbol)
547 .with_context(|| format!("invalid token symbol: {token_symbol}"))?;
548 let max_supply = AssetAmount::new(max_supply).context("invalid max_supply")?;
549 let token_supply = AssetAmount::new(token_supply).context("invalid token_supply")?;
550 let faucet = FungibleFaucet::builder()
551 .name(name)
552 .symbol(symbol)
553 .decimals(DEFAULT_FAUCET_DECIMALS)
554 .max_supply(max_supply)
555 .token_supply(token_supply)
556 .build()
557 .context("failed to build FungibleFaucet")?;
558
559 let token_policy_manager = TokenPolicyManager::builder()
560 .active_mint_policy(mint_policy)
561 .active_burn_policy(BurnPolicy::allow_all())
562 .active_send_policy(TransferPolicy::allow_all())
563 .active_receive_policy(TransferPolicy::allow_all())
564 .build();
565
566 let allowed_script_roots: BTreeSet<NoteScriptRoot> = allowed_script_roots
567 .into_iter()
568 .chain([MintNote::script_root(), BurnNote::script_root()])
569 .collect();
570
571 self.add_existing_network_fungible_faucet(
572 allowed_script_roots,
573 faucet,
574 AccountType::Public,
575 AccessControl::Ownable2Step { owner: owner_account_id },
576 token_policy_manager,
577 assets.into_iter().collect(),
578 )
579 }
580
581 pub fn add_existing_network_faucet_with_metadata(
588 &mut self,
589 owner_account_id: AccountId,
590 faucet: FungibleFaucet,
591 allowed_script_roots: impl IntoIterator<Item = NoteScriptRoot>,
592 ) -> anyhow::Result<Account> {
593 let token_policy_manager = TokenPolicyManager::builder()
594 .active_mint_policy(MintPolicy::owner_only())
595 .active_burn_policy(BurnPolicy::allow_all())
596 .active_send_policy(TransferPolicy::allow_all())
597 .active_receive_policy(TransferPolicy::allow_all())
598 .build();
599
600 let allowed_script_roots: BTreeSet<NoteScriptRoot> = allowed_script_roots
601 .into_iter()
602 .chain([MintNote::script_root(), BurnNote::script_root()])
603 .collect();
604
605 self.add_existing_network_fungible_faucet(
606 allowed_script_roots,
607 faucet,
608 AccountType::Public,
609 AccessControl::Ownable2Step { owner: owner_account_id },
610 token_policy_manager,
611 Vec::new(),
612 )
613 }
614
615 pub fn create_new_faucet(
619 &mut self,
620 auth_method: Auth,
621 token_symbol: &str,
622 max_supply: u64,
623 ) -> anyhow::Result<Account> {
624 let name = TokenName::new(token_symbol)?;
625 let symbol = TokenSymbol::new(token_symbol)
626 .with_context(|| format!("invalid token symbol: {token_symbol}"))?;
627 let max_supply = AssetAmount::new(max_supply).context("invalid max_supply")?;
628 let faucet = FungibleFaucet::builder()
629 .name(name)
630 .symbol(symbol)
631 .decimals(DEFAULT_FAUCET_DECIMALS)
632 .max_supply(max_supply)
633 .build()
634 .context("failed to build FungibleFaucet")?;
635
636 let token_policy_manager = TokenPolicyManager::builder()
637 .active_mint_policy(MintPolicy::allow_all())
638 .active_burn_policy(BurnPolicy::allow_all())
639 .build();
640
641 let account_builder = AccountBuilder::new(self.rng.random())
642 .account_type(AccountType::Public)
643 .with_component(faucet)
644 .with_component(Authority::AuthControlled)
645 .with_asset_callbacks(AssetCallbackFlag::Disabled)
646 .with_components(token_policy_manager)
647 .with_component(Pausable::unpaused())
648 .with_component(PausableManager);
649
650 self.add_account_from_builder(auth_method, account_builder, AccountState::New)
651 }
652
653 pub fn create_new_mock_account(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
656 let account_builder = Account::builder(self.rng.random())
657 .account_type(AccountType::Public)
658 .with_component(MockAccountComponent::with_empty_slots());
659
660 self.add_account_from_builder(auth_method, account_builder, AccountState::New)
661 }
662
663 pub fn add_existing_mock_account(&mut self, auth_method: Auth) -> anyhow::Result<Account> {
666 self.add_existing_mock_account_with_storage_and_assets(auth_method, [], [])
667 }
668
669 pub fn add_existing_mock_account_with_storage(
672 &mut self,
673 auth_method: Auth,
674 slots: impl IntoIterator<Item = StorageSlot>,
675 ) -> anyhow::Result<Account> {
676 self.add_existing_mock_account_with_storage_and_assets(auth_method, slots, [])
677 }
678
679 pub fn add_existing_mock_account_with_assets(
682 &mut self,
683 auth_method: Auth,
684 assets: impl IntoIterator<Item = Asset>,
685 ) -> anyhow::Result<Account> {
686 self.add_existing_mock_account_with_storage_and_assets(auth_method, [], assets)
687 }
688
689 pub fn add_existing_mock_account_with_storage_and_assets(
692 &mut self,
693 auth_method: Auth,
694 slots: impl IntoIterator<Item = StorageSlot>,
695 assets: impl IntoIterator<Item = Asset>,
696 ) -> anyhow::Result<Account> {
697 let account_builder = Account::builder(self.rng.random())
698 .account_type(AccountType::Public)
699 .with_component(MockAccountComponent::with_slots(slots.into_iter().collect()))
700 .with_assets(assets);
701
702 self.add_account_from_builder(auth_method, account_builder, AccountState::Exists)
703 }
704
705 pub fn add_account_from_builder(
716 &mut self,
717 auth_method: Auth,
718 mut account_builder: AccountBuilder,
719 account_state: AccountState,
720 ) -> anyhow::Result<Account> {
721 let (auth_components, authenticator) = auth_method.build_components();
722 account_builder = account_builder.with_components(auth_components);
723
724 let account = if let AccountState::New = account_state {
725 account_builder.build().context("failed to build account from builder")?
726 } else {
727 account_builder
728 .build_existing()
729 .context("failed to build account from builder")?
730 };
731
732 self.account_authenticators
733 .insert(account.id(), AccountAuthenticator::new(authenticator));
734
735 if let AccountState::Exists = account_state {
736 self.accounts.insert(account.id(), account.clone());
737 }
738
739 Ok(account)
740 }
741 pub fn add_existing_account_from_components(
742 &mut self,
743 auth: Auth,
744 components: impl IntoIterator<Item = AccountComponent>,
745 ) -> anyhow::Result<Account> {
746 let mut account_builder =
747 Account::builder(rand::rng().random()).account_type(AccountType::Public);
748
749 for component in components {
750 account_builder = account_builder.with_component(component);
751 }
752
753 self.add_account_from_builder(auth, account_builder, AccountState::Exists)
754 }
755
756 pub fn add_account(&mut self, account: Account) -> anyhow::Result<()> {
765 self.accounts.insert(account.id(), account);
766
767 Ok(())
770 }
771
772 pub fn add_output_note(&mut self, note: impl Into<RawOutputNote>) {
777 self.notes.push(note.into());
778 }
779
780 pub fn add_p2any_note(
785 &mut self,
786 sender_account_id: AccountId,
787 note_type: NoteType,
788 assets: impl IntoIterator<Item = Asset>,
789 ) -> anyhow::Result<Note> {
790 let note = create_p2any_note(sender_account_id, note_type, assets, &mut self.rng);
791 self.add_output_note(RawOutputNote::Full(note.clone()));
792
793 Ok(note)
794 }
795
796 pub fn add_p2id_note(
802 &mut self,
803 sender_account_id: AccountId,
804 target_account_id: AccountId,
805 asset: &[Asset],
806 note_type: NoteType,
807 ) -> Result<Note, NoteError> {
808 let note: Note = P2idNote::builder()
809 .sender(sender_account_id)
810 .target(target_account_id)
811 .assets(asset.iter().copied())
812 .note_type(note_type)
813 .generate_serial_number(&mut self.rng)
814 .build()?
815 .into();
816 self.add_output_note(RawOutputNote::Full(note.clone()));
817
818 Ok(note)
819 }
820
821 pub fn add_tx_fee_note(
826 &mut self,
827 sender_account_id: AccountId,
828 assets: &[Asset],
829 ) -> Result<Note, NoteError> {
830 let note: Note = TxFeeNote::builder()
831 .sender(sender_account_id)
832 .assets(assets.iter().copied())
833 .generate_serial_number(&mut self.rng)
834 .build()?
835 .into();
836 self.add_output_note(RawOutputNote::Full(note.clone()));
837
838 Ok(note)
839 }
840
841 pub fn add_p2ide_note(
849 &mut self,
850 sender_account_id: AccountId,
851 target_account_id: AccountId,
852 reclaimer: Option<AccountId>,
853 asset: &[Asset],
854 note_type: NoteType,
855 reclaim_height: Option<BlockNumber>,
856 timelock_height: Option<BlockNumber>,
857 ) -> Result<Note, NoteError> {
858 let note: Note = P2ideNote::builder()
859 .sender(sender_account_id)
860 .target(target_account_id)
861 .maybe_reclaimer(reclaimer)
862 .assets(asset.iter().copied())
863 .note_type(note_type)
864 .maybe_reclaim_height(reclaim_height)
865 .maybe_timelock_height(timelock_height)
866 .generate_serial_number(&mut self.rng)
867 .build()?
868 .into();
869
870 self.add_output_note(RawOutputNote::Full(note.clone()));
871
872 Ok(note)
873 }
874
875 pub fn add_swap_note(
877 &mut self,
878 sender: AccountId,
879 offered_asset: Asset,
880 requested_asset: Asset,
881 payback_note_type: NoteType,
882 ) -> anyhow::Result<(Note, NoteDetails)> {
883 let swap_note = SwapNote::builder()
884 .sender(sender)
885 .offered_asset(offered_asset)
886 .requested_asset(requested_asset)
887 .note_type(NoteType::Public)
888 .payback_note_type(payback_note_type)
889 .generate_serial_number(&mut self.rng)
890 .build()?;
891
892 let payback_note = swap_note.payback_note_details();
893 let note = Note::from(swap_note);
894
895 self.add_output_note(RawOutputNote::Full(note.clone()));
896
897 Ok((note, payback_note))
898 }
899
900 pub fn add_spawn_note<'note, I>(
911 &mut self,
912 output_notes: impl IntoIterator<Item = &'note Note, IntoIter = I>,
913 ) -> anyhow::Result<Note>
914 where
915 I: ExactSizeIterator<Item = &'note Note>,
916 {
917 let note = create_spawn_note(output_notes)?;
918 self.add_output_note(RawOutputNote::Full(note.clone()));
919
920 Ok(note)
921 }
922
923 pub fn add_p2id_note_with_fee(
930 &mut self,
931 target_account_id: AccountId,
932 amount: u64,
933 ) -> anyhow::Result<Note> {
934 let fee_asset = self.fee_asset(amount)?;
935 let note = self.add_p2id_note(
936 self.fee_faucet_id,
937 target_account_id,
938 &[Asset::from(fee_asset)],
939 NoteType::Public,
940 )?;
941
942 Ok(note)
943 }
944
945 pub fn rng_mut(&mut self) -> &mut RandomCoin {
952 &mut self.rng
953 }
954
955 fn fee_asset(&self, amount: u64) -> anyhow::Result<FungibleAsset> {
957 FungibleAsset::new(self.fee_faucet_id, amount).context("failed to create fee asset")
958 }
959}
960
961impl Default for MockChainBuilder {
962 fn default() -> Self {
963 Self::new()
964 }
965}