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: β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β initialized (Value) β Word: [1, 0, 0, 0] when readyβ β
β β balances (StorageMap) β 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, β β
β β note_type] β β
β β assets: [withdraw amount] β β
β β target: Bank β β
β ββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β 2. BANK CONSUMES REQUEST (Transaction begins) β
β ββββββββββββββββββββββββββββββββ β
β β Note script executes: β β
β β sender = get_sender() β β
β β storage = get_storage() β β
β β 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 = storage[10..13] β MAST digest β β
β β recipient = note::build_recipient( β β
β β serial_num, script_root, β β
β β [user.suffix, user.prefix] β β
β β ) β β
β β note_idx = output_note::create(tag, note_type, β β
β β recipient) β β
β β 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β
The complete flow is exercised by the three integration tests built up over the previous chapters, which together cover the same init β deposit β withdraw story shown in the diagram above:
examples/miden-bank/integration/tests/deposit_test.rsβ introduced in Part 4. Covers the deposit happy path (deposit_test) plus two failure paths:deposit_exceeds_max_should_failanddeposit_without_init_should_fail.examples/miden-bank/integration/tests/init_test.rsβ introduced in Part 6. Exercises the init transaction script (init_test) and verifies theinitializedflag flips from0to1.examples/miden-bank/integration/tests/withdraw_test.rsβ introduced in Part 7. Runs init + deposit + withdraw end-to-end (withdraw_test) and asserts the P2ID output note is created with the correct payload.
Running all three together from the workspace root is the closest thing to a single end-to-end run:
cargo test --package integration --release -- --nocapture --test-threads=1
Expected output
Compiling integration v0.1.0 (/path/to/miden-bank/integration)
Finished `release` profile [optimized] target(s)
Running tests/deposit_test.rs
running 3 tests
test deposit_test ... ok
test deposit_exceeds_max_should_fail ... ok
test deposit_without_init_should_fail ... ok
test result: ok. 3 passed; 0 failed; 0 ignored
Running tests/init_test.rs
running 1 test
test init_test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored
Running tests/withdraw_test.rs
running 1 test
test withdraw_test ... ok
test result: ok. 1 passed; 0 failed; 0 ignored
Summary: All Componentsβ
Here's the complete picture of what you've built:
| Component | Type | Purpose |
|---|---|---|
bank-account | Account Component | Manages balances and vault |
deposit-note | Note Script | Processes incoming deposits |
withdraw-request-note | Note Script | Requests withdrawals |
init-tx-script | Transaction Script | Initializes the bank |
| Storage Slot | Type | Content |
|---|---|---|
initialized | StorageValue<Word> | Initialization flag |
balances | StorageMap<Word, Felt> | Depositor balances |
| API | Purpose |
|---|---|
active_note::get_sender() | Identify note creator |
active_note::get_assets() | Get attached assets |
active_note::get_storage() | 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_canonical_u64() >= withdraw_amount.as_canonical_u64(),
"Insufficient balance"
);
let new_balance = current_balance - withdraw_amount;
Never use <, > on Felt values directly. Always convert to u64 first:
// β BROKEN: Produces incorrect results
if current_balance < withdraw_amount { ... }
// β
CORRECT: Use as_canonical_u64()
if current_balance.as_canonical_u64() < withdraw_amount.as_canonical_u64() { ... }
Congratulations! πβ
You've completed the Miden Bank tutorial! You now understand:
- β
Account components with storage (
StorageValue<Word>andStorageMap<Word, Felt>) - β 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β
- Testing with MockChain - Deep dive into testing patterns
- Debugging Guide - Troubleshoot common issues
- Common Pitfalls - Avoid known gotchas
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! π