Skip to main content
Version: 0.13 (unstable)

Part 8: Complete Flows

In this final section, we'll bring everything together and walk through the complete deposit and withdrawal flows, verifying that all the components work as a unified banking system.

What You'll Build in This Part​

By the end of this section, you will have:

  • Understood the complete deposit flow from note creation to balance update
  • Understood the complete withdraw flow including P2ID note creation
  • Verified the entire system works with an end-to-end MockChain test
  • Completed the Miden Bank tutorial! πŸŽ‰

Building on Parts 0-7​

You've built all the pieces. Now let's see them work together:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ COMPLETE BANK SYSTEM β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ Components Built: β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ bank-account β”‚ Storage + deposit() + withdraw() β”‚ β”‚
β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚
β”‚ β”‚ deposit-note β”‚ Note script β†’ bank_account::deposit() β”‚ β”‚
β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚
β”‚ β”‚ withdraw-note β”‚ Note script β†’ bank_account::withdraw() β”‚ β”‚
β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚
β”‚ β”‚ init-tx-script β”‚ Transaction script β†’ initialize() β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β”‚ Storage Layout: β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Slot 0: initialized β”‚ Word: [1, 0, 0, 0] when ready β”‚ β”‚
β”‚ β”‚ Slot 1: balances β”‚ Map: user_key β†’ [balance, 0, 0, 0]β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The Complete Deposit Flow​

Let's trace through exactly what happens when a user deposits tokens:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ DEPOSIT FLOW β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ 1. USER CREATES DEPOSIT NOTE β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Deposit Note β”‚ β”‚
β”‚ β”‚ sender: User β”‚ β”‚
β”‚ β”‚ assets: [1000 tok] β”‚ β”‚
β”‚ β”‚ script: deposit-noteβ”‚ β”‚
β”‚ β”‚ target: Bank β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ 2. BANK CONSUMES NOTE (Transaction begins) β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Bank Account β”‚ β”‚
β”‚ β”‚ vault += 1000 tokensβ”‚ ◀── Protocol adds assets to vault β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ 3. NOTE SCRIPT EXECUTES β”‚
β”‚ depositor = active_note::get_sender() β†’ User's AccountId β”‚
β”‚ assets = active_note::get_assets() β†’ [1000 tokens] β”‚
β”‚ for asset in assets: β”‚
β”‚ bank_account::deposit(depositor, asset) ◀── Cross-componentβ”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ 4. DEPOSIT METHOD RUNS (in bank-account context) β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ require_initialized() βœ“ Passes β”‚ β”‚
β”‚ β”‚ amount <= MAX_DEPOSIT βœ“ 1000 <= 100k β”‚ β”‚
β”‚ β”‚ native_account::add_asset() ← Confirm β”‚ β”‚
β”‚ β”‚ balances[User] += 1000 ← Update β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ 5. TRANSACTION COMPLETES β”‚
β”‚ Bank storage: balances[User] = 1000 β”‚
β”‚ Bank vault: +1000 tokens β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The Complete Withdraw Flow​

