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 โ account.deposit() โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ withdraw-request-note โ Note script โ account.withdraw() โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ init-tx-script โ Transaction script โ initialize() โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Storage Layout: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ initialized (Value) โ Word: [1, 0, 0, 0] when ready โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ balances (StorageMap) โ Balance per user and asset class โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
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 tokens] โ โ
โ โ script: deposit-note โ โ
โ โ consumed by: Bank โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ 2. BANK CONSUMES THE NOTE โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Transaction begins; note script executes โ โ
โ โ Vault is credited when deposit() calls add_asset() โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ 3. NOTE SCRIPT CALLS THE BANK COMPONENT โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ depositor = active_note::get_sender() โ User's AccountId โ โ
โ โ assets = active_note::get_initial_assets() โ [1000 tokens] โ โ
โ โ for asset in assets: โ โ
โ โ account.deposit(depositor, asset) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ 4. DEPOSIT METHOD VALIDATES AND RECEIVES THE ASSET โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ require_initialized() โ โ โ
โ โ assert asset.is_fungible() โ โ โ
โ โ assert 0 < amount <= MAX_DEPOSIT (1,000,000) โ โ โ
โ โ native_account::add_asset(asset) โ Credit vault โ โ
โ โ balances[User, asset class] += 1000 โ Update ledger โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ 5. TRANSACTION COMMITS โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Depositor's ledger balance: 1000 โ โ
โ โ Bank vault: +1000 tokens โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Complete Withdraw Flowโ
Now let's trace the withdrawal process:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ WITHDRAW FLOW โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ 1. USER CREATES WITHDRAWAL REQUEST NOTE โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Withdrawal Request Note โ โ
โ โ sender: User โ โ
โ โ assets: [] โ โ
โ โ storage (14 Felts): โ โ
โ โ asset (4), serial (4), tag, type, script root (4) โ โ
โ โ requested amount: 500 tokens โ โ
โ โ consumed by: Bank โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ 2. BANK CONSUMES THE REQUEST โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Note script reads active_note::get_storage() โ โ
โ โ Reconstructs the requested asset from storage[0..4] โ โ
โ โ Calls account.withdraw(asset, serial, tag, type) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ 3. WITHDRAW METHOD VALIDATES AND UPDATES THE LEDGER โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ require_initialized() โ โ โ
โ โ assert asset.is_fungible() โ โ โ
โ โ User = active_note::get_sender() โ โ
โ โ current_balance = 1000 โ โ
โ โ assert current_balance >= 500 โ โ โ
โ โ balances[User, asset class] = 1000 - 500 = 500 โ โ
โ โ create_p2id_note(...) โ Output note โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ 4. P2ID NOTE IS CREATED โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ script_root = storage[10..14] โ โ
โ โ recipient = note::build_recipient( โ โ
โ โ serial, script_root, [user.suffix, user.prefix]) โ โ
โ โ note_idx = output_note::create(tag, type, recipient) โ โ
โ โ native_account::remove_asset(500 tokens) โ โ
โ โ output_note::add_asset(500 tokens, note_idx) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ 5. TRANSACTION COMMITS โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Depositor's ledger balance: 500 โ โ
โ โ Bank vault: -500 tokens โ โ
โ โ Output: public or private P2ID carrying 500 tokens โ User โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ 6. USER CONSUMES P2ID IN A SEPARATE TRANSACTION โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ P2ID recipient check passes for User โ โ
โ โ User's wallet receives 500 tokens โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Try It: Complete End-to-End Testโ
The complete flow is exercised by the three integration test files 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 rejection tests for excessive deposits, deposits before initialization, and NFTs with zero padding in their value word.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) for both public and private outputs, rejects consumption by another account, and verifies the depositor receives the withdrawn tokens.
Run the complete suite from the workspace root:
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 4 tests
test deposit_test ... ok
test deposit_exceeds_max_should_fail ... ok
test deposit_without_init_should_fail ... ok
test deposit_nft_with_zero_padding_should_fail ... ok
test result: ok. 4 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
The repository also ships cargo run --bin initialize and cargo run --bin deposit (under examples/miden-bank/integration/src/bin/) for exercising the same flow against a live testnet node. The deposit bin attaches 1,000 native base units, and both binaries wait for actual transaction commitment before reporting success. Testnet charges fees: each binary prints the new account ID and waits for a public native-token P2ID. Request funding for that ID from the testnet faucet while it waits; the helper consumes the funding note before proceeding. The MockChain tests above verify initialization, deposit, and withdrawal without external funding.
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_initial_assets() | Get creation-time 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;
Direct Felt comparisons use canonical integer ordering in the current SDK. Both forms below are valid; this tutorial uses the explicit u64 form for quantity checks:
// Direct comparison of canonical Felt values.
if current_balance < withdraw_amount { ... }
// Equivalent comparison with explicit integer conversion.
if current_balance.as_canonical_u64() < withdraw_amount.as_canonical_u64() { ... }
Always perform this check before subtracting. Conversion after modular underflow cannot recover the intended balance.
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! ๐