Now let's trace the withdrawal process:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ WITHDRAW FLOW β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β”‚
β”‚ 1. USER CREATES WITHDRAW REQUEST NOTE β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Withdraw Request Note β”‚ β”‚
β”‚ β”‚ sender: User β”‚ β”‚
β”‚ β”‚ inputs: [serial, tag, β”‚ β”‚
β”‚ β”‚ aux, note_type] β”‚ β”‚
β”‚ β”‚ assets: [withdraw amount] β”‚ β”‚
β”‚ β”‚ target: Bank β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ 2. BANK CONSUMES REQUEST (Transaction begins) β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Note script executes: β”‚ β”‚
β”‚ β”‚ sender = get_sender() β”‚ β”‚
β”‚ β”‚ inputs = get_inputs() β”‚ β”‚
β”‚ β”‚ asset = Asset from inputs β”‚ β”‚
β”‚ β”‚ bank_account::withdraw(...) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ 3. WITHDRAW METHOD RUNS β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ require_initialized() βœ“ Passes β”‚ β”‚
β”‚ β”‚ current_balance = get_balance(User) β†’ 1000 β”‚ β”‚
β”‚ β”‚ VALIDATE: 1000 >= 400 βœ“ Passes β”‚ β—€ CRITICAL
β”‚ β”‚ balances[User] = 1000 - 400 β†’ 600 β”‚ β”‚
β”‚ β”‚ create_p2id_note(...) β†’ Output note β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ 4. P2ID NOTE CREATED (inside create_p2id_note) β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ script_root = p2id_note_root() β†’ MAST digest β”‚ β”‚
β”‚ β”‚ recipient = Recipient::compute( β”‚ β”‚
β”‚ β”‚ serial_num, script_root, β”‚ β”‚
β”‚ β”‚ [user.suffix, user.prefix, 0, 0, 0, 0, 0, 0] β”‚ β”‚
β”‚ β”‚ ) β”‚ β”‚
β”‚ β”‚ note_idx = output_note::create(tag, aux, ...) β”‚ β”‚
β”‚ β”‚ native_account::remove_asset(400 tokens) β”‚ β”‚
β”‚ β”‚ output_note::add_asset(400 tokens, note_idx) β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ 5. TRANSACTION COMPLETES β”‚
β”‚ Bank storage: balances[User] = 600 β”‚
β”‚ Bank vault: -400 tokens β”‚
β”‚ Output: P2ID note with 400 tokens β†’ User β”‚
β”‚ β”‚ β”‚
β”‚ β–Ό β”‚
β”‚ 6. USER CONSUMES P2ID NOTE (separate transaction) β”‚
β”‚ User's wallet receives 400 tokens β”‚
β”‚ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Try It: Complete End-to-End Test​

Let's create a comprehensive test that exercises the entire bank system:

integration/tests/part8_complete_flow_test.rs
use integration::helpers::{
build_project_in_dir, create_testing_account_from_package, create_testing_note_from_package,
AccountCreationConfig, NoteCreationConfig,
};
use miden_client::{
account::StorageMap,
note::{Note, NoteAssets, NoteExecutionHint, NoteMetadata, NoteTag, NoteType},
transaction::OutputNote,
Felt, Word,
};
use miden_lib::note::utils::build_p2id_recipient;
use miden_objects::{
account::AccountId,
asset::{Asset, FungibleAsset},
transaction::TransactionScript,
};
use miden_testing::{Auth, MockChain};
use std::{path::Path, sync::Arc};

/// Compute a P2ID note tag for a local account.
fn compute_p2id_tag_for_local_account(account_id: AccountId) -> NoteTag {
const LOCAL_ANY_PREFIX: u32 = 0xC000_0000;
const TAG_BITS: u8 = 14;

let prefix_u64 = account_id.prefix().as_u64();
let shifted = (prefix_u64 >> 34) as u32;
let mask = u32::MAX << (30 - TAG_BITS);
let account_bits = shifted & mask;
let tag_value = LOCAL_ANY_PREFIX | account_bits;

NoteTag::LocalAny(tag_value)
}

/// Complete end-to-end test of the Miden Bank
///
/// This test exercises:
/// 1. Bank initialization via transaction script
/// 2. Deposit via deposit-note
/// 3. Withdrawal via withdraw-request-note
/// 4. Balance verification at each step
#[tokio::test]
async fn test_complete_bank_flow() -> anyhow::Result<()> {
println!("╔══════════════════════════════════════════════════════════════╗");
println!("β•‘ MIDEN BANK - COMPLETE FLOW TEST β•‘");
println!("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•");

// ═══════════════════════════════════════════════════════════════════
// SETUP
// ═══════════════════════════════════════════════════════════════════
println!("\nπŸ“¦ Setting up test environment...");

let mut builder = MockChain::builder();

let deposit_amount: u64 = 1000;
let withdraw_amount: u64 = 400;

// Create a faucet to mint test assets
let faucet =
builder.add_existing_basic_faucet(Auth::BasicAuth, "TEST", deposit_amount, Some(10))?;

// Create note sender account (the depositor)
let sender = builder.add_existing_wallet_with_assets(
Auth::BasicAuth,
[FungibleAsset::new(faucet.id(), deposit_amount)?.into()],
)?;
println!(" βœ“ Faucet and sender wallet created");

// Build all packages
let bank_package = Arc::new(build_project_in_dir(
Path::new("../contracts/bank-account"),
true,
)?);
let deposit_note_package = Arc::new(build_project_in_dir(
Path::new("../contracts/deposit-note"),
true,
)?);
let init_tx_script_package = Arc::new(build_project_in_dir(
Path::new("../contracts/init-tx-script"),
true,
)?);
let withdraw_request_note_package = Arc::new(build_project_in_dir(
Path::new("../contracts/withdraw-request-note"),
true,
)?);
println!(" βœ“ All packages built");

// Create bank account with storage slots
let bank_cfg = AccountCreationConfig {
storage_slots: vec![
miden_client::account::StorageSlot::Value(Word::default()),
miden_client::account::StorageSlot::Map(StorageMap::with_entries([])?),
],
..Default::default()
};
let mut bank_account =
create_testing_account_from_package(bank_package.clone(), bank_cfg).await?;
println!(" βœ“ Bank account created: {:?}", bank_account.id());

// Create deposit note with assets
let fungible_asset = FungibleAsset::new(faucet.id(), deposit_amount)?;
let note_assets = NoteAssets::new(vec![Asset::Fungible(fungible_asset)])?;
let deposit_note = create_testing_note_from_package(
deposit_note_package.clone(),
sender.id(),
NoteCreationConfig {
assets: note_assets,
..Default::default()
},
)?;

// Craft withdraw request note with 11-Felt input layout
let p2id_tag = compute_p2id_tag_for_local_account(sender.id());
let p2id_tag_u32 = match p2id_tag {
NoteTag::LocalAny(v) => v,
_ => panic!("Expected LocalAny tag"),
};
let p2id_tag_felt = Felt::new(p2id_tag_u32 as u64);

let p2id_output_note_serial_num = Word::from([
Felt::new(0x1234567890abcdef),
Felt::new(0xfedcba0987654321),
Felt::new(0xdeadbeefcafebabe),
Felt::new(0x0123456789abcdef),
]);

let aux = Felt::new(0);
let note_type_felt = Felt::new(1); // Public

// Note inputs: 11 Felts
// [0-3]: withdraw asset (amount, 0, faucet_suffix, faucet_prefix)
// [4-7]: serial_num
// [8]: tag
// [9]: aux
// [10]: note_type
let withdraw_request_note_inputs = vec![
Felt::new(withdraw_amount),
Felt::new(0),
faucet.id().suffix(),
faucet.id().prefix().as_felt(),
p2id_output_note_serial_num[0],
p2id_output_note_serial_num[1],
p2id_output_note_serial_num[2],
p2id_output_note_serial_num[3],
p2id_tag_felt,
aux,
note_type_felt,
];

let withdraw_request_note = create_testing_note_from_package(
withdraw_request_note_package.clone(),
sender.id(),
NoteCreationConfig {
inputs: withdraw_request_note_inputs,
..Default::default()
},
)?;

// Add to builder
builder.add_account(bank_account.clone())?;
builder.add_output_note(OutputNote::Full(deposit_note.clone().into()));
builder.add_output_note(OutputNote::Full(withdraw_request_note.clone().into()));

let mut mock_chain = builder.build()?;
println!(" βœ“ MockChain built");

// ═══════════════════════════════════════════════════════════════════
// STEP 1: Initialize the bank
// ═══════════════════════════════════════════════════════════════════
println!("\n1️⃣ INITIALIZING BANK...");

let init_program = init_tx_script_package.unwrap_program();
let init_tx_script = TransactionScript::new((*init_program).clone());

let init_tx_context = mock_chain
.build_tx_context(bank_account.id(), &[], &[])?
.tx_script(init_tx_script)
.build()?;

let executed_init = init_tx_context.execute().await?;
bank_account.apply_delta(&executed_init.account_delta())?;
mock_chain.add_pending_executed_transaction(&executed_init)?;
mock_chain.prove_next_block()?;

println!(" βœ“ Bank initialized (storage[0] = [1, 0, 0, 0])");

// ═══════════════════════════════════════════════════════════════════
// STEP 2: Deposit tokens
// ═══════════════════════════════════════════════════════════════════
println!("\n2️⃣ DEPOSITING TOKENS...");
println!(" Deposit amount: {} tokens", deposit_amount);

let deposit_tx_context = mock_chain
.build_tx_context(bank_account.id(), &[deposit_note.id()], &[])?
.build()?;

let executed_deposit = deposit_tx_context.execute().await?;
bank_account.apply_delta(&executed_deposit.account_delta())?;
mock_chain.add_pending_executed_transaction(&executed_deposit)?;
mock_chain.prove_next_block()?;

// Verify balance after deposit
let depositor_key = Word::from([
sender.id().prefix().as_felt(),
sender.id().suffix(),
faucet.id().prefix().as_felt(),
faucet.id().suffix(),
]);
let balance_after_deposit = bank_account.storage().get_map_item(1, depositor_key)?;
println!(
" βœ“ Bank processed deposit, balance: {} tokens",
balance_after_deposit[3].as_int()
);

// ═══════════════════════════════════════════════════════════════════
// STEP 3: Withdraw tokens
// ═══════════════════════════════════════════════════════════════════
println!("\n3️⃣ WITHDRAWING TOKENS...");
println!(" Withdraw amount: {} tokens", withdraw_amount);

// Build expected P2ID output note
let recipient = build_p2id_recipient(sender.id(), p2id_output_note_serial_num)?;
let p2id_output_note_asset = FungibleAsset::new(faucet.id(), withdraw_amount)?;
let p2id_output_note_assets = NoteAssets::new(vec![p2id_output_note_asset.into()])?;
let p2id_output_note_metadata = NoteMetadata::new(
bank_account.id(),
NoteType::Public,
p2id_tag,
NoteExecutionHint::none(),
aux,
)?;
let p2id_output_note = Note::new(
p2id_output_note_assets,
p2id_output_note_metadata,
recipient,
);

let withdraw_tx_context = mock_chain
.build_tx_context(bank_account.id(), &[withdraw_request_note.id()], &[])?
.extend_expected_output_notes(vec![OutputNote::Full(p2id_output_note.into())])
.build()?;

let executed_withdraw = withdraw_tx_context.execute().await?;
bank_account.apply_delta(&executed_withdraw.account_delta())?;
mock_chain.add_pending_executed_transaction(&executed_withdraw)?;
mock_chain.prove_next_block()?;

println!(" βœ“ Bank processed withdraw request");
println!(" βœ“ P2ID output note created for sender");

// Verify final balance
let final_balance = bank_account.storage().get_map_item(1, depositor_key)?;
let final_balance_amount = final_balance[3].as_int();
let expected_final = deposit_amount - withdraw_amount;

println!(" βœ“ Final balance verified: {} tokens", final_balance_amount);

// ═══════════════════════════════════════════════════════════════════
// SUMMARY
// ═══════════════════════════════════════════════════════════════════
println!("\n╔══════════════════════════════════════════════════════════════╗");
println!("β•‘ TEST SUMMARY β•‘");
println!("╠══════════════════════════════════════════════════════════════╣");
println!(
"β•‘ Initial deposit: {:>6} tokens β•‘",
deposit_amount
);
println!(
"β•‘ Withdrawal: -{:>6} tokens β•‘",
withdraw_amount
);
println!(
"β•‘ Final balance: {:>6} tokens β•‘",
final_balance_amount
);
println!("β•‘ β•‘");
println!("β•‘ βœ… All operations completed successfully! β•‘");
println!("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•");

assert_eq!(
final_balance_amount, expected_final,
"Final balance should be deposit - withdraw"
);

Ok(())
}

Run the complete test from the project root:

>_ Terminal
cargo test --package integration part8_complete_flow -- --nocapture
Expected output
   Compiling integration v0.1.0 (/path/to/miden-bank/integration)
Finished `test` profile [unoptimized + debuginfo] target(s)
Running tests/part8_complete_flow_test.rs

running 1 test
╔══════════════════════════════════════════════════════════════╗
β•‘ MIDEN BANK - COMPLETE FLOW TEST β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

πŸ“¦ Setting up test environment...
βœ“ Faucet and sender wallet created
βœ“ All packages built
βœ“ Bank account created: 0x...
βœ“ MockChain built

1️⃣ INITIALIZING BANK...
βœ“ Bank initialized (storage[0] = [1, 0, 0, 0])

2️⃣ DEPOSITING TOKENS...
Deposit amount: 1000 tokens
βœ“ Bank processed deposit, balance: 1000 tokens

3️⃣ WITHDRAWING TOKENS...
Withdraw amount: 400 tokens
βœ“ Bank processed withdraw request
βœ“ P2ID output note created for sender
βœ“ Final balance verified: 600 tokens

╔══════════════════════════════════════════════════════════════╗
β•‘ TEST SUMMARY β•‘
╠══════════════════════════════════════════════════════════════╣
β•‘ Initial deposit: 1000 tokens β•‘
β•‘ Withdrawal: - 400 tokens β•‘
β•‘ Final balance: 600 tokens β•‘
β•‘ β•‘
β•‘ βœ… All operations completed successfully! β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
test test_complete_bank_flow ... ok

test result: ok. 1 passed; 0 failed; 0 ignored

Summary: All Components​

Here's the complete picture of what you've built:

ComponentTypePurpose
bank-accountAccount ComponentManages balances and vault
deposit-noteNote ScriptProcesses incoming deposits
withdraw-request-noteNote ScriptRequests withdrawals
init-tx-scriptTransaction ScriptInitializes the bank
Storage SlotTypeContent
0ValueInitialization flag
1StorageMapDepositor balances
APIPurpose
active_note::get_sender()Identify note creator
active_note::get_assets()Get attached assets
active_note::get_inputs()Get note parameters
native_account::add_asset()Receive into vault
native_account::remove_asset()Send from vault
output_note::create()Create output note
output_note::add_asset()Attach assets to note

Key Security Patterns​

Remember these critical patterns from this tutorial:

// ❌ DANGEROUS: Silent underflow!
let new_balance = current_balance - withdraw_amount;

// βœ… SAFE: Validate first
assert!(
current_balance.as_u64() >= withdraw_amount.as_u64(),
"Insufficient balance"
);
let new_balance = Felt::from_u64_unchecked(
current_balance.as_u64() - withdraw_amount.as_u64()
);

Never use <, > on Felt values directly. Always convert to u64 first:

// ❌ BROKEN: Produces incorrect results
if current_balance < withdraw_amount { ... }

// βœ… CORRECT: Use as_u64()
if current_balance.as_u64() < withdraw_amount.as_u64() { ... }

Congratulations! πŸŽ‰β€‹

You've completed the Miden Bank tutorial! You now understand:

  • βœ… Account components with storage (Value and StorageMap)
  • βœ… Constants and constraints for business rules
  • βœ… Asset management with vault operations
  • βœ… Note scripts for processing incoming notes
  • βœ… Cross-component calls via generated bindings
  • βœ… Transaction scripts for owner operations
  • βœ… Output notes for sending assets (P2ID pattern)
  • βœ… Security patterns for safe arithmetic

Continue Learning​

Build More​

Use these patterns to build:

  • Token faucets
  • DEX contracts
  • NFT marketplaces
  • Multi-signature wallets
  • And more!

Explore the complete banking application:

Happy building on Miden! πŸš€