diff --git a/.github/workflows/cut-versions.yml b/.github/workflows/cut-versions.yml
index edc66850..4e739b13 100644
--- a/.github/workflows/cut-versions.yml
+++ b/.github/workflows/cut-versions.yml
@@ -152,7 +152,8 @@ jobs:
# Clean directories that will be re-synced (v0.4 nested paths)
rm -rf docs/core-concepts/miden-base docs/core-concepts/miden-vm docs/core-concepts/miden-node docs/core-concepts/compiler
rm -rf docs/builder/tools/client
- # Note: docs/builder/tutorials is NOT fully cleaned to preserve local tutorials (e.g. miden-bank)
+ rm -rf docs/builder/tutorials/miden-bank docs/builder/tutorials/index.md
+ # Note: docs/builder/tutorials/rust-compiler/ is NOT cleaned to preserve local guides (testing, debugging, pitfalls)
# Core Concepts docs → docs/core-concepts/*
if [ -d "vendor/miden-base/docs/src" ]; then
@@ -220,7 +221,8 @@ jobs:
rm -rf docs/core-concepts/miden-node
rm -rf docs/core-concepts/compiler
rm -rf docs/builder/tools/client
- # Note: tutorials live in docs/builder/tutorials/ (authored content, not cleaned)
+ rm -rf docs/builder/tutorials/miden-bank docs/builder/tutorials/index.md
+ # Note: docs/builder/tutorials/rust-compiler/ is authored content (testing, debugging, pitfalls), not cleaned
- name: Commit snapshots
run: |
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index afd3ac04..064cf290 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -148,7 +148,8 @@ jobs:
# Clean directories that will be re-synced (v0.4 nested paths)
rm -rf docs/core-concepts/miden-base docs/core-concepts/miden-vm docs/core-concepts/miden-node docs/core-concepts/compiler
rm -rf docs/builder/tools/client
- # Note: docs/builder/tutorials is NOT fully cleaned to preserve local tutorials (e.g. miden-bank)
+ rm -rf docs/builder/tutorials/miden-bank docs/builder/tutorials/index.md
+ # Note: docs/builder/tutorials/rust-compiler/ is NOT cleaned to preserve local guides (testing, debugging, pitfalls)
# Core Concepts docs → docs/core-concepts/*
if [ -d "vendor/miden-base/docs/src" ]; then
diff --git a/docs/builder/smart-contracts/index.md b/docs/builder/smart-contracts/index.md
index 6481d4ec..e119896d 100644
--- a/docs/builder/smart-contracts/index.md
+++ b/docs/builder/smart-contracts/index.md
@@ -6,7 +6,7 @@ description: "Reference documentation for building Miden smart contracts in Rust
# Miden Smart Contracts
-This section is the complete reference for building smart contracts on Miden using Rust and the Miden SDK (v0.10). If you're new to Miden, follow the hands-on [Miden Bank Tutorial](../tutorials/rust-compiler/miden-bank/).
+This section is the complete reference for building smart contracts on Miden using Rust and the Miden SDK (v0.10). If you're new to Miden, follow the hands-on [Miden Bank Tutorial](../tutorials/miden-bank/).
All Miden Rust contracts compile under these constraints: `#![no_std]`, Rust 2024 edition.
diff --git a/docs/builder/smart-contracts/overview.md b/docs/builder/smart-contracts/overview.md
index f0d787f8..69b7b825 100644
--- a/docs/builder/smart-contracts/overview.md
+++ b/docs/builder/smart-contracts/overview.md
@@ -6,7 +6,7 @@ description: "Miden's execution model, account structure, note system, and trans
# What is a Miden Smart Contract
-Miden is a ZK rollup where transactions execute on the client and only a cryptographic proof is submitted to the network. Every entity — wallets, contracts, faucets — is an account with code, storage, a vault, and a nonce. Assets move between accounts through notes, which act as programmable UTXOs. This page describes the execution model, account structure, note system, and transaction lifecycle. For a hands-on walkthrough, see the [Miden Bank Tutorial](../tutorials/rust-compiler/miden-bank/).
+Miden is a ZK rollup where transactions execute on the client and only a cryptographic proof is submitted to the network. Every entity — wallets, contracts, faucets — is an account with code, storage, a vault, and a nonce. Assets move between accounts through notes, which act as programmable UTXOs. This page describes the execution model, account structure, note system, and transaction lifecycle. For a hands-on walkthrough, see the [Miden Bank Tutorial](../tutorials/miden-bank/).
## What makes Miden different
@@ -148,4 +148,4 @@ Miden supports several account types, configured in `Cargo.toml`:
| [Cross-Component Calls](./cross-component-calls) | Inter-component communication | WIT bindings, `generate!()` |
| [Types](./types) | Felt, Word, Asset — the VM's native types | Field arithmetic |
-Ready to start building? Follow the [Miden Bank Tutorial](../tutorials/rust-compiler/miden-bank/) for a hands-on walkthrough.
+Ready to start building? Follow the [Miden Bank Tutorial](../tutorials/miden-bank/) for a hands-on walkthrough.
diff --git a/docs/builder/smart-contracts/patterns.md b/docs/builder/smart-contracts/patterns.md
index 66bcd57b..bbeb84c0 100644
--- a/docs/builder/smart-contracts/patterns.md
+++ b/docs/builder/smart-contracts/patterns.md
@@ -6,7 +6,7 @@ description: "Common patterns and security considerations for Miden smart contra
# Patterns
-Security considerations and common patterns for Miden smart contracts. For runnable examples, see the [compiler examples directory](https://github.com/0xMiden/compiler/tree/next/examples) and the [Miden Bank Tutorial](../tutorials/rust-compiler/miden-bank/).
+Security considerations and common patterns for Miden smart contracts. For runnable examples, see the [compiler examples directory](https://github.com/0xMiden/compiler/tree/next/examples) and the [Miden Bank Tutorial](../tutorials/miden-bank/).
## Access control
diff --git a/docs/builder/tutorials/rust-compiler/index.md b/docs/builder/tutorials/rust-compiler/index.md
index 2243a4d9..f8eea9ed 100644
--- a/docs/builder/tutorials/rust-compiler/index.md
+++ b/docs/builder/tutorials/rust-compiler/index.md
@@ -29,7 +29,7 @@ If you're new to the Miden Rust compiler, start with the **Miden Bank Tutorial**
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/00-project-setup.md b/docs/builder/tutorials/rust-compiler/miden-bank/00-project-setup.md
deleted file mode 100644
index 2ee30806..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/00-project-setup.md
+++ /dev/null
@@ -1,348 +0,0 @@
----
-sidebar_position: 0
-title: "Part 0: Project Setup"
-description: "Set up a new Miden project and prepare the workspace for building the banking application."
----
-
-# Part 0: Project Setup
-
-In this section, you'll create a new Miden project and set up the workspace structure for our banking application. By the end, you'll have a working project that compiles successfully.
-
-## What You'll Build in This Part
-
-By the end of this section, you will have:
-
-- Created a new Miden project using `miden new`
-- Understood the workspace structure
-- Renamed and configured the project for our bank
-- Successfully compiled a minimal account component
-
-## Prerequisites
-
-Before starting, ensure you have completed the [Get Started installation guide](../../../get-started/setup/installation) and have:
-
-- **Rust toolchain** installed and configured
-- **midenup toolchain** installed with Miden CLI tools (`miden` command available)
-
-Verify your installation:
-
-```bash title=">_ Terminal"
-miden --version
-```
-
-
-Expected output
-
-```text
-The Miden toolchain porcelain:
-
-Environment:
-- cargo version: cargo 1.93.0 (083ac5135 2025-12-15).
-
-Midenup:
-- midenup + miden version: 0.1.0.
-- active toolchain version: 0.20.3.
-- ...
-```
-
-
-
-## Step 1: Create the Project
-
-Create a new Miden project using the CLI:
-
-```bash title=">_ Terminal"
-miden new miden-bank
-cd miden-bank
-```
-
-This creates a workspace with the following structure:
-
-```text
-miden-bank/
-├── contracts/ # Smart contract code
-│ ├── counter-account/ # Example account contract (we'll replace this)
-│ └── increment-note/ # Example note script (we'll replace this)
-├── integration/ # Tests and deployment scripts
-│ ├── src/
-│ │ ├── bin/ # Executable scripts for on-chain interactions
-│ │ ├── lib.rs
-│ │ └── helpers.rs # Helper functions for tests
-│ └── tests/ # Test files
-├── Cargo.toml # Workspace root
-└── rust-toolchain.toml # Rust toolchain specification
-```
-
-The project follows Miden's design philosophy:
-
-- **`contracts/`**: Your smart contract code (account components, note scripts, transaction scripts)
-- **`integration/`**: All on-chain interactions, deployment scripts, and tests
-
-## Step 2: Set Up the Bank Account Contract
-
-We'll replace the example `counter-account` with our `bank-account`. First, rename the directory:
-
-```bash title=">_ Terminal"
-mv contracts/counter-account contracts/bank-account
-```
-
-Now update the `Cargo.toml` inside `contracts/bank-account/`:
-
-```toml title="contracts/bank-account/Cargo.toml"
-[package]
-name = "bank-account"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-miden = { version = "0.10" }
-
-[package.metadata.component]
-package = "miden:bank-account"
-
-[package.metadata.miden]
-project-kind = "account"
-supported-types = ["RegularAccountImmutableCode"]
-```
-
-### Key Configuration Options
-
-| Field | Description |
-|-------|-------------|
-| `crate-type = ["cdylib"]` | Required for WebAssembly compilation |
-| `project-kind = "account"` | Tells the compiler this is an account component |
-| `supported-types` | Account types this component supports |
-| `package = "miden:bank-account"` | The component package name for cross-component calls |
-
-:::info Supported Account Types
-`RegularAccountImmutableCode` means the account code cannot be changed after deployment. This is appropriate for our bank since we want the logic to be fixed.
-:::
-
-## Step 3: Create a Minimal Bank Component
-
-Replace the contents of `contracts/bank-account/src/lib.rs` with a minimal bank structure:
-
-```rust title="contracts/bank-account/src/lib.rs"
-// Do not link against libstd (i.e. anything defined in `std::`)
-#![no_std]
-#![feature(alloc_error_handler)]
-
-#[macro_use]
-extern crate alloc;
-
-use miden::*;
-
-/// Bank account component - we'll build this up throughout the tutorial.
-#[component]
-struct Bank {
- /// Tracks whether the bank has been initialized (deposits enabled).
- /// Word layout: [is_initialized (0 or 1), 0, 0, 0]
- #[storage(description = "initialized")]
- initialized: Value,
-
- /// Maps depositor AccountId -> balance (as Felt).
- /// We'll use this to track user balances in Part 1.
- #[storage(description = "balances")]
- balances: StorageMap,
-}
-
-#[component]
-impl Bank {
- /// Initialize the bank account, enabling deposits.
- pub fn initialize(&mut self) {
- // Read current value from storage
- let current: Word = self.initialized.read();
-
- // Check not already initialized
- assert!(
- current[0].as_u64() == 0,
- "Bank already initialized"
- );
-
- // Set initialized flag to 1
- let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
- self.initialized.write(initialized_word);
- }
-
- /// Get the balance for a depositor.
- ///
- /// This method is required for the component to compile correctly -
- /// account components must use WIT binding types (like AccountId)
- /// in at least one public method.
- pub fn get_balance(&self, depositor: AccountId) -> Felt {
- let key = Word::from([depositor.prefix, depositor.suffix, felt!(0), felt!(0)]);
- self.balances.get(&key)
- }
-}
-```
-
-This is our starting point with two storage slots:
-- `initialized`: A `Value` slot to track whether the bank is ready
-- `balances`: A `StorageMap` to track user balances (we'll use this starting in Part 1)
-
-:::note Compiler Requirement
-Account components must use WIT binding types (like `AccountId`, `Asset`, etc.) in at least one public method signature for the compiler to generate the required bindings correctly. The `get_balance` method serves this purpose.
-:::
-
-## Step 4: Update the Workspace Configuration
-
-Update the root `Cargo.toml` to reflect our renamed contract:
-
-```toml title="Cargo.toml"
-[workspace]
-members = [
- "integration"
-]
-exclude = [
- "contracts/",
-]
-resolver = "2"
-
-[workspace.package]
-edition = "2021"
-
-[workspace.dependencies]
-```
-
-:::info Contracts Are Excluded
-In v0.13, contracts are excluded from the Cargo workspace and built independently by `cargo miden`. Each contract specifies its own `miden` dependency directly. Only the `integration` crate remains a workspace member.
-:::
-
-## Step 5: Build and Verify
-
-Let's verify everything compiles correctly:
-
-```bash title=">_ Terminal"
-cd contracts/bank-account
-miden build --release
-```
-
-
-Expected output
-
-```text
- Compiling bank-account v0.1.0 (/path/to/miden-bank/contracts/bank-account)
- Finished `release` profile [optimized] target(s)
-Creating Miden package /path/to/miden-bank/target/miden/release/bank_account.masp
-```
-
-
-
-The compiled output is stored in `target/miden/release/bank_account.masp`.
-
-:::tip What's a .masp File?
-A `.masp` file is a Miden Assembly Package. It contains the compiled MASM (Miden Assembly) code and metadata needed to deploy and interact with your contract.
-:::
-
-## Try It: Verify Your Setup
-
-Let's create a simple test to verify the bank account can be created. Create a new test file:
-
-```rust title="integration/tests/part0_setup_test.rs"
-use integration::helpers::{
- build_project_in_dir, create_testing_account_from_package, AccountCreationConfig,
-};
-use miden_client::account::{StorageMap, StorageSlot, StorageSlotName};
-use miden_client::Word;
-use std::{path::Path, sync::Arc};
-
-#[tokio::test]
-async fn test_bank_account_builds_and_loads() -> anyhow::Result<()> {
- // Build the bank account contract
- let bank_package = Arc::new(build_project_in_dir(
- Path::new("../contracts/bank-account"),
- true,
- )?);
-
- // Create named storage slots matching the contract's storage layout
- let initialized_slot =
- StorageSlotName::new("miden::component::miden_bank_account::initialized")
- .expect("Valid slot name");
- let balances_slot =
- StorageSlotName::new("miden::component::miden_bank_account::balances")
- .expect("Valid slot name");
-
- let bank_cfg = AccountCreationConfig {
- storage_slots: vec![
- StorageSlot::with_value(initialized_slot, Word::default()),
- StorageSlot::with_map(
- balances_slot,
- StorageMap::with_entries([]).expect("Empty storage map"),
- ),
- ],
- ..Default::default()
- };
-
- let bank_account =
- create_testing_account_from_package(bank_package.clone(), bank_cfg).await?;
-
- // Verify the account was created
- println!("Bank account created with ID: {:?}", bank_account.id());
- println!("Part 0 setup verified!");
-
- Ok(())
-}
-```
-
-Run the test from the project root:
-
-```bash title=">_ Terminal"
-cargo test --package integration test_bank_account_builds_and_loads -- --nocapture
-```
-
-
-Expected output
-
-```text
- Compiling integration v0.1.0 (/path/to/miden-bank/integration)
- Finished `test` profile [unoptimized + debuginfo] target(s)
- Running tests/part0_setup_test.rs
-
-running 1 test
-Bank account created with ID: 0x...
-Part 0 setup verified!
-test test_bank_account_builds_and_loads ... ok
-
-test result: ok. 1 passed; 0 failed; 0 ignored
-```
-
-
-
-:::tip Troubleshooting
-**"Failed to build bank account contract"**: Make sure the `contracts/bank-account/Cargo.toml` is properly configured and you've updated the root `Cargo.toml` members list.
-
-**"cannot find module helpers"**: Ensure the `integration/src/helpers.rs` file exists (it should have been generated by `miden new`).
-:::
-
-## What We've Built So Far
-
-At this point, you have:
-
-| Component | Status | Description |
-|-----------|--------|-------------|
-| `bank-account` | Minimal | Initialization flag + balance storage |
-| `deposit-note` | Not started | Coming in Part 4 |
-| `withdraw-note` | Not started | Coming in Part 7 |
-| `init-tx-script` | Not started | Coming in Part 6 |
-
-Your bank can be created, but doesn't do anything useful yet. In the next parts, we'll add:
-
-1. **Part 1**: Deeper dive into storage (Value vs StorageMap)
-2. **Part 2**: Business rules and constraints
-3. **Part 3**: Asset handling for deposits
-4. And more...
-
-## Key Takeaways
-
-1. **`miden new`** creates a complete project workspace with contracts and integration folders
-2. **Account components** are defined with `#[component]` on a struct
-3. **Storage slots** are declared with `#[storage(description = "...")]` attributes (the compiler auto-assigns slot numbers)
-4. **`miden build`** compiles Rust to Miden Assembly (.masp package)
-5. **Tests verify** that your code works before moving on
-
-## Next Steps
-
-Now that your project is set up, let's dive deeper into account components and storage in [Part 1: Account Components and Storage](./account-components).
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/01-account-components.md b/docs/builder/tutorials/rust-compiler/miden-bank/01-account-components.md
deleted file mode 100644
index 3bda45a0..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/01-account-components.md
+++ /dev/null
@@ -1,426 +0,0 @@
----
-sidebar_position: 1
-title: "Part 1: Account Components and Storage"
-description: "Learn how to define account components with the #[component] attribute and manage persistent state using Value and StorageMap storage types."
----
-
-# Part 1: Account Components and Storage
-
-In this section, you'll learn the fundamentals of building Miden account components. We'll expand our Bank to include balance tracking with a `StorageMap`, giving us the foundation for deposits and withdrawals.
-
-## What You'll Build in This Part
-
-By the end of this section, you will have:
-
-- Understood the `#[component]` attribute and what it generates
-- Added a `StorageMap` for tracking depositor balances
-- Implemented a `get_balance()` query method
-- **Verified it works** with a MockChain test
-
-## Building on Part 0
-
-In Part 0, we created a minimal bank with just an `initialized` flag. Now we'll add balance tracking:
-
-```text
-Part 0: Part 1:
-┌────────────────────┐ ┌──────────────────────────┐
-│ Bank │ │ Bank │
-│ ───────────────── │ ──► │ ──────────────────────── │
-│ initialized (Value)│ │ initialized (Value) │
-│ │ │ balances (StorageMap) │ ◄── NEW
-└────────────────────┘ └──────────────────────────┘
-```
-
-## The #[component] Attribute
-
-The `#[component]` attribute marks a struct as a Miden account component. When you compile with `miden build`, it generates:
-
-- **WIT (WebAssembly Interface Types)** bindings for cross-component calls
-- **MASM (Miden Assembly)** code for the account logic
-- **Storage slot management** code
-
-Let's expand our Bank component:
-
-## Step 1: Add the Balances Storage Map
-
-Update `contracts/bank-account/src/lib.rs`:
-
-```rust title="contracts/bank-account/src/lib.rs" {17-20}
-#![no_std]
-#![feature(alloc_error_handler)]
-
-#[macro_use]
-extern crate alloc;
-
-use miden::*;
-
-/// Bank account component that tracks depositor balances.
-#[component]
-struct Bank {
- /// Tracks whether the bank has been initialized (deposits enabled).
- /// Word layout: [is_initialized (0 or 1), 0, 0, 0]
- #[storage(description = "initialized")]
- initialized: Value,
-
- /// Maps depositor AccountId -> balance (as Felt)
- /// Key: [prefix, suffix, asset_prefix, asset_suffix]
- #[storage(description = "balances")]
- balances: StorageMap,
-}
-```
-
-We've added a `StorageMap` that will track each depositor's balance. The compiler auto-assigns slot numbers based on field order.
-
-## Storage Types Explained
-
-Miden accounts have storage slots that persist state on-chain. Each slot holds one `Word` (4 Felts = 32 bytes). The Miden Rust compiler provides two abstractions:
-
-### Value Storage
-
-The `Value` type provides access to a single storage slot:
-
-```rust
-#[storage(description = "initialized")]
-initialized: Value,
-```
-
-Use `Value` when you need to store a single `Word` of data.
-
-**Reading and writing:**
-
-```rust
-// Read returns a Word
-let current: Word = self.initialized.read();
-
-// Check the first element (our flag)
-if current[0].as_u64() == 0 {
- // Not initialized
-}
-
-// Write a new value
-let new_value = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
-self.initialized.write(new_value);
-```
-
-:::tip Type Annotations
-The `.read()` method requires a type annotation: `let current: Word = self.initialized.read();`
-:::
-
-### StorageMap
-
-The `StorageMap` type provides key-value storage within a slot:
-
-```rust
-#[storage(description = "balances")]
-balances: StorageMap,
-```
-
-Use `StorageMap` when you need to store multiple values indexed by keys.
-
-**Reading and writing:**
-
-```rust
-// Create a key (must be a Word)
-let key = Word::from([
- depositor.prefix,
- depositor.suffix,
- felt!(0),
- felt!(0),
-]);
-
-// Get returns a Felt (single value, not a Word)
-let balance: Felt = self.balances.get(&key);
-
-// Set stores a Felt at the key
-let new_balance = balance + deposit_amount;
-self.balances.set(key, new_balance);
-```
-
-:::warning StorageMap Returns Felt
-Unlike `Value::read()` which returns a `Word`, `StorageMap::get()` returns a single `Felt`. This is an important distinction.
-:::
-
-### Storage Layout
-
-Plan your storage layout carefully:
-
-| Name | Type | Purpose |
-|------|------|---------|
-| `initialized` | `Value` | Initialization flag |
-| `balances` | `StorageMap` | Depositor balances |
-
-The `description` attribute generates named slot identifiers (e.g., `miden::component::miden_bank_account::initialized`) used in tests to reference specific slots. The compiler auto-assigns slot numbers based on field order.
-
-## Step 2: Implement Component Methods
-
-Now let's add methods to our Bank. The `#[component]` attribute is also used on the `impl` block:
-
-```rust title="contracts/bank-account/src/lib.rs"
-#[component]
-impl Bank {
- /// Initialize the bank account, enabling deposits.
- pub fn initialize(&mut self) {
- // Read current value from storage
- let current: Word = self.initialized.read();
-
- // Check not already initialized
- assert!(
- current[0].as_u64() == 0,
- "Bank already initialized"
- );
-
- // Set initialized flag to 1
- let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
- self.initialized.write(initialized_word);
- }
-
- /// Get the balance for a depositor.
- pub fn get_balance(&self, depositor: AccountId) -> Felt {
- let key = Word::from([depositor.prefix, depositor.suffix, felt!(0), felt!(0)]);
- self.balances.get(&key)
- }
-
- /// Check that the bank is initialized.
- fn require_initialized(&self) {
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 1,
- "Bank not initialized - deposits not enabled"
- );
- }
-}
-```
-
-### Public vs Private Methods
-
-- **Public methods** (`pub fn`) are exposed in the generated WIT interface and can be called by other contracts
-- **Private methods** (`fn`) are internal and cannot be called from outside
-
-```rust
-// Public: Can be called by note scripts and other contracts
-pub fn get_balance(&self, depositor: AccountId) -> Felt { ... }
-
-// Private: Internal helper, not exposed
-fn require_initialized(&self) { ... }
-```
-
-## Step 3: Build the Component
-
-Build your updated account component:
-
-```bash title=">_ Terminal"
-cd contracts/bank-account
-miden build
-```
-
-This compiles the Rust code to Miden Assembly and generates:
-
-- `target/miden/release/bank_account.masp` - The compiled package
-- `target/generated-wit/` - WIT interface files for other contracts to use
-
-## Try It: Verify Your Code
-
-Let's write a MockChain test to verify our Bank component works correctly. This test will:
-1. Create a bank account
-2. Initialize it
-3. Verify the storage was updated
-
-Create a new test file:
-
-```rust title="integration/tests/part1_account_test.rs"
-use integration::helpers::{
- build_project_in_dir, create_testing_account_from_package, AccountCreationConfig,
-};
-use miden_client::account::{StorageMap, StorageSlot, StorageSlotName};
-use miden_client::{Felt, Word};
-use std::{path::Path, sync::Arc};
-
-#[tokio::test]
-async fn test_bank_account_storage() -> anyhow::Result<()> {
- // =========================================================================
- // SETUP: Build contracts and create the bank account
- // =========================================================================
-
- // Build the bank account contract
- let bank_package = Arc::new(build_project_in_dir(
- Path::new("../contracts/bank-account"),
- true,
- )?);
-
- // Create named storage slots matching the contract's storage layout
- // The naming convention is: miden::component::{package_name_underscored}::{field_name}
- let initialized_slot =
- StorageSlotName::new("miden::component::miden_bank_account::initialized")
- .expect("Valid slot name");
- let balances_slot =
- StorageSlotName::new("miden::component::miden_bank_account::balances")
- .expect("Valid slot name");
-
- let bank_cfg = AccountCreationConfig {
- storage_slots: vec![
- StorageSlot::with_value(initialized_slot.clone(), Word::default()),
- StorageSlot::with_map(
- balances_slot.clone(),
- StorageMap::with_entries([]).expect("Empty storage map"),
- ),
- ],
- ..Default::default()
- };
-
- let bank_account =
- create_testing_account_from_package(bank_package.clone(), bank_cfg).await?;
-
- // =========================================================================
- // VERIFY: Check initial storage state
- // =========================================================================
-
- // Verify initialized flag starts as 0
- let initialized_value = bank_account.storage().get_item(&initialized_slot)?;
- assert_eq!(
- initialized_value,
- Word::default(),
- "Initialized flag should start as 0"
- );
-
- println!("Bank account created successfully!");
- println!(" Account ID: {:?}", bank_account.id());
- println!(" Initialized flag: {:?}", initialized_value[0].as_int());
-
- // =========================================================================
- // VERIFY: Storage slots are correctly configured
- // =========================================================================
-
- // Check that we can query the balances map (should return 0 for any key)
- let test_key = Word::from([Felt::new(1), Felt::new(2), Felt::new(0), Felt::new(0)]);
- let balance = bank_account.storage().get_map_item(&balances_slot, test_key)?;
-
- // Balance for non-existent depositor should be all zeros
- assert_eq!(
- balance,
- Word::default(),
- "Balance for unknown depositor should be zero"
- );
-
- println!(" Balances map accessible: Yes");
- println!("\nPart 1 test passed!");
-
- Ok(())
-}
-```
-
-Run the test from the project root:
-
-```bash title=">_ Terminal"
-cargo test --package integration test_bank_account_storage -- --nocapture
-```
-
-
-Expected output
-
-```text
- Compiling integration v0.1.0 (/path/to/miden-bank/integration)
- Finished `test` profile [unoptimized + debuginfo] target(s)
- Running tests/part1_account_test.rs
-
-running 1 test
-Bank account created successfully!
- Account ID: 0x...
- Initialized flag: 0
- Balances map accessible: Yes
-
-Part 1 test passed!
-test test_bank_account_storage ... ok
-
-test result: ok. 1 passed; 0 failed; 0 ignored
-```
-
-
-
-:::tip Troubleshooting
-**"cannot find function `build_project_in_dir`"**: Make sure your `integration/src/helpers.rs` exports this function and `integration/src/lib.rs` has `pub mod helpers;`.
-
-**"StorageSlot not found"**: Ensure you're using the correct imports: `use miden_client::account::{StorageSlot, StorageSlotName};`
-:::
-
-## Complete Code for This Part
-
-Here's the full `lib.rs` after Part 1:
-
-
-Click to expand full code
-
-```rust title="contracts/bank-account/src/lib.rs"
-#![no_std]
-#![feature(alloc_error_handler)]
-
-#[macro_use]
-extern crate alloc;
-
-use miden::*;
-
-/// Bank account component that tracks depositor balances.
-#[component]
-struct Bank {
- /// Tracks whether the bank has been initialized (deposits enabled).
- /// Word layout: [is_initialized (0 or 1), 0, 0, 0]
- #[storage(description = "initialized")]
- initialized: Value,
-
- /// Maps depositor AccountId -> balance (as Felt)
- /// Key: [prefix, suffix, asset_prefix, asset_suffix]
- #[storage(description = "balances")]
- balances: StorageMap,
-}
-
-#[component]
-impl Bank {
- /// Initialize the bank account, enabling deposits.
- pub fn initialize(&mut self) {
- // Read current value from storage
- let current: Word = self.initialized.read();
-
- // Check not already initialized
- assert!(
- current[0].as_u64() == 0,
- "Bank already initialized"
- );
-
- // Set initialized flag to 1
- let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
- self.initialized.write(initialized_word);
- }
-
- /// Get the balance for a depositor.
- pub fn get_balance(&self, depositor: AccountId) -> Felt {
- let key = Word::from([depositor.prefix, depositor.suffix, felt!(0), felt!(0)]);
- self.balances.get(&key)
- }
-
- /// Check that the bank is initialized.
- fn require_initialized(&self) {
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 1,
- "Bank not initialized - deposits not enabled"
- );
- }
-}
-```
-
-
-
-## Key Takeaways
-
-1. **`#[component]`** marks structs and impl blocks as Miden account components
-2. **`Value`** stores a single Word, read with `.read()`, write with `.write()`
-3. **`StorageMap`** stores key-value pairs, access with `.get()` and `.set()`
-4. **Storage slots** are identified by name (auto-assigned by compiler), each holds 4 Felts (32 bytes)
-5. **Public methods** are callable by other contracts via generated bindings
-
-:::tip View Complete Source
-See the complete bank account implementation in the [miden-bank repository](https://github.com/keinberger/miden-bank/blob/main/contracts/bank-account/src/lib.rs).
-:::
-
-## Next Steps
-
-Now that you understand account components and storage, let's learn how to define business rules with [Part 2: Constants and Constraints](./constants-constraints).
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/02-constants-constraints.md b/docs/builder/tutorials/rust-compiler/miden-bank/02-constants-constraints.md
deleted file mode 100644
index 2e47aeee..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/02-constants-constraints.md
+++ /dev/null
@@ -1,456 +0,0 @@
----
-sidebar_position: 2
-title: "Part 2: Constants and Constraints"
-description: "Learn how to define constants for business rules and use assertions to validate transactions in Miden Rust contracts."
----
-
-# Part 2: Constants and Constraints
-
-In this section, you'll learn how to define business rules using constants and enforce them with assertions. We'll implement deposit limits and see how failed constraints cause transactions to be rejected.
-
-## What You'll Build in This Part
-
-By the end of this section, you will have:
-
-- Defined constants for business rules
-- Used `assert!()` for transaction validation
-- Learned safe Felt comparison with `.as_u64()`
-- Added a deposit method skeleton with validation
-- **Verified constraints work** by testing that invalid operations fail
-
-## Building on Part 1
-
-In Part 1, we set up the Bank's storage structure. Now we'll add business rules:
-
-```text
-Part 1: Part 2:
-┌──────────────────┐ ┌──────────────────┐
-│ Bank │ │ Bank │
-│ ─────────────────│ ──► │ ─────────────────│
-│ + initialize() │ │ + initialize() │
-│ + get_balance() │ │ + get_balance() │
-│ │ │ + deposit() │ ◄── NEW (skeleton)
-│ │ │ + MAX_DEPOSIT │ ◄── NEW constant
-└──────────────────┘ └──────────────────┘
-```
-
-## Defining Constants
-
-Constants in Miden Rust contracts work just like regular Rust constants:
-
-```rust title="contracts/bank-account/src/lib.rs"
-/// Maximum allowed deposit amount per transaction.
-///
-/// Value: 1,000,000 tokens (arbitrary limit for demonstration)
-const MAX_DEPOSIT_AMOUNT: u64 = 1_000_000;
-```
-
-Use constants for:
-
-- Business rule limits (max amounts, timeouts)
-- Magic numbers that need documentation
-- Values used in multiple places
-
-:::info Constants vs Storage
-Constants are compiled into the contract code and cannot change. Use storage slots for values that need to be modified at runtime.
-:::
-
-## The assert!() Macro
-
-The `assert!()` macro validates conditions during transaction execution:
-
-```rust title="contracts/bank-account/src/lib.rs"
-pub fn initialize(&mut self) {
- // Check not already initialized
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 0,
- "Bank already initialized"
- );
-
- // Set initialized flag to 1
- let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
- self.initialized.write(initialized_word);
-}
-```
-
-When an assertion fails:
-
-1. The Miden VM execution halts
-2. No valid proof can be generated
-3. The transaction is rejected
-
-This is the primary mechanism for enforcing business rules in Miden contracts.
-
-## Safe Felt Comparisons
-
-:::warning Pitfall: Felt Comparison Operators
-Never use `<`, `>`, `<=`, or `>=` operators directly on `Felt` values. They produce incorrect results due to field element ordering.
-:::
-
-**Wrong approach:**
-
-```rust
-// DON'T DO THIS - produces incorrect results
-if deposit_amount > felt!(1_000_000) {
- // This comparison is unreliable!
-}
-```
-
-**Correct approach:**
-
-```rust
-// CORRECT - convert to u64 first
-if deposit_amount.as_u64() > MAX_DEPOSIT_AMOUNT {
- // This works correctly
-}
-```
-
-The `.as_u64()` method extracts the underlying 64-bit integer from a Felt, allowing standard Rust comparisons.
-
-## Step 1: Add the Constant and Deposit Method
-
-Update your `contracts/bank-account/src/lib.rs` to add the constant and a deposit method skeleton:
-
-```rust title="contracts/bank-account/src/lib.rs" {1-4,36-55}
-/// Maximum allowed deposit amount per transaction.
-///
-/// Value: 1,000,000 tokens (arbitrary limit for demonstration)
-const MAX_DEPOSIT_AMOUNT: u64 = 1_000_000;
-
-#[component]
-impl Bank {
- /// Initialize the bank account, enabling deposits.
- pub fn initialize(&mut self) {
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 0,
- "Bank already initialized"
- );
-
- let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
- self.initialized.write(initialized_word);
- }
-
- /// Get the balance for a depositor.
- pub fn get_balance(&self, depositor: AccountId) -> Felt {
- let key = Word::from([depositor.prefix, depositor.suffix, felt!(0), felt!(0)]);
- self.balances.get(&key)
- }
-
- /// Check that the bank is initialized.
- fn require_initialized(&self) {
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 1,
- "Bank not initialized - deposits not enabled"
- );
- }
-
- /// Deposit assets into the bank.
- /// For now, this just validates constraints - we'll add asset handling in Part 3.
- pub fn deposit(&mut self, depositor: AccountId, deposit_asset: Asset) {
- // ========================================================================
- // CONSTRAINT: Bank must be initialized
- // ========================================================================
- self.require_initialized();
-
- // Extract the fungible amount from the asset
- let deposit_amount = deposit_asset.inner[0];
-
- // ========================================================================
- // CONSTRAINT: Maximum deposit amount check
- // ========================================================================
- assert!(
- deposit_amount.as_u64() <= MAX_DEPOSIT_AMOUNT,
- "Deposit amount exceeds maximum allowed"
- );
-
- // We'll add balance tracking and asset handling in Part 3
- // For now, just validate the constraints
- }
-}
-```
-
-### The require_initialized() Guard
-
-We use a helper method to check initialization state:
-
-```rust
-fn require_initialized(&self) {
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 1,
- "Bank not initialized - deposits not enabled"
- );
-}
-```
-
-This pattern:
-
-- Centralizes the initialization check
-- Provides a clear error message
-- Can be reused across multiple methods
-
-## How Assertions Affect Proving
-
-When an assertion fails in the Miden VM:
-
-```text
-Transaction Execution Flow:
-┌─────────────────────┐
-│ User submits TX │
-└──────────┬──────────┘
- ▼
-┌─────────────────────┐
-│ VM executes code │
-└──────────┬──────────┘
- ▼
- ┌──────┴──────┐
- │ Assertion? │
- └──────┬──────┘
- Pass │ Fail
- ┌──────┴──────┐
- ▼ ▼
-┌────────┐ ┌────────────┐
-│ Prove │ │ TX Rejected│
-│ Success│ │ No Proof │
-└────────┘ └────────────┘
-```
-
-Key points:
-
-- Failed assertions prevent proof generation
-- No state changes occur if the transaction fails
-- Error messages help with debugging
-
-## Step 2: Build and Verify
-
-Build the updated contract:
-
-```bash title=">_ Terminal"
-cd contracts/bank-account
-miden build
-```
-
-## Try It: Verify Constraints Work
-
-Let's write a test to verify our constraints work correctly. This test verifies that depositing without initialization fails:
-
-```rust title="integration/tests/part2_constraints_test.rs"
-use integration::helpers::{
- build_project_in_dir, create_testing_account_from_package, AccountCreationConfig,
-};
-use miden_client::account::{StorageMap, StorageSlot, StorageSlotName};
-use miden_client::Word;
-use std::{path::Path, sync::Arc};
-
-/// Test that our constraint logic is set up correctly
-#[tokio::test]
-async fn test_constraints_are_defined() -> anyhow::Result<()> {
- // Build the bank account contract to verify it compiles with constraints
- let bank_package = Arc::new(build_project_in_dir(
- Path::new("../contracts/bank-account"),
- true,
- )?);
-
- // Create named storage slots
- let initialized_slot =
- StorageSlotName::new("miden::component::miden_bank_account::initialized")
- .expect("Valid slot name");
- let balances_slot =
- StorageSlotName::new("miden::component::miden_bank_account::balances")
- .expect("Valid slot name");
-
- // Create an uninitialized bank account
- let bank_cfg = AccountCreationConfig {
- storage_slots: vec![
- StorageSlot::with_value(initialized_slot.clone(), Word::default()),
- StorageSlot::with_map(
- balances_slot,
- StorageMap::with_entries([]).expect("Empty storage map"),
- ),
- ],
- ..Default::default()
- };
-
- let bank_account =
- create_testing_account_from_package(bank_package.clone(), bank_cfg).await?;
-
- // Verify the bank starts uninitialized
- let initialized = bank_account.storage().get_item(&initialized_slot)?;
- assert_eq!(
- initialized[0].as_int(),
- 0,
- "Bank should start uninitialized"
- );
-
- println!("Bank account created with constraints!");
- println!(" - MAX_DEPOSIT_AMOUNT: 1,000,000");
- println!(" - require_initialized() guard in place");
- println!(" - Initialization status: {}", initialized[0].as_int());
- println!("\nPart 2 constraints test passed!");
-
- Ok(())
-}
-```
-
-Run the test from the project root:
-
-```bash title=">_ Terminal"
-cargo test --package integration test_constraints_are_defined -- --nocapture
-```
-
-
-Expected output
-
-```text
- Compiling integration v0.1.0 (/path/to/miden-bank/integration)
- Finished `test` profile [unoptimized + debuginfo] target(s)
- Running tests/part2_constraints_test.rs
-
-running 1 test
-Bank account created with constraints!
- - MAX_DEPOSIT_AMOUNT: 1,000,000
- - require_initialized() guard in place
- - Initialization status: 0
-
-Part 2 constraints test passed!
-test test_constraints_are_defined ... ok
-
-test result: ok. 1 passed; 0 failed; 0 ignored
-```
-
-
-
-:::tip Preview: Testing Failed Assertions
-In Part 4, when we have the deposit note script, we'll write a full test that verifies:
-1. Depositing without initialization fails
-2. Depositing amounts over MAX_DEPOSIT_AMOUNT fails
-
-For now, the constraint logic is in place and we've verified the contract compiles.
-:::
-
-## Common Constraint Patterns
-
-### Balance Checks (Preview for Part 3)
-
-```rust
-fn require_sufficient_balance(&self, depositor: AccountId, amount: Felt) {
- let balance = self.get_balance(depositor);
- assert!(
- balance.as_u64() >= amount.as_u64(),
- "Insufficient balance"
- );
-}
-```
-
-:::danger Critical: Always Validate Before Subtraction
-This pattern is **mandatory** for any operation that subtracts from a balance. Miden uses field element (Felt) arithmetic, which is modular. Without this check, subtracting more than the balance would NOT cause an error - instead, the value would silently wrap around to a large positive number, effectively allowing unlimited withdrawals. See [Common Pitfalls](../pitfalls#felt-arithmetic-underflowoverflow) for more details.
-:::
-
-### State Checks
-
-```rust
-fn require_not_paused(&self) {
- let paused: Word = self.paused.read();
- assert!(
- paused[0].as_u64() == 0,
- "Contract is paused"
- );
-}
-```
-
-## Complete Code for This Part
-
-Here's the full `lib.rs` after Part 2:
-
-
-Click to expand full code
-
-```rust title="contracts/bank-account/src/lib.rs"
-#![no_std]
-#![feature(alloc_error_handler)]
-
-#[macro_use]
-extern crate alloc;
-
-use miden::*;
-
-/// Maximum allowed deposit amount per transaction.
-const MAX_DEPOSIT_AMOUNT: u64 = 1_000_000;
-
-/// Bank account component that tracks depositor balances.
-#[component]
-struct Bank {
- #[storage(description = "initialized")]
- initialized: Value,
-
- #[storage(description = "balances")]
- balances: StorageMap,
-}
-
-#[component]
-impl Bank {
- /// Initialize the bank account, enabling deposits.
- pub fn initialize(&mut self) {
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 0,
- "Bank already initialized"
- );
-
- let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
- self.initialized.write(initialized_word);
- }
-
- /// Get the balance for a depositor.
- pub fn get_balance(&self, depositor: AccountId) -> Felt {
- let key = Word::from([depositor.prefix, depositor.suffix, felt!(0), felt!(0)]);
- self.balances.get(&key)
- }
-
- /// Check that the bank is initialized.
- fn require_initialized(&self) {
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 1,
- "Bank not initialized - deposits not enabled"
- );
- }
-
- /// Deposit assets into the bank.
- pub fn deposit(&mut self, depositor: AccountId, deposit_asset: Asset) {
- // CONSTRAINT: Bank must be initialized
- self.require_initialized();
-
- let deposit_amount = deposit_asset.inner[0];
-
- // CONSTRAINT: Maximum deposit amount check
- assert!(
- deposit_amount.as_u64() <= MAX_DEPOSIT_AMOUNT,
- "Deposit amount exceeds maximum allowed"
- );
-
- // Balance tracking and asset handling added in Part 3
- }
-}
-```
-
-
-
-## Key Takeaways
-
-1. **Constants** define immutable business rules at compile time
-2. **`assert!()`** enforces constraints - failures reject the transaction
-3. **Always use `.as_u64()`** for Felt comparisons, never direct operators
-4. **Helper methods** like `require_initialized()` centralize validation logic
-5. **Failed assertions** mean no valid proof can be generated
-
-:::tip View Complete Source
-See the complete constraint implementation in the [miden-bank repository](https://github.com/keinberger/miden-bank/blob/main/contracts/bank-account/src/lib.rs).
-:::
-
-## Next Steps
-
-Now that you can define and enforce business rules, let's learn how to handle assets in [Part 3: Asset Management](./asset-management).
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/03-asset-management.md b/docs/builder/tutorials/rust-compiler/miden-bank/03-asset-management.md
deleted file mode 100644
index b6bdce3b..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/03-asset-management.md
+++ /dev/null
@@ -1,619 +0,0 @@
----
-sidebar_position: 3
-title: "Part 3: Asset Management"
-description: "Learn how to handle fungible assets in Miden Rust contracts using vault operations and balance tracking."
----
-
-# Part 3: Asset Management
-
-In this section, you'll learn how to receive and send assets in Miden accounts. We'll complete the deposit logic that receives tokens into the bank's vault and tracks balances per depositor.
-
-## What You'll Build in This Part
-
-By the end of this section, you will have:
-
-- Understood the `Asset` type structure for fungible assets
-- Implemented full deposit logic with `native_account::add_asset()`
-- Learned about balance key design for per-user, per-asset tracking
-- Added a withdraw method skeleton (to be completed in Part 7)
-- **Verified deposits work** with a MockChain test
-
-## Building on Part 2
-
-In Part 2, we added constraints. Now we'll complete the deposit function with actual asset handling:
-
-```text
-Part 2: Part 3:
-┌──────────────────┐ ┌──────────────────┐
-│ Bank │ │ Bank │
-│ ─────────────────│ ──► │ ─────────────────│
-│ + deposit() │ │ + deposit() │ ◄── COMPLETE
-│ (skeleton) │ │ + balance tracking
-│ │ │ + vault operations
-│ │ │ + withdraw() │ ◄── NEW (skeleton)
-└──────────────────┘ └──────────────────┘
-```
-
-## The Asset Type
-
-Miden represents fungible assets as a `Word` (4 Felts) with this layout:
-
-```text
-Asset Layout: [amount, 0, faucet_suffix, faucet_prefix]
- ━━━━━━━ ━ ━━━━━━━━━━━━━ ━━━━━━━━━━━━━
- index 0 1 index 2 index 3
-```
-
-| Index | Field | Description |
-| ----- | --------------- | ------------------------------------ |
-| 0 | `amount` | The quantity of tokens |
-| 1 | (reserved) | Always 0 for fungible assets |
-| 2 | `faucet_suffix` | Second part of the faucet account ID |
-| 3 | `faucet_prefix` | First part of the faucet account ID |
-
-Access these fields through `asset.inner`:
-
-```rust
-let amount = deposit_asset.inner[0]; // The token amount
-let faucet_suffix = deposit_asset.inner[2]; // Faucet ID suffix
-let faucet_prefix = deposit_asset.inner[3]; // Faucet ID prefix
-```
-
-## Receiving Assets with add_asset()
-
-The `native_account::add_asset()` function adds an asset to the account's vault:
-
-```rust
-// Add asset to the bank's vault
-native_account::add_asset(deposit_asset);
-```
-
-When called:
-
-- The asset is added to the account's internal vault
-- The vault tracks all assets the account holds
-- Multiple assets of the same type are combined automatically
-
-:::info Vault vs Balance Tracking
-The vault is managed by the Miden protocol automatically. Our `StorageMap` for balances is an **application-level** tracking of who deposited what, separate from the protocol-level vault.
-:::
-
-## Step 1: Complete the Deposit Function
-
-Update `contracts/bank-account/src/lib.rs` to complete the deposit function with balance tracking and vault operations:
-
-```rust title="contracts/bank-account/src/lib.rs"
-/// Deposit assets into the bank.
-pub fn deposit(&mut self, depositor: AccountId, deposit_asset: Asset) {
- // ========================================================================
- // CONSTRAINT: Bank must be initialized
- // ========================================================================
- self.require_initialized();
-
- // Extract the fungible amount from the asset
- let deposit_amount = deposit_asset.inner[0];
-
- // ========================================================================
- // CONSTRAINT: Maximum deposit amount check
- // ========================================================================
- assert!(
- deposit_amount.as_u64() <= MAX_DEPOSIT_AMOUNT,
- "Deposit amount exceeds maximum allowed"
- );
-
- // ========================================================================
- // UPDATE BALANCE
- // ========================================================================
- // Create key from depositor's AccountId and asset faucet ID
- // This allows tracking balances per depositor per asset type
- let key = Word::from([
- depositor.prefix,
- depositor.suffix,
- deposit_asset.inner[3], // asset prefix (faucet)
- deposit_asset.inner[2], // asset suffix (faucet)
- ]);
-
- // Update balance: current + deposit_amount
- let current_balance: Felt = self.balances.get(&key);
- let new_balance = current_balance + deposit_amount;
- self.balances.set(key, new_balance);
-
- // ========================================================================
- // ADD ASSET TO VAULT
- // ========================================================================
- native_account::add_asset(deposit_asset);
-}
-```
-
-### Balance Key Design
-
-We construct a composite key for balance tracking:
-
-```rust
-let key = Word::from([
- depositor.prefix, // Who deposited
- depositor.suffix,
- deposit_asset.inner[3], // Which asset type (faucet ID prefix)
- deposit_asset.inner[2], // Which asset type (faucet ID suffix)
-]);
-```
-
-This design allows:
-
-- **Per-depositor tracking**: Each user has their own balance
-- **Per-asset tracking**: Different token types are tracked separately
-- **Unique keys**: The combination ensures no collisions
-
-## Step 2: Add the Withdraw Method Skeleton
-
-Now add a withdraw method skeleton. We'll complete it in Part 7 when we cover output notes.
-
-:::danger Critical Security Warning: Felt Arithmetic Underflow
-
-Miden uses **modular field arithmetic**. Subtracting a larger value from a smaller one does **NOT** cause an error - it **silently wraps** to a massive positive number!
-
-For example: `50 - 100` does NOT equal `-50`. Instead, it equals a number close to `2^64`.
-
-**You MUST validate before ANY subtraction:**
-
-```rust
-// WRONG - DANGEROUS! Silent underflow if balance < amount
-let new_balance = current_balance - withdraw_amount;
-
-// CORRECT - Always validate first
-assert!(
- current_balance.as_u64() >= withdraw_amount.as_u64(),
- "Withdrawal amount exceeds available balance"
-);
-let new_balance = current_balance - withdraw_amount;
-```
-
-This is not optional - it's a **security requirement** for any financial operation.
-:::
-
-Add this method to your Bank impl block:
-
-```rust title="contracts/bank-account/src/lib.rs"
-/// Withdraw assets from the bank.
-/// Creates a P2ID note to send assets back to the depositor.
-pub fn withdraw(
- &mut self,
- depositor: AccountId,
- withdraw_asset: Asset,
- serial_num: Word,
- tag: Felt,
- note_type: Felt,
-) {
- // ========================================================================
- // CONSTRAINT: Bank must be initialized
- // ========================================================================
- self.require_initialized();
-
- // Extract the fungible amount from the asset
- let withdraw_amount = withdraw_asset.inner[0];
-
- // Create key from depositor's AccountId and asset faucet ID
- let key = Word::from([
- depositor.prefix,
- depositor.suffix,
- withdraw_asset.inner[3],
- withdraw_asset.inner[2],
- ]);
-
- // ========================================================================
- // CRITICAL: Validate balance BEFORE subtraction
- // ========================================================================
- // Get current balance and validate sufficient funds exist.
- // This check is critical: Felt arithmetic is modular, so subtracting
- // more than the balance would silently wrap to a large positive number.
- let current_balance: Felt = self.balances.get(&key);
- assert!(
- current_balance.as_u64() >= withdraw_amount.as_u64(),
- "Withdrawal amount exceeds available balance"
- );
-
- // Now safe to subtract
- let new_balance = current_balance - withdraw_amount;
- self.balances.set(key, new_balance);
-
- // Create a P2ID note to send the requested asset back to the depositor
- // We'll implement create_p2id_note() in Part 7
- self.create_p2id_note(serial_num, &withdraw_asset, depositor, tag, note_type);
-}
-```
-
-For now, add a placeholder for `create_p2id_note()`:
-
-```rust title="contracts/bank-account/src/lib.rs"
-/// Create a P2ID note to send assets to a recipient.
-/// Full implementation in Part 7.
-fn create_p2id_note(
- &mut self,
- _serial_num: Word,
- _asset: &Asset,
- _recipient_id: AccountId,
- _tag: Felt,
- _note_type: Felt,
-) {
- // Placeholder - implemented in Part 7: Output Notes
- // For now, this will cause a compile error if actually called
- todo!("P2ID note creation - see Part 7")
-}
-```
-
-## Step 3: Build and Verify
-
-Build the contract:
-
-```bash title=">_ Terminal"
-cd contracts/bank-account
-miden build
-```
-
-## Try It: Verify Deposits Work
-
-Let's write a test to verify our deposit logic works correctly:
-
-```rust title="integration/tests/part3_deposit_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, StorageSlot, StorageSlotName};
-use miden_client::asset::{Asset, FungibleAsset};
-use miden_client::note::NoteAssets;
-use miden_client::transaction::{OutputNote, TransactionScript};
-use miden_client::{Felt, Word};
-use miden_testing::{Auth, MockChain};
-use std::{path::Path, sync::Arc};
-
-#[tokio::test]
-async fn test_deposit_updates_balance() -> anyhow::Result<()> {
- // =========================================================================
- // SETUP
- // =========================================================================
- let mut builder = MockChain::builder();
-
- // Create a faucet for test tokens
- let faucet = builder.add_existing_basic_faucet(Auth::BasicAuth, "TEST", 10_000_000, Some(10))?;
-
- // Create sender wallet with tokens
- let sender = builder.add_existing_wallet_with_assets(Auth::BasicAuth, [FungibleAsset::new(faucet.id(), 1000)?.into()])?;
-
- // Build contracts
- 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,
- )?);
-
- // Create the bank account with storage slots
- let initialized_slot =
- StorageSlotName::new("miden::component::miden_bank_account::initialized")
- .expect("Valid slot name");
- let balances_slot =
- StorageSlotName::new("miden::component::miden_bank_account::balances")
- .expect("Valid slot name");
-
- let bank_cfg = AccountCreationConfig {
- storage_slots: vec![
- StorageSlot::with_value(initialized_slot.clone(), Word::default()),
- StorageSlot::with_map(
- balances_slot.clone(),
- StorageMap::with_entries([]).expect("Empty storage map"),
- ),
- ],
- ..Default::default()
- };
-
- let mut bank_account =
- create_testing_account_from_package(bank_package.clone(), bank_cfg).await?;
-
- // Add to mock chain
- builder.add_account(bank_account.clone())?;
-
- // =========================================================================
- // STEP 2: Create deposit note before building the mock chain
- // =========================================================================
- let deposit_amount: u64 = 1000;
- 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()
- },
- )?;
-
- // Add note to builder before building
- builder.add_output_note(OutputNote::Full(deposit_note.clone()));
-
- let mut mock_chain = builder.build()?;
-
- // =========================================================================
- // STEP 1: Initialize the 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()?;
-
- // Verify initialization
- let initialized = bank_account.storage().get_item(&initialized_slot)?;
- assert_eq!(
- initialized[0].as_int(),
- 1,
- "Bank should be initialized"
- );
- println!("Bank initialized successfully!");
-
- // =========================================================================
- // STEP 2: Execute deposit
- // =========================================================================
-
- // Execute deposit transaction
- let tx_context = mock_chain
- .build_tx_context(bank_account.id(), &[deposit_note.id()], &[])?
- .build()?;
-
- let executed_transaction = tx_context.execute().await?;
- bank_account.apply_delta(&executed_transaction.account_delta())?;
- mock_chain.add_pending_executed_transaction(&executed_transaction)?;
- mock_chain.prove_next_block()?;
-
- println!("Deposit transaction executed!");
-
- // =========================================================================
- // VERIFY: Check balance was updated
- // =========================================================================
- let depositor_key = Word::from([
- sender.id().prefix().as_felt(),
- sender.id().suffix(),
- faucet.id().prefix().as_felt(),
- faucet.id().suffix(),
- ]);
-
- let balance = bank_account.storage().get_map_item(&balances_slot, depositor_key)?;
-
- // Balance is stored as a single Felt in the last position of the Word
- let balance_value = balance[3].as_int();
-
- println!("Depositor balance: {}", balance_value);
- assert_eq!(
- balance_value,
- deposit_amount,
- "Balance should equal deposited amount"
- );
-
- println!("\nPart 3 deposit test passed!");
-
- Ok(())
-}
-```
-
-:::note Test Dependencies
-This test requires:
-- `deposit-note` contract (Part 4)
-- `init-tx-script` contract (Part 6)
-
-If you haven't created these yet, you can run this test after completing Parts 4 and 6, or create placeholder contracts. For now, let's verify the bank-account compiles correctly.
-:::
-
-Build verification:
-
-```bash title=">_ Terminal"
-cd contracts/bank-account
-miden build
-```
-
-If you have the note scripts ready, run the full test from the project root:
-
-```bash title=">_ Terminal"
-cargo test --package integration test_deposit_updates_balance -- --nocapture
-```
-
-
-Expected output
-
-```text
- Compiling integration v0.1.0 (/path/to/miden-bank/integration)
- Finished `test` profile [unoptimized + debuginfo] target(s)
- Running tests/part3_deposit_test.rs
-
-running 1 test
-Bank initialized successfully!
-Deposit transaction executed!
-Depositor balance: 1000
-
-Part 3 deposit test passed!
-test test_deposit_updates_balance ... ok
-
-test result: ok. 1 passed; 0 failed; 0 ignored
-```
-
-
-
-## Asset Flow Summary
-
-```text
-DEPOSIT FLOW:
-┌───────────┐ deposit_note ┌────────────┐
-│ Depositor │ ──────────────────▶ Bank Vault │
-│ Wallet │ (with asset) │ + Balance │
-└───────────┘ └────────────┘
-
-WITHDRAW FLOW:
-┌────────────┐ P2ID note ┌───────────┐
-│ Bank Vault │ ──────────────────▶ Depositor│
-│ - Balance │ (with asset) │ Wallet │
-└────────────┘ └───────────┘
-```
-
-## Complete Code for This Part
-
-Here's the full `lib.rs` after Part 3:
-
-
-Click to expand full code
-
-```rust title="contracts/bank-account/src/lib.rs"
-#![no_std]
-#![feature(alloc_error_handler)]
-
-#[macro_use]
-extern crate alloc;
-
-use miden::*;
-
-/// Maximum allowed deposit amount per transaction.
-const MAX_DEPOSIT_AMOUNT: u64 = 1_000_000;
-
-/// Bank account component that tracks depositor balances.
-#[component]
-struct Bank {
- #[storage(description = "initialized")]
- initialized: Value,
-
- #[storage(description = "balances")]
- balances: StorageMap,
-}
-
-#[component]
-impl Bank {
- /// Initialize the bank account, enabling deposits.
- pub fn initialize(&mut self) {
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 0,
- "Bank already initialized"
- );
-
- let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
- self.initialized.write(initialized_word);
- }
-
- /// Get the balance for a depositor.
- pub fn get_balance(&self, depositor: AccountId) -> Felt {
- let key = Word::from([depositor.prefix, depositor.suffix, felt!(0), felt!(0)]);
- self.balances.get(&key)
- }
-
- /// Check that the bank is initialized.
- fn require_initialized(&self) {
- let current: Word = self.initialized.read();
- assert!(
- current[0].as_u64() == 1,
- "Bank not initialized - deposits not enabled"
- );
- }
-
- /// Deposit assets into the bank.
- pub fn deposit(&mut self, depositor: AccountId, deposit_asset: Asset) {
- self.require_initialized();
-
- let deposit_amount = deposit_asset.inner[0];
-
- assert!(
- deposit_amount.as_u64() <= MAX_DEPOSIT_AMOUNT,
- "Deposit amount exceeds maximum allowed"
- );
-
- let key = Word::from([
- depositor.prefix,
- depositor.suffix,
- deposit_asset.inner[3],
- deposit_asset.inner[2],
- ]);
-
- let current_balance: Felt = self.balances.get(&key);
- let new_balance = current_balance + deposit_amount;
- self.balances.set(key, new_balance);
-
- native_account::add_asset(deposit_asset);
- }
-
- /// Withdraw assets from the bank.
- pub fn withdraw(
- &mut self,
- depositor: AccountId,
- withdraw_asset: Asset,
- serial_num: Word,
- tag: Felt,
- note_type: Felt,
- ) {
- self.require_initialized();
-
- let withdraw_amount = withdraw_asset.inner[0];
-
- let key = Word::from([
- depositor.prefix,
- depositor.suffix,
- withdraw_asset.inner[3],
- withdraw_asset.inner[2],
- ]);
-
- // CRITICAL: Validate balance BEFORE subtraction
- let current_balance: Felt = self.balances.get(&key);
- assert!(
- current_balance.as_u64() >= withdraw_amount.as_u64(),
- "Withdrawal amount exceeds available balance"
- );
-
- let new_balance = current_balance - withdraw_amount;
- self.balances.set(key, new_balance);
-
- self.create_p2id_note(serial_num, &withdraw_asset, depositor, tag, note_type);
- }
-
- /// Create a P2ID note - placeholder for Part 7.
- fn create_p2id_note(
- &mut self,
- _serial_num: Word,
- _asset: &Asset,
- _recipient_id: AccountId,
- _tag: Felt,
- _note_type: Felt,
- ) {
- todo!("P2ID note creation - see Part 7")
- }
-}
-```
-
-
-
-## Key Takeaways
-
-1. **Asset layout**: `[amount, 0, faucet_suffix, faucet_prefix]`
-2. **`native_account::add_asset()`** adds assets to the vault
-3. **`native_account::remove_asset()`** removes assets from the vault (Part 7)
-4. **Balance tracking** is application-level logic using `StorageMap`
-5. **Composite keys** allow per-user, per-asset balance tracking
-6. **CRITICAL: Always validate before subtraction** - Felt arithmetic wraps silently!
-
-:::tip View Complete Source
-See the complete deposit and withdraw implementations in the [miden-bank repository](https://github.com/keinberger/miden-bank/blob/main/contracts/bank-account/src/lib.rs).
-:::
-
-## Next Steps
-
-Now that you understand asset management, let's learn how to trigger these operations with [Part 4: Note Scripts](./note-scripts).
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/04-note-scripts.md b/docs/builder/tutorials/rust-compiler/miden-bank/04-note-scripts.md
deleted file mode 100644
index b2cd4d24..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/04-note-scripts.md
+++ /dev/null
@@ -1,534 +0,0 @@
----
-sidebar_position: 4
-title: "Part 4: Note Scripts"
-description: "Learn how to write note scripts that execute when notes are consumed, using active_note APIs to access sender, assets, and inputs."
----
-
-# Part 4: Note Scripts
-
-In this section, you'll learn how to write note scripts - code that executes when a note is consumed by an account. We'll create the deposit note that lets users deposit tokens into the bank.
-
-## What You'll Build in This Part
-
-By the end of this section, you will have:
-
-- Created the `deposit-note` contract
-- Understood the `#[note]` struct+impl pattern and `#[note_script]` method attribute
-- Used `active_note` APIs to access sender and assets
-- Built the note script and its dependencies
-- **Verified it works** with a complete deposit flow test
-
-## Building on Part 3
-
-In Part 3, we completed the bank's deposit method. Now we need a way to trigger it:
-
-```text
-Part 3: Part 4:
-┌──────────────────┐ ┌──────────────────┐
-│ Bank (complete) │ │ Bank (complete) │
-│ ─────────────────│ │ ─────────────────│
-│ + deposit() │ │ + deposit() │
-│ + withdraw() │ │ + withdraw() │
-└──────────────────┘ └──────────────────┘
- ▲
- │ calls
- ┌────────────────────┐
- │ deposit-note │ ◄── NEW
- │ (note script) │
- └────────────────────┘
-```
-
-## Note Scripts vs Account Components
-
-| Feature | Account Component | Note Script |
-|---------|------------------|-------------|
-| Purpose | Persistent account logic | One-time execution when consumed |
-| Storage | Has persistent storage | No storage (reads from note data) |
-| Attribute | `#[component]` | `#[note]` struct + `#[note_script]` method |
-| Entry point | Methods on struct | `fn run(self, _arg: Word)` |
-| Invocation | Called by other contracts | Executes when note is consumed |
-
-Note scripts are like "messages" that carry code along with data and assets.
-
-## Step 1: Create the Deposit Note Project
-
-First, create the deposit-note contract. If you used `miden new`, you may have an `increment-note` folder - rename or replace it:
-
-```bash title=">_ Terminal"
-# Remove or rename the example
-rm -rf contracts/increment-note
-# Or: mv contracts/increment-note contracts/increment-note-backup
-
-# Create the deposit-note directory
-mkdir -p contracts/deposit-note/src
-```
-
-## Step 2: Configure Cargo.toml
-
-Create the `Cargo.toml` for the deposit note:
-
-```toml title="contracts/deposit-note/Cargo.toml"
-[package]
-name = "deposit-note"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-miden = { version = "0.10" }
-
-[package.metadata.component]
-package = "miden:deposit-note"
-
-[package.metadata.miden]
-project-kind = "note-script"
-
-# Dependencies on account components
-[package.metadata.miden.dependencies]
-"miden:bank-account" = { path = "../bank-account" }
-
-[package.metadata.component.target.dependencies]
-"miden:bank-account" = { path = "../bank-account/target/generated-wit/" }
-```
-
-Key configuration:
-- `project-kind = "note-script"` - Marks this as a note script
-- Dependencies sections declare which accounts it can interact with
-
-## Step 3: Implement the Deposit Note
-
-Create the note script implementation:
-
-```rust title="contracts/deposit-note/src/lib.rs"
-#![no_std]
-#![feature(alloc_error_handler)]
-
-use miden::*;
-
-// Import the bank account's generated bindings
-use crate::bindings::miden::bank_account::bank_account;
-
-/// Deposit Note Script
-///
-/// When consumed by the Bank account, this note transfers all its assets
-/// to the bank and credits the depositor (note sender) with the deposited amount.
-#[note]
-struct DepositNote;
-
-#[note]
-impl DepositNote {
- #[note_script]
- fn run(self, _arg: Word) {
- // The depositor is whoever created/sent this note
- let depositor = active_note::get_sender();
-
- // Get all assets attached to this note
- let assets = active_note::get_assets();
-
- // Deposit each asset into the bank
- for asset in assets {
- bank_account::deposit(depositor, asset);
- }
- }
-}
-```
-
-### The #[note] and #[note_script] Attributes
-
-The `#[note]` attribute is applied to both a unit struct and its `impl` block to define a note script. Within the `impl` block, the `#[note_script]` attribute marks the entry point method. The function signature is always:
-
-```rust
-fn run(self, _arg: Word)
-```
-
-The method takes `self` as its first parameter. The `_arg` parameter can pass additional data, but we don't use it in the deposit note.
-
-## Note Context APIs
-
-The `active_note` module provides APIs to access note data during execution:
-
-### get_sender() - Who Created the Note
-
-```rust
-let depositor = active_note::get_sender();
-```
-
-Returns the `AccountId` of the account that created/sent the note. In our bank:
-- The sender is the depositor
-- Their ID is used to credit their balance
-
-### get_assets() - Attached Assets
-
-```rust
-let assets = active_note::get_assets();
-for asset in assets {
- // Process each asset
-}
-```
-
-Returns an iterator over all assets attached to the note.
-
-### get_inputs() - Note Parameters
-
-```rust
-let inputs = active_note::get_inputs();
-let first_input = inputs[0];
-```
-
-Returns a vector of `Felt` values passed when the note was created. We'll use inputs in the withdraw request note (Part 7).
-
-## Step 4: Update the Workspace
-
-Update the root `Cargo.toml` to include the new contract:
-
-```toml title="Cargo.toml" {5}
-[workspace]
-members = [
- "integration"
-]
-exclude = [
- "contracts/",
-]
-resolver = "2"
-
-[workspace.package]
-edition = "2021"
-
-[workspace.dependencies]
-```
-
-## Step 5: Build the Note Script
-
-:::info Build Order Matters
-Build account components **first** before building note scripts that depend on them. The note script needs the generated WIT files from the account.
-:::
-
-```bash title=">_ Terminal"
-# First, ensure bank-account is built (generates WIT files)
-cd contracts/bank-account
-miden build
-
-# Now build the deposit note
-cd ../deposit-note
-miden build
-```
-
-
-Expected output
-
-```text
- Compiling deposit-note v0.1.0
- Finished `release` profile [optimized] target(s)
-Creating Miden package /path/to/miden-bank/target/miden/release/deposit_note.masp
-```
-
-
-
-## Execution Flow Diagram
-
-```text
-1. User creates deposit note with 100 tokens attached
- ┌───────────────────────────────────────┐
- │ Note: deposit-note │
- │ Sender: User's AccountId │
- │ Assets: [100 tokens] │
- └───────────────────────────────────────┘
-
-2. Bank account consumes the note
- ┌───────────────────────────────────────┐
- │ Bank receives assets into vault │
- │ Note script executes... │
- └───────────────────────────────────────┘
-
-3. Note script runs
- depositor = get_sender() → User's AccountId
- assets = get_assets() → [100 tokens]
- bank_account::deposit(depositor, 100 tokens)
-
-4. Bank's deposit() method executes
- - Validates initialization and amount
- - Updates balance: balances[User] += 100
- - Adds asset to vault
-```
-
-## Try It: Verify Deposits Work
-
-Now let's write a test to verify the complete deposit flow. This test:
-1. Initializes the bank
-2. Creates a deposit note with tokens
-3. Has the bank consume the note
-4. Verifies the balance was updated
-
-```rust title="integration/tests/part4_deposit_note_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, StorageSlot, StorageSlotName};
-use miden_client::note::NoteAssets;
-use miden_client::transaction::{OutputNote, TransactionScript};
-use miden_client::asset::{Asset, FungibleAsset};
-use miden_client::{Felt, Word};
-use miden_testing::{Auth, MockChain};
-use std::{path::Path, sync::Arc};
-
-#[tokio::test]
-async fn test_deposit_note_credits_depositor() -> anyhow::Result<()> {
- // =========================================================================
- // SETUP: Build contracts and create mock chain
- // =========================================================================
- let mut builder = MockChain::builder();
-
- // Create a faucet for test tokens
- let faucet = builder.add_existing_basic_faucet(Auth::BasicAuth, "TEST", 10_000_000, Some(10))?;
-
- // Create sender (depositor) wallet
- let sender = builder.add_existing_wallet_with_assets(Auth::BasicAuth, [FungibleAsset::new(faucet.id(), 1000)?.into()])?;
-
- // Build all contracts
- 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,
- )?);
-
- // Create bank account
- let initialized_slot =
- StorageSlotName::new("miden::component::miden_bank_account::initialized")
- .expect("Valid slot name");
- let balances_slot =
- StorageSlotName::new("miden::component::miden_bank_account::balances")
- .expect("Valid slot name");
-
- let bank_cfg = AccountCreationConfig {
- storage_slots: vec![
- StorageSlot::with_value(initialized_slot, Word::default()),
- StorageSlot::with_map(
- balances_slot.clone(),
- StorageMap::with_entries([]).expect("Empty storage map"),
- ),
- ],
- ..Default::default()
- };
-
- let mut bank_account =
- create_testing_account_from_package(bank_package.clone(), bank_cfg).await?;
-
- builder.add_account(bank_account.clone())?;
-
- // Create the deposit note and add it before building the chain
- let deposit_amount: u64 = 1000;
- 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(), // Sender is the depositor
- NoteCreationConfig {
- assets: note_assets,
- ..Default::default()
- },
- )?;
-
- builder.add_output_note(OutputNote::Full(deposit_note.clone()));
- let mut mock_chain = builder.build()?;
-
- // =========================================================================
- // STEP 1: Initialize the 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!("Step 1: Bank initialized");
-
- // =========================================================================
- // STEP 2: Execute deposit
- // =========================================================================
- let tx_context = mock_chain
- .build_tx_context(bank_account.id(), &[deposit_note.id()], &[])?
- .build()?;
-
- let executed_transaction = tx_context.execute().await?;
- bank_account.apply_delta(&executed_transaction.account_delta())?;
- mock_chain.add_pending_executed_transaction(&executed_transaction)?;
- mock_chain.prove_next_block()?;
-
- println!("Step 2: Deposit note consumed");
-
- // =========================================================================
- // VERIFY: Balance was updated
- // =========================================================================
- let depositor_key = Word::from([
- sender.id().prefix().as_felt(),
- sender.id().suffix(),
- faucet.id().prefix().as_felt(),
- faucet.id().suffix(),
- ]);
-
- let balance = bank_account.storage().get_map_item(&balances_slot, depositor_key)?;
- let balance_value = balance[3].as_int();
-
- println!("Step 3: Verified balance = {}", balance_value);
-
- assert_eq!(
- balance_value,
- deposit_amount,
- "Balance should equal deposited amount"
- );
-
- println!("\nPart 4 deposit note test passed!");
-
- Ok(())
-}
-```
-
-:::note Dependencies
-This test requires the `init-tx-script` contract which we'll create in Part 6. You can either:
-1. Skip ahead to create a minimal init-tx-script (see Part 6)
-2. Run this test after completing Part 6
-
-For now, verify that your deposit-note builds successfully.
-:::
-
-Run the test from the project root (after creating init-tx-script in Part 6):
-
-```bash title=">_ Terminal"
-cargo test --package integration test_deposit_note_credits_depositor -- --nocapture
-```
-
-
-Expected output
-
-```text
- Compiling integration v0.1.0 (/path/to/miden-bank/integration)
- Finished `test` profile [unoptimized + debuginfo] target(s)
- Running tests/part4_deposit_note_test.rs
-
-running 1 test
-Step 1: Bank initialized
-Step 2: Deposit note consumed
-Step 3: Verified balance = 1000
-
-Part 4 deposit note test passed!
-test test_deposit_note_credits_depositor ... ok
-
-test result: ok. 1 passed; 0 failed; 0 ignored
-```
-
-
-
-## Preview: Withdraw Request Note
-
-For withdrawals, we'll use note inputs to pass parameters. Here's a preview of the withdraw request note (implemented in Part 7):
-
-```rust title="contracts/withdraw-request-note/src/lib.rs (preview)"
-/// Withdraw Request Note Script
-///
-/// # Note Inputs (10 Felts)
-/// [0-3]: withdraw asset (amount, 0, faucet_suffix, faucet_prefix)
-/// [4-7]: serial_num (random/unique per note)
-/// [8]: tag (P2ID note tag for routing)
-/// [9]: note_type (1 = Public, 2 = Private)
-#[note]
-struct WithdrawRequestNote;
-
-#[note]
-impl WithdrawRequestNote {
- #[note_script]
- fn run(self, _arg: Word) {
- let depositor = active_note::get_sender();
- let inputs = active_note::get_inputs();
-
- // Parse parameters from inputs
- let withdraw_asset = Asset::new(Word::from([
- inputs[0], inputs[1], inputs[2], inputs[3]
- ]));
-
- let serial_num = Word::from([
- inputs[4], inputs[5], inputs[6], inputs[7]
- ]);
-
- let tag = inputs[8];
- let note_type = inputs[9];
-
- bank_account::withdraw(depositor, withdraw_asset, serial_num, tag, note_type);
- }
-}
-```
-
-:::warning Stack Limits
-Note inputs are limited. Keep your input layout compact. See [Common Pitfalls](../pitfalls) for stack-related constraints.
-:::
-
-## Complete Code for This Part
-
-
-Click to expand deposit-note/src/lib.rs
-
-```rust title="contracts/deposit-note/src/lib.rs"
-#![no_std]
-#![feature(alloc_error_handler)]
-
-use miden::*;
-
-use crate::bindings::miden::bank_account::bank_account;
-
-/// Deposit Note Script
-#[note]
-struct DepositNote;
-
-#[note]
-impl DepositNote {
- #[note_script]
- fn run(self, _arg: Word) {
- let depositor = active_note::get_sender();
- let assets = active_note::get_assets();
-
- for asset in assets {
- bank_account::deposit(depositor, asset);
- }
- }
-}
-```
-
-
-
-## Key Takeaways
-
-1. **`#[note]`** marks the struct and impl block, with **`#[note_script]`** on the entry point method `fn run(self, _arg: Word)`
-2. **`active_note::get_sender()`** returns who created the note
-3. **`active_note::get_assets()`** returns assets attached to the note
-4. **`active_note::get_inputs()`** returns parameterized data
-5. **Note scripts execute once** when consumed - no persistent state
-6. **Build order matters** - account components first, then note scripts
-
-:::tip View Complete Source
-See the complete note script implementations:
-- [Deposit Note](https://github.com/keinberger/miden-bank/blob/main/contracts/deposit-note/src/lib.rs)
-- [Withdraw Request Note](https://github.com/keinberger/miden-bank/blob/main/contracts/withdraw-request-note/src/lib.rs)
-:::
-
-## Next Steps
-
-Now that you understand note scripts, let's learn how they call account methods in [Part 5: Cross-Component Calls](./cross-component-calls).
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/05-cross-component-calls.md b/docs/builder/tutorials/rust-compiler/miden-bank/05-cross-component-calls.md
deleted file mode 100644
index bb7a1d23..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/05-cross-component-calls.md
+++ /dev/null
@@ -1,290 +0,0 @@
----
-sidebar_position: 5
-title: "Part 5: Cross-Component Calls"
-description: "Learn how note scripts and transaction scripts call account component methods using generated bindings and proper dependency configuration."
----
-
-# Part 5: Cross-Component Calls
-
-In this section, you'll learn how note scripts call methods on account components. We'll explore the generated bindings system and the dependency configuration that makes the deposit note work.
-
-## What You'll Learn in This Part
-
-By the end of this section, you will have:
-
-- Understood how bindings are generated and imported
-- Learned the dependency configuration in `Cargo.toml`
-- Explored the WIT interface files
-- **Verified cross-component calls work** via the deposit flow
-
-## Building on Part 4
-
-In Part 4, you wrote `bank_account::deposit(depositor, asset)` in the deposit note. But how does that call actually work? This part explains the binding system:
-
-```text
-┌────────────────────────────────────────────────────────────┐
-│ How Bindings Work │
-├────────────────────────────────────────────────────────────┤
-│ │
-│ bank-account/ │
-│ └── src/lib.rs miden build │
-│ pub fn deposit() ─────────────▶ generated-wit/ │
-│ pub fn withdraw() miden_bank-account.wit
-│ │
-│ ┌───────────────────────────┐ │
-│ ▼ │ │
-│ deposit-note/ │ │
-│ └── src/lib.rs │ │
-│ use crate::bindings::miden::bank_account::bank_account;
-│ bank_account::deposit(...) ◄───── calls via binding │
-│ │
-└────────────────────────────────────────────────────────────┘
-```
-
-## The Bindings System
-
-When you build an account component with `miden build`, it generates:
-
-1. **MASM code** - The compiled contract logic
-2. **WIT files** - WebAssembly Interface Type definitions
-
-Other contracts (note scripts, transaction scripts) import these WIT files to call the account's methods.
-
-```text
-Build Flow:
-┌──────────────────┐ miden build ┌─────────────────────────────────┐
-│ bank-account/ │ ─────────────────▶│ target/generated-wit/ │
-│ src/lib.rs │ │ miden_bank-account.wit │
-│ │ │ miden_bank-account_world.wit │
-└──────────────────┘ └─────────────────────────────────┘
- │
- ▼
- ┌─────────────────────────────────┐
- │ deposit-note/ │
- │ imports generated bindings │
- └─────────────────────────────────┘
-```
-
-## Importing Bindings
-
-In your note script, import the generated bindings:
-
-```rust title="contracts/deposit-note/src/lib.rs"
-// Import the bank account's generated bindings
-use crate::bindings::miden::bank_account::bank_account;
-```
-
-The import path follows this pattern:
-```
-crate::bindings::{package-prefix}::{component-name}::{interface-name}
-```
-
-For our bank:
-- `miden` - The package prefix from `[package.metadata.component]`
-- `bank_account` - The component name (derived from package name with underscores)
-- `bank_account` - The interface name (same as component)
-
-## Calling Account Methods
-
-Once imported, call the account methods directly:
-
-```rust title="contracts/deposit-note/src/lib.rs"
-#[note]
-struct DepositNote;
-
-#[note]
-impl DepositNote {
- #[note_script]
- fn run(self, _arg: Word) {
- let depositor = active_note::get_sender();
- let assets = active_note::get_assets();
-
- for asset in assets {
- // Call the bank account's deposit method
- bank_account::deposit(depositor, asset);
- }
- }
-}
-```
-
-The binding automatically handles:
-- Marshalling arguments across the component boundary
-- Invoking the correct MASM procedures
-- Returning results back to the caller
-
-## Configuring Dependencies
-
-Your `Cargo.toml` needs **two** dependency sections:
-
-```toml title="contracts/deposit-note/Cargo.toml"
-[package.metadata.miden.dependencies]
-"miden:bank-account" = { path = "../bank-account" }
-
-[package.metadata.component.target.dependencies]
-"miden:bank-account" = { path = "../bank-account/target/generated-wit/" }
-```
-
-### miden.dependencies
-
-```toml
-[package.metadata.miden.dependencies]
-"miden:bank-account" = { path = "../bank-account" }
-```
-
-This tells `cargo-miden` where to find the source package. Used during the build process to:
-- Verify interface compatibility
-- Link the compiled MASM code
-
-### component.target.dependencies
-
-```toml
-[package.metadata.component.target.dependencies]
-"miden:bank-account" = { path = "../bank-account/target/generated-wit/" }
-```
-
-This tells the Rust compiler where to find the WIT interface files. The path points to the `generated-wit/` directory created when you built the account component.
-
-:::warning Both Sections Required
-If either section is missing, your build will fail with linking or interface errors.
-:::
-
-## Build Order
-
-Components must be built in dependency order:
-
-```bash title=">_ Terminal"
-# 1. Build the account component first
-cd contracts/bank-account
-miden build
-
-# 2. Then build note scripts that depend on it
-cd ../deposit-note
-miden build
-```
-
-If you build out of order, you'll see errors about missing WIT files.
-
-## What Methods Are Available?
-
-Only **public methods** (`pub fn`) on the `#[component] impl` block are available through bindings:
-
-```rust title="contracts/bank-account/src/lib.rs"
-#[component]
-impl Bank {
- // PUBLIC: Available through bindings
- pub fn deposit(&mut self, depositor: AccountId, deposit_asset: Asset) { ... }
- pub fn withdraw(&mut self, /* ... */) { ... }
- pub fn get_balance(&self, depositor: AccountId) -> Felt { ... }
- pub fn initialize(&mut self) { ... }
-
- // PRIVATE: NOT available through bindings
- fn require_initialized(&self) { ... }
- fn create_p2id_note(&mut self, /* ... */) { ... }
-}
-```
-
-## Understanding the Generated WIT
-
-The WIT files describe the interface. Here's a simplified example:
-
-```wit title="target/generated-wit/miden_bank-account.wit"
-interface bank-account {
- use miden:types/types.{account-id, asset, felt, word};
-
- initialize: func();
- deposit: func(depositor: account-id, deposit-asset: asset);
- withdraw: func(depositor: account-id, withdraw-asset: asset, ...);
- get-balance: func(depositor: account-id) -> felt;
-}
-```
-
-This WIT is used to generate the Rust bindings that appear in `crate::bindings`.
-
-## Transaction Script Bindings (Preview)
-
-Transaction scripts use a slightly different import pattern:
-
-```rust title="contracts/init-tx-script/src/lib.rs"
-use crate::bindings::Account;
-
-#[tx_script]
-fn run(_arg: Word, account: &mut Account) {
- // The account parameter IS the bound component
- account.initialize();
-}
-```
-
-The `Account` binding in transaction scripts wraps the entire component, giving direct method access through the `account` parameter. We'll implement this in Part 6.
-
-## Try It: Verify Bindings Work
-
-If you completed Part 4 and built both contracts, the bindings are already working! Let's verify:
-
-```bash title=">_ Terminal"
-# Check that the WIT files were generated
-ls contracts/bank-account/target/generated-wit/
-```
-
-
-Expected output
-
-```text
-miden_bank-account.wit
-miden_bank-account_world.wit
-```
-
-
-
-These files enable the deposit note to call `bank_account::deposit()`.
-
-## Common Issues
-
-### "Cannot find module" Error
-
-```
-error: cannot find module `bindings`
-```
-
-**Cause**: The account component wasn't built, or the WIT path is wrong.
-
-**Solution**:
-1. Build the account: `cd contracts/bank-account && miden build`
-2. Verify the WIT path in `Cargo.toml` points to `target/generated-wit/`
-
-### "Method not found" Error
-
-```
-error: no method named `deposit` found
-```
-
-**Cause**: The method isn't marked `pub` in the account component.
-
-**Solution**: Ensure the method has `pub fn` visibility.
-
-### "Dependency not found" Error
-
-```
-error: dependency 'miden:bank-account' not found
-```
-
-**Cause**: One of the dependency sections is missing or has the wrong path.
-
-**Solution**: Ensure both `[package.metadata.miden.dependencies]` and `[package.metadata.component.target.dependencies]` are present with correct paths.
-
-## Key Takeaways
-
-1. **Build accounts first** - They generate WIT files that note scripts need
-2. **Two dependency sections** - Both `miden.dependencies` and `component.target.dependencies` are required
-3. **Import path pattern** - `crate::bindings::{package}::{component}::{interface}`
-4. **Only public methods** - Private methods aren't exposed in bindings
-5. **Transaction scripts differ** - They receive the account as a parameter (Part 6)
-
-:::tip View Complete Source
-See the complete Cargo.toml configurations:
-- [Deposit Note Cargo.toml](https://github.com/keinberger/miden-bank/blob/main/contracts/deposit-note/Cargo.toml)
-- [Withdraw Request Note Cargo.toml](https://github.com/keinberger/miden-bank/blob/main/contracts/withdraw-request-note/Cargo.toml)
-:::
-
-## Next Steps
-
-Now that you understand cross-component calls, let's create the transaction script that initializes the bank in [Part 6: Transaction Scripts](./transaction-scripts).
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/06-transaction-scripts.md b/docs/builder/tutorials/rust-compiler/miden-bank/06-transaction-scripts.md
deleted file mode 100644
index 6254097c..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/06-transaction-scripts.md
+++ /dev/null
@@ -1,488 +0,0 @@
----
-sidebar_position: 6
-title: "Part 6: Transaction Scripts"
-description: "Learn how to write transaction scripts for account initialization and owner-controlled operations using the #[tx_script] attribute."
----
-
-# Part 6: Transaction Scripts
-
-In this section, you'll learn how to write transaction scripts - code that the account owner explicitly executes. We'll implement an initialization script that enables the bank to accept deposits.
-
-## What You'll Build in This Part
-
-By the end of this section, you will have:
-
-- Created the `init-tx-script` transaction script project
-- Understood the `#[tx_script]` attribute and function signature
-- Learned the difference between transaction scripts and note scripts
-- **Verified initialization works** via a MockChain test
-
-## Building on Part 5
-
-In Parts 4-5, you created note scripts that execute when notes are consumed. Now you'll create a transaction script - code the account owner explicitly runs:
-
-```text
-┌────────────────────────────────────────────────────────────────┐
-│ Script Types Comparison │
-├────────────────────────────────────────────────────────────────┤
-│ │
-│ Note Scripts (Parts 4-5) Transaction Scripts (Part 6)│
-│ ───────────────────────── ────────────────────────────│
-│ • Triggered by note consumption • Explicitly called by owner│
-│ • Import bindings via modules • Receive account parameter │
-│ • Process incoming assets • Setup, admin operations │
-│ │
-│ deposit-note/ init-tx-script/ │
-│ └── calls bank_account::deposit() └── calls account.initialize()
-│ │
-└────────────────────────────────────────────────────────────────┘
-```
-
-## Transaction Scripts vs Note Scripts
-
-| Aspect | Transaction Script | Note Script |
-|--------|-------------------|-------------|
-| Initiation | Explicitly called by account owner | Triggered when note is consumed |
-| Access | Direct account method access | Must call through bindings |
-| Use case | Setup, owner operations | Receiving messages/assets |
-| Parameter | `account: &mut Account` | Note context via `active_note::` |
-
-**Use transaction scripts for:**
-- One-time initialization
-- Admin/owner operations
-- Operations that don't involve receiving notes
-
-**Use note scripts for:**
-- Receiving assets from other accounts
-- Processing requests from other accounts
-- Multi-party interactions
-
-## Step 1: Create the Transaction Script Project
-
-Create a new directory for the transaction script:
-
-```bash title=">_ Terminal"
-mkdir -p contracts/init-tx-script/src
-```
-
-## Step 2: Configure Cargo.toml
-
-Create the Cargo.toml with transaction script configuration:
-
-```toml title="contracts/init-tx-script/Cargo.toml"
-[package]
-name = "init-tx-script"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-miden = { version = "0.10" }
-
-[package.metadata.component]
-package = "miden:init-tx-script"
-
-[package.metadata.miden]
-project-kind = "transaction-script"
-
-[package.metadata.miden.dependencies]
-"miden:bank-account" = { path = "../bank-account" }
-
-[package.metadata.component.target.dependencies]
-"miden:bank-account" = { path = "../bank-account/target/generated-wit/" }
-```
-
-Key configuration:
-- `project-kind = "transaction-script"` - Marks this as a transaction script (not "account" or "note")
-- Dependencies reference the account component (same pattern as note scripts)
-
-## Step 3: Add to Workspace
-
-Update your root `Cargo.toml` to include the new project:
-
-```toml title="Cargo.toml"
-[workspace]
-members = [
- "integration"
-]
-exclude = [
- "contracts/",
-]
-resolver = "2"
-
-[workspace.package]
-edition = "2021"
-
-[workspace.dependencies]
-```
-
-## Step 4: Implement the Transaction Script
-
-Create the initialization script:
-
-```rust title="contracts/init-tx-script/src/lib.rs"
-// Do not link against libstd (i.e. anything defined in `std::`)
-#![no_std]
-#![feature(alloc_error_handler)]
-
-use miden::*;
-
-// Import the Account binding which wraps the bank-account component methods
-use crate::bindings::Account;
-
-/// Initialize Transaction Script
-///
-/// This transaction script initializes the bank account, enabling deposits.
-/// It must be executed by the bank account owner before any deposits can be made.
-///
-/// # Flow
-/// 1. Transaction is created with this script attached
-/// 2. Script executes in the context of the bank account
-/// 3. Calls `account.initialize()` to enable deposits
-/// 4. Bank account is now "deployed" and visible on chain
-#[tx_script]
-fn run(_arg: Word, account: &mut Account) {
- account.initialize();
-}
-```
-
-## The #[tx_script] Attribute
-
-The `#[tx_script]` attribute marks the entry point for a transaction script:
-
-```rust
-#[tx_script]
-fn run(_arg: Word, account: &mut Account) {
- account.initialize();
-}
-```
-
-### Function Signature
-
-| Parameter | Type | Description |
-|-----------|------|-------------|
-| `_arg` | `Word` | Optional argument passed when executing |
-| `account` | `&mut Account` | Mutable reference to the account component |
-
-The `Account` type is generated from your component's bindings and provides access to all public methods.
-
-## The Account Binding
-
-Unlike note scripts that import bindings like `bank_account::deposit()`, transaction scripts receive the account as a parameter:
-
-```rust
-// Note script style (indirect):
-use crate::bindings::miden::bank_account::bank_account;
-bank_account::deposit(depositor, asset);
-
-// Transaction script style (direct):
-use crate::bindings::Account;
-fn run(_arg: Word, account: &mut Account) {
- account.initialize(); // Direct method call
-}
-```
-
-The `Account` wrapper provides:
-- Direct method access without module prefixes
-- Proper mutable/immutable borrowing
-- Automatic context binding
-
-## Step 5: Build the Transaction Script
-
-Build in dependency order:
-
-```bash title=">_ Terminal"
-# First, ensure the account component is built (generates WIT files)
-cd contracts/bank-account
-miden build
-
-# Then build the transaction script
-cd ../init-tx-script
-miden build
-```
-
-
-Expected output
-
-```text
- Compiling init-tx-script v0.1.0
- Finished `release` profile [optimized] target(s)
-Creating Miden package /path/to/miden-bank/target/miden/release/init_tx_script.masp
-```
-
-
-
-## Account Deployment Pattern
-
-In Miden, accounts are only visible on-chain after their first state change. Transaction scripts are commonly used for this "deployment":
-
-```text
-Execution Flow:
-
-1. Account owner creates transaction with init-tx-script
- ┌───────────────────────────────────────┐
- │ Transaction │
- │ Account: Bank's AccountId │
- │ Script: init-tx-script │
- └───────────────────────────────────────┘
-
-2. Transaction executes
- ┌───────────────────────────────────────┐
- │ run(_arg, account) │
- │ └─ account.initialize() │
- │ └─ Sets initialized flag to 1 │
- └───────────────────────────────────────┘
-
-3. Account state updated
- ┌───────────────────────────────────────┐
- │ Bank Account │
- │ Storage[0] = [1, 0, 0, 0] ← Initialized
- │ Now visible on-chain │
- └───────────────────────────────────────┘
-```
-
-Before initialization:
-- Account exists locally but isn't visible on the network
-- Cannot receive notes or interact with other accounts
-
-After initialization:
-- Account is "deployed" and visible
-- Can receive deposits and interact normally
-
-## Using Script Arguments
-
-The `_arg` parameter can pass data to the script:
-
-```rust title="Example: Parameterized script"
-#[tx_script]
-fn run(arg: Word, account: &mut Account) {
- // Use arg as configuration
- let config_value = arg[0];
- account.configure(config_value);
-}
-```
-
-When creating the transaction, provide the argument:
-
-```rust title="Integration code (not contract code)"
-let tx_script_args = Word::from([felt!(42), felt!(0), felt!(0), felt!(0)]);
-let tx_context = mock_chain
- .build_tx_context(bank_account.id(), &[], &[])?
- .tx_script(init_tx_script)
- .tx_script_args(tx_script_args) // Pass the argument
- .build()?;
-```
-
-## Try It: Verify Initialization Works
-
-Let's test that the initialization transaction script enables deposits.
-
-Create a test file:
-
-```rust title="integration/tests/part6_tx_script_test.rs"
-use integration::helpers::{
- build_project_in_dir, create_testing_account_from_package, AccountCreationConfig,
-};
-use miden_client::account::{StorageMap, StorageSlot, StorageSlotName};
-use miden_client::Word;
-use miden_client::transaction::TransactionScript;
-use miden_testing::MockChain;
-use std::{path::Path, sync::Arc};
-
-/// Test that the init-tx-script properly initializes the bank account
-#[tokio::test]
-async fn test_init_tx_script_enables_deposits() -> anyhow::Result<()> {
- // Build all required packages
- let mut builder = MockChain::builder();
-
- let bank_package = Arc::new(build_project_in_dir(
- Path::new("../contracts/bank-account"),
- true,
- )?);
-
- let init_tx_script_package = Arc::new(build_project_in_dir(
- Path::new("../contracts/init-tx-script"),
- true,
- )?);
-
- // Create uninitialized bank account with named storage slots
- let initialized_slot =
- StorageSlotName::new("miden::component::miden_bank_account::initialized")
- .expect("Valid slot name");
- let balances_slot =
- StorageSlotName::new("miden::component::miden_bank_account::balances")
- .expect("Valid slot name");
-
- let bank_cfg = AccountCreationConfig {
- storage_slots: vec![
- StorageSlot::with_value(initialized_slot.clone(), Word::default()),
- StorageSlot::with_map(
- balances_slot,
- StorageMap::with_entries([]).expect("Empty storage map"),
- ),
- ],
- ..Default::default()
- };
-
- let mut bank_account =
- create_testing_account_from_package(bank_package.clone(), bank_cfg).await?;
-
- // Verify bank is NOT initialized
- let initial_storage = bank_account.storage().get_item(&initialized_slot)?;
- assert_eq!(
- initial_storage[0].as_int(),
- 0,
- "Bank should start uninitialized"
- );
-
- println!("Step 1: Bank starts uninitialized (storage[0] = 0)");
-
- // Add bank to mock chain
- builder.add_account(bank_account.clone())?;
- let mut mock_chain = builder.build()?;
-
- // Create the TransactionScript from our init-tx-script
- let init_program = init_tx_script_package.unwrap_program();
- let init_tx_script = TransactionScript::new((*init_program).clone());
-
- // Build and execute the initialization transaction
- 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()?;
-
- // Verify bank IS now initialized
- let final_storage = bank_account.storage().get_item(&initialized_slot)?;
- assert_eq!(
- final_storage[0].as_int(),
- 1,
- "Bank should be initialized after tx script"
- );
-
- println!("Step 2: Bank initialized via transaction script (storage[0] = 1)");
- println!("\nPart 6 transaction script test passed!");
-
- Ok(())
-}
-```
-
-Run the test from the project root:
-
-```bash title=">_ Terminal"
-cargo test --package integration test_init_tx_script_enables_deposits -- --nocapture
-```
-
-
-Expected output
-
-```text
- Compiling integration v0.1.0 (/path/to/miden-bank/integration)
- Finished `test` profile [unoptimized + debuginfo] target(s)
- Running tests/part6_tx_script_test.rs
-
-running 1 test
-✓ Bank successfully initialized via transaction script
- Storage[0] changed from [0,0,0,0] to [1,0,0,0]
- Bank is now ready to accept deposits!
-test test_init_tx_script_enables_deposits ... ok
-
-test result: ok. 1 passed; 0 failed; 0 ignored
-```
-
-
-
-:::tip Troubleshooting
-**"Cannot find module bindings"**: The bank-account wasn't built. Run `miden build` in `contracts/bank-account` first.
-
-**"Dependency not found"**: Check that both dependency sections are in Cargo.toml with correct paths.
-:::
-
-## What We've Built So Far
-
-| Component | Status | Description |
-|-----------|--------|-------------|
-| `bank-account` | ✅ Complete | Full deposit logic with storage and constraints |
-| `deposit-note` | ✅ Complete | Note script that calls deposit method |
-| `init-tx-script` | ✅ Complete | Transaction script for initialization |
-| `withdraw-request-note` | Not started | Coming in Part 7 |
-
-## Complete Code for This Part
-
-
-Click to see the complete init-tx-script code
-
-```rust title="contracts/init-tx-script/src/lib.rs"
-// Do not link against libstd (i.e. anything defined in `std::`)
-#![no_std]
-#![feature(alloc_error_handler)]
-
-use miden::*;
-
-// Import the Account binding which wraps the bank-account component methods
-use crate::bindings::Account;
-
-/// Initialize Transaction Script
-///
-/// This transaction script initializes the bank account, enabling deposits.
-/// It must be executed by the bank account owner before any deposits can be made.
-///
-/// # Flow
-/// 1. Transaction is created with this script attached
-/// 2. Script executes in the context of the bank account
-/// 3. Calls `account.initialize()` to enable deposits
-/// 4. Bank account is now "deployed" and visible on chain
-#[tx_script]
-fn run(_arg: Word, account: &mut Account) {
- account.initialize();
-}
-```
-
-```toml title="contracts/init-tx-script/Cargo.toml"
-[package]
-name = "init-tx-script"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-miden = { version = "0.10" }
-
-[package.metadata.component]
-package = "miden:init-tx-script"
-
-[package.metadata.miden]
-project-kind = "transaction-script"
-
-[package.metadata.miden.dependencies]
-"miden:bank-account" = { path = "../bank-account" }
-
-[package.metadata.component.target.dependencies]
-"miden:bank-account" = { path = "../bank-account/target/generated-wit/" }
-```
-
-
-
-## Key Takeaways
-
-1. **`#[tx_script]`** marks the entry point with signature `fn run(_arg: Word, account: &mut Account)`
-2. **Direct account access** - Methods called on the `account` parameter, not via module imports
-3. **Owner-initiated** - Only the account owner can execute transaction scripts
-4. **Deployment pattern** - First state change makes account visible on-chain
-5. **Dependencies** - Same Cargo.toml configuration as note scripts
-
-:::tip View Complete Source
-See the complete transaction script implementation in the [miden-bank repository](https://github.com/keinberger/miden-bank/blob/main/contracts/init-tx-script/src/lib.rs).
-:::
-
-## Next Steps
-
-Now that you understand transaction scripts, let's learn the advanced topic of creating output notes in [Part 7: Creating Output Notes](./output-notes).
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/07-output-notes.md b/docs/builder/tutorials/rust-compiler/miden-bank/07-output-notes.md
deleted file mode 100644
index f4c38ed4..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/07-output-notes.md
+++ /dev/null
@@ -1,724 +0,0 @@
----
-sidebar_position: 7
-title: "Part 7: Creating Output Notes"
-description: "Learn how to create output notes programmatically within account methods, including the P2ID (Pay-to-ID) note pattern for sending assets."
----
-
-# Part 7: Creating Output Notes
-
-In this section, you'll learn how to create output notes from within account methods. We'll implement the full withdrawal logic that creates P2ID (Pay-to-ID) notes to send assets back to depositors.
-
-## What You'll Build in This Part
-
-By the end of this section, you will have:
-
-- Created the `withdraw-request-note` note script project
-- Implemented the `withdraw()` method with balance validation
-- Implemented `create_p2id_note()` for sending assets
-- **Verified withdrawals work** via a MockChain test
-
-## Building on Part 6
-
-In Part 6, you created a transaction script for initialization. Now you'll complete the bank by implementing withdrawals that create output notes:
-
-```text
-┌────────────────────────────────────────────────────────────────┐
-│ Complete Bank Flow │
-├────────────────────────────────────────────────────────────────┤
-│ │
-│ Part 6: Initialize │
-│ ┌─────────────────┐ init-tx-script ┌───────────────┐ │
-│ │ Bank (uninit) │ ──────────────────────▶│ Bank (ready) │ │
-│ └─────────────────┘ └───────────────┘ │
-│ │
-│ Part 4: Deposit │
-│ ┌─────────────────┐ deposit-note ┌───────────────┐ │
-│ │ User sends │ ──────────────────────▶│ Balance += X │ │
-│ │ deposit note │ │ Vault += X │ │
-│ └─────────────────┘ └───────────────┘ │
-│ │
-│ Part 7: Withdraw (NEW) │
-│ ┌─────────────────┐ withdraw-request ┌───────────────┐ │
-│ │ User sends │ ──────────────────────▶│ Balance -= X │ │
-│ │ withdraw note │ │ Creates P2ID │ │
-│ └─────────────────┘ │ output note │ │
-│ └───────────────┘ │
-│ │
-└────────────────────────────────────────────────────────────────┘
-```
-
-## Output Notes Overview
-
-When an account needs to send assets to another account, it creates an **output note**. The note travels through the network until the recipient consumes it.
-
-```text
-WITHDRAW FLOW:
-┌────────────────┐ ┌────────────────┐ ┌────────────────┐
-│ Bank Account │ creates │ P2ID Note │ consumed │ Depositor │
-│ │ ────────▶│ (with assets) │ ────────▶│ Wallet │
-│ remove_asset() │ │ │ │ receives asset │
-└────────────────┘ └────────────────┘ └────────────────┘
-```
-
-## The P2ID Note Pattern
-
-P2ID (Pay-to-ID) is a standard note pattern in Miden that sends assets to a specific account:
-
-- **Target account**: Only one account can consume the note
-- **Asset transfer**: Assets are transferred on consumption
-- **Standard script**: Uses a well-known script from miden-standards
-
-## Step 1: Add Withdraw Method to Bank Account
-
-First, let's add the `withdraw()` method to your bank account. Update `contracts/bank-account/src/lib.rs`:
-
-```rust title="contracts/bank-account/src/lib.rs"
-#[component]
-impl Bank {
- // ... existing methods (initialize, deposit, get_balance) ...
-
- /// Withdraw assets back to the depositor.
- ///
- /// Creates a P2ID note that sends the requested asset to the depositor's account.
- ///
- /// # Arguments
- /// * `depositor` - The AccountId of the user withdrawing
- /// * `withdraw_asset` - The fungible asset to withdraw
- /// * `serial_num` - Unique serial number for the P2ID output note
- /// * `tag` - The note tag for the P2ID output note (allows caller to specify routing)
- /// * `note_type` - Note type: 1 = Public (stored on-chain), 2 = Private (off-chain)
- ///
- /// # Panics
- /// Panics if the withdrawal amount exceeds the depositor's current balance.
- /// Panics if the bank has not been initialized.
- pub fn withdraw(
- &mut self,
- depositor: AccountId,
- withdraw_asset: Asset,
- serial_num: Word,
- tag: Felt,
- note_type: Felt,
- ) {
- // Ensure the bank is initialized before processing withdrawals
- self.require_initialized();
-
- // Extract the fungible amount from the asset
- let withdraw_amount = withdraw_asset.inner[0];
-
- // Create key from depositor's AccountId and asset faucet ID
- let key = Word::from([
- depositor.prefix,
- depositor.suffix,
- withdraw_asset.inner[3], // asset prefix (faucet)
- withdraw_asset.inner[2], // asset suffix (faucet)
- ]);
-
- // Get current balance and validate sufficient funds exist.
- // This check is critical: Felt arithmetic is modular, so subtracting
- // more than the balance would silently wrap to a large positive number.
- let current_balance: Felt = self.balances.get(&key);
- assert!(
- current_balance.as_u64() >= withdraw_amount.as_u64(),
- "Withdrawal amount exceeds available balance"
- );
-
- // Update balance: current - withdraw_amount
- let new_balance = current_balance - withdraw_amount;
- self.balances.set(key, new_balance);
-
- // Create a P2ID note to send the requested asset back to the depositor
- self.create_p2id_note(serial_num, &withdraw_asset, depositor, tag, note_type);
- }
-}
-```
-
-:::danger Critical Security: Balance Validation
-Always validate `current_balance >= withdraw_amount` BEFORE subtraction. Miden uses modular field arithmetic - subtracting a larger value silently wraps to a massive positive number!
-:::
-
-## Step 2: Add the P2ID Note Root
-
-The P2ID note uses a standard script from miden-standards. Add this helper function:
-
-```rust title="contracts/bank-account/src/lib.rs"
-#[component]
-impl Bank {
- // ... other methods ...
-
- /// Returns the P2ID note script root digest.
- ///
- /// This is a constant value derived from the standard P2ID note script in miden-standards.
- /// The digest is the MAST root of the compiled P2ID note script.
- fn p2id_note_root() -> Digest {
- Digest::from_word(Word::new([
- Felt::from_u64_unchecked(13362761878458161062),
- Felt::from_u64_unchecked(15090726097241769395),
- Felt::from_u64_unchecked(444910447169617901),
- Felt::from_u64_unchecked(3558201871398422326),
- ]))
- }
-}
-```
-
-:::warning Version-Specific
-This digest is specific to miden-standards version. If the P2ID script changes in a future version, this digest must be updated.
-:::
-
-## Step 3: Implement create_p2id_note
-
-Add the private method that creates the output note:
-
-```rust title="contracts/bank-account/src/lib.rs"
-#[component]
-impl Bank {
- // ... other methods ...
-
- /// Create a P2ID (Pay-to-ID) note to send assets to a recipient.
- ///
- /// # Arguments
- /// * `serial_num` - Unique serial number for the note
- /// * `asset` - The asset to include in the note
- /// * `recipient_id` - The AccountId that can consume this note
- /// * `tag` - The note tag (passed by caller to allow proper P2ID routing)
- /// * `note_type` - Note type as Felt: 1 = Public, 2 = Private
- fn create_p2id_note(
- &mut self,
- serial_num: Word,
- asset: &Asset,
- recipient_id: AccountId,
- tag: Felt,
- note_type: Felt,
- ) {
- // Convert the passed tag Felt to a Tag
- // The caller is responsible for computing the proper P2ID tag
- // (typically with_account_target for the recipient)
- let tag = Tag::from(tag);
-
- // Convert note_type Felt to NoteType
- // 1 = Public (stored on-chain), 2 = Private (off-chain)
- let note_type = NoteType::from(note_type);
-
- // Get the P2ID note script root digest
- let script_root = Self::p2id_note_root();
-
- // Compute the recipient hash from:
- // - serial_num: unique identifier for this note instance
- // - script_root: the P2ID note script's MAST root
- // - inputs: the target account ID
- //
- // The P2ID script expects inputs as [suffix, prefix]
- let recipient = Recipient::compute(
- serial_num,
- script_root,
- vec![
- recipient_id.suffix,
- recipient_id.prefix,
- ],
- );
-
- // Create the output note
- let note_idx = output_note::create(tag, note_type, recipient);
-
- // Remove the asset from the bank's vault
- native_account::remove_asset(asset.clone());
-
- // Add the asset to the output note
- output_note::add_asset(asset.clone(), note_idx);
- }
-}
-```
-
-### Understanding Recipient::compute()
-
-| Parameter | Description |
-|-----------|-------------|
-| `serial_num` | Unique 4-Felt value preventing note reuse |
-| `script_root` | The P2ID script's MAST root digest |
-| `inputs` | Script inputs (account ID for P2ID) |
-
-:::warning Array Ordering
-Note the order: `suffix` comes before `prefix`. This is the opposite of how `AccountId` fields are typically accessed. See [Common Pitfalls](../pitfalls#array-ordering-rustmasm-reversal) for details.
-:::
-
-### Understanding output_note::create()
-
-| Parameter | Type | Description |
-|-----------|------|-------------|
-| `tag` | `Tag` | Routing information for the note |
-| `note_type` | `NoteType` | Public (1) or Private (2) |
-| `recipient` | `Recipient` | Who can consume the note |
-
-## Step 4: Create the Withdraw Request Note Project
-
-Create the directory structure:
-
-```bash title=">_ Terminal"
-mkdir -p contracts/withdraw-request-note/src
-```
-
-### Configure Cargo.toml
-
-```toml title="contracts/withdraw-request-note/Cargo.toml"
-[package]
-name = "withdraw-request-note"
-version = "0.1.0"
-edition = "2021"
-
-[lib]
-crate-type = ["cdylib"]
-
-[dependencies]
-miden = { version = "0.10" }
-
-[package.metadata.component]
-package = "miden:withdraw-request-note"
-
-[package.metadata.miden]
-project-kind = "note-script"
-
-[package.metadata.miden.dependencies]
-"miden:bank-account" = { path = "../bank-account" }
-
-[package.metadata.component.target.dependencies]
-"miden:bank-account" = { path = "../bank-account/target/generated-wit/" }
-```
-
-### Update Workspace
-
-Add to your root `Cargo.toml`:
-
-```toml title="Cargo.toml"
-[workspace]
-members = [
- "integration"
-]
-exclude = [
- "contracts/",
-]
-resolver = "2"
-
-[workspace.package]
-edition = "2021"
-
-[workspace.dependencies]
-```
-
-## Step 5: Implement the Withdraw Request Note Script
-
-```rust title="contracts/withdraw-request-note/src/lib.rs"
-// Do not link against libstd (i.e. anything defined in `std::`)
-#![no_std]
-#![feature(alloc_error_handler)]
-
-use miden::*;
-
-// Import the bank account's generated bindings
-use crate::bindings::miden::bank_account::bank_account;
-
-/// Withdraw Request Note Script
-///
-/// When consumed by the Bank account, this note requests a withdrawal and
-/// the bank creates a P2ID note to send assets back to the depositor.
-///
-/// # Flow
-/// 1. Note is created by a depositor specifying the withdrawal details
-/// 2. Bank account consumes this note
-/// 3. Note script reads the sender (depositor) and inputs
-/// 4. Calls `bank_account::withdraw(depositor, asset, serial_num, tag, note_type)`
-/// 5. Bank updates the depositor's balance
-/// 6. Bank creates a P2ID note with the specified parameters to send assets back
-///
-/// # Note Inputs (10 Felts)
-/// [0-3]: withdraw asset (amount, 0, faucet_suffix, faucet_prefix)
-/// [4-7]: serial_num (random/unique per note)
-/// [8]: tag (P2ID note tag for routing)
-/// [9]: note_type (1 = Public, 2 = Private)
-#[note]
-struct WithdrawRequestNote;
-
-#[note]
-impl WithdrawRequestNote {
- #[note_script]
- fn run(self, _arg: Word) {
- // The depositor is whoever created/sent this note
- let depositor = active_note::get_sender();
-
- // Get the inputs
- let inputs = active_note::get_inputs();
-
- // Asset: [amount, 0, faucet_suffix, faucet_prefix]
- let withdraw_asset = Asset::new(Word::from([inputs[0], inputs[1], inputs[2], inputs[3]]));
-
- // Serial number: full 4 Felts (random/unique per note)
- let serial_num = Word::from([inputs[4], inputs[5], inputs[6], inputs[7]]);
-
- // Tag: single Felt for P2ID note routing
- let tag = inputs[8];
-
- // Note type: 1 = Public, 2 = Private
- let note_type = inputs[9];
-
- // Call the bank account to withdraw the assets
- bank_account::withdraw(depositor, withdraw_asset, serial_num, tag, note_type);
- }
-}
-```
-
-### Note Input Layout
-
-The withdraw-request-note expects 10 Felt inputs:
-
-```text
-Note Inputs (10 Felts):
-┌───────────────────────────────────────────────────────────────────────────┐
-│ Index │ Value │ Description │
-├───────┼─────────────────┼─────────────────────────────────────────────────┤
-│ 0 │ amount │ Token amount to withdraw │
-│ 1 │ 0 │ Reserved (always 0 for fungible) │
-│ 2 │ faucet_suffix │ Faucet ID suffix (identifies asset type) │
-│ 3 │ faucet_prefix │ Faucet ID prefix (identifies asset type) │
-│ 4-7 │ serial_num │ Unique ID for the output P2ID note (4 Felts) │
-│ 8 │ tag │ Note routing tag for P2ID note │
-│ 9 │ note_type │ 1 (Public) or 2 (Private) │
-└───────────────────────────────────────────────────────────────────────────┘
-```
-
-:::note Why the Asset is in Inputs
-Unlike the deposit note which gets assets from `active_note::get_assets()`, the withdraw request note doesn't carry assets. Instead, the asset to withdraw is specified in the note inputs. The bank then withdraws from its own vault based on these inputs.
-:::
-
-## Step 6: Build All Components
-
-Build in dependency order:
-
-```bash title=">_ Terminal"
-# 1. Build the account component (generates WIT files)
-cd contracts/bank-account
-miden build
-
-# 2. Build the withdraw request note
-cd ../withdraw-request-note
-miden build
-```
-
-## Try It: Verify Withdrawals Work
-
-Let's test the complete withdraw flow. This test:
-1. Creates a bank account and initializes it
-2. Creates a deposit note and processes it
-3. Creates a withdraw-request note with the 10-Felt input layout
-4. Processes the withdrawal and verifies a P2ID output note is created
-
-```rust title="integration/tests/part7_withdraw_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, StorageSlotName},
- asset::{Asset, FungibleAsset},
- note::{build_p2id_recipient, Note, NoteAssets, NoteMetadata, NoteTag, NoteType},
- transaction::{OutputNote, TransactionScript},
- Felt, Word,
-};
-use miden_testing::{Auth, MockChain};
-use std::{path::Path, sync::Arc};
-
-#[tokio::test]
-async fn test_withdraw_creates_p2id_note() -> anyhow::Result<()> {
- // =========================================================================
- // SETUP
- // =========================================================================
- let mut builder = MockChain::builder();
-
- let deposit_amount: u64 = 1000;
-
- // Create faucet and sender (depositor)
- let faucet =
- builder.add_existing_basic_faucet(Auth::BasicAuth, "TEST", deposit_amount, Some(10))?;
- let sender = builder.add_existing_wallet_with_assets(
- Auth::BasicAuth,
- [FungibleAsset::new(faucet.id(), deposit_amount)?.into()],
- )?;
-
- // Build contracts
- 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,
- )?);
-
- // Create bank account with named storage slots
- let initialized_slot =
- StorageSlotName::new("miden::component::miden_bank_account::initialized")
- .expect("Valid slot name");
- let balances_slot =
- StorageSlotName::new("miden::component::miden_bank_account::balances")
- .expect("Valid slot name");
-
- let bank_cfg = AccountCreationConfig {
- storage_slots: vec![
- miden_client::account::StorageSlot::with_value(initialized_slot, Word::default()),
- miden_client::account::StorageSlot::with_map(
- balances_slot,
- StorageMap::with_entries([]).expect("Empty storage map"),
- ),
- ],
- ..Default::default()
- };
- let mut bank_account =
- create_testing_account_from_package(bank_package.clone(), bank_cfg).await?;
-
- // Create deposit note
- 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()
- },
- )?;
-
- // Add accounts and notes to builder
- builder.add_account(bank_account.clone())?;
- builder.add_output_note(OutputNote::Full(deposit_note.clone()));
-
- // =========================================================================
- // CRAFT WITHDRAW REQUEST NOTE (10-Felt input layout)
- // =========================================================================
- let withdraw_amount = deposit_amount / 2;
-
- // Compute P2ID tag for the sender
- let p2id_tag = NoteTag::with_account_target(sender.id());
- let p2id_tag_felt = Felt::new(p2id_tag.as_u32() as u64);
-
- // Serial number for output note
- let p2id_output_note_serial_num = Word::from([
- Felt::new(0x1234567890abcdef),
- Felt::new(0xfedcba0987654321),
- Felt::new(0xdeadbeefcafebabe),
- Felt::new(0x0123456789abcdef),
- ]);
-
- let note_type_felt = Felt::new(1); // Public
-
- // Note inputs: 10 Felts
- // [0-3]: withdraw asset (amount, 0, faucet_suffix, faucet_prefix)
- // [4-7]: serial_num
- // [8]: tag
- // [9]: 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,
- 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()
- },
- )?;
-
- builder.add_output_note(OutputNote::Full(withdraw_request_note.clone()));
-
- // =========================================================================
- // EXECUTE: Initialize, Deposit, Withdraw
- // =========================================================================
- let mut mock_chain = builder.build()?;
-
- // Initialize 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!("Step 1: Bank initialized");
-
- // Process deposit
- 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()?;
-
- println!("Step 2: Deposited {} tokens", deposit_amount);
-
- // Process withdraw with 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,
- );
- 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)])
- .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!("Step 3: Withdrew {} tokens", withdraw_amount);
- println!("\nPart 7 withdraw test passed!");
-
- Ok(())
-}
-```
-
-Run the test from the project root:
-
-```bash title=">_ Terminal"
-cargo test --package integration test_withdraw_creates_p2id_note -- --nocapture
-```
-
-
-Expected output
-
-```text
- Compiling integration v0.1.0 (/path/to/miden-bank/integration)
- Finished `test` profile [unoptimized + debuginfo] target(s)
- Running tests/part7_withdraw_test.rs
-
-running 1 test
-Step 1: Bank initialized
-Step 2: Deposited 1000 tokens
-Step 3: Withdrew 500 tokens
-
-Part 7 withdraw test passed!
-test test_withdraw_creates_p2id_note ... ok
-
-test result: ok. 1 passed; 0 failed; 0 ignored
-```
-
-
-
-:::tip Troubleshooting
-**"Insufficient balance for withdrawal"**: Make sure the deposit was processed before attempting withdrawal.
-
-**"Missing expected output note"**: Verify the P2ID note parameters (tag, serial_num, etc.) match exactly.
-:::
-
-## What We've Built So Far
-
-| Component | Status | Description |
-|-----------|--------|-------------|
-| `bank-account` | ✅ Complete | Full deposit AND withdraw logic |
-| `deposit-note` | ✅ Complete | Note script for deposits |
-| `withdraw-request-note` | ✅ Complete | Note script for withdrawals |
-| `init-tx-script` | ✅ Complete | Transaction script for initialization |
-
-## Complete Code for This Part
-
-
-Click to see the complete withdraw-request-note code
-
-```rust title="contracts/withdraw-request-note/src/lib.rs"
-// Do not link against libstd (i.e. anything defined in `std::`)
-#![no_std]
-#![feature(alloc_error_handler)]
-
-use miden::*;
-
-// Import the bank account's generated bindings
-use crate::bindings::miden::bank_account::bank_account;
-
-/// Withdraw Request Note Script
-///
-/// When consumed by the Bank account, this note requests a withdrawal and
-/// the bank creates a P2ID note to send assets back to the depositor.
-///
-/// # Note Inputs (10 Felts)
-/// [0-3]: withdraw asset (amount, 0, faucet_suffix, faucet_prefix)
-/// [4-7]: serial_num (random/unique per note)
-/// [8]: tag (P2ID note tag for routing)
-/// [9]: note_type (1 = Public, 2 = Private)
-#[note]
-struct WithdrawRequestNote;
-
-#[note]
-impl WithdrawRequestNote {
- #[note_script]
- fn run(self, _arg: Word) {
- // The depositor is whoever created/sent this note
- let depositor = active_note::get_sender();
-
- // Get the inputs
- let inputs = active_note::get_inputs();
-
- // Asset: [amount, 0, faucet_suffix, faucet_prefix]
- let withdraw_asset = Asset::new(Word::from([inputs[0], inputs[1], inputs[2], inputs[3]]));
-
- // Serial number: full 4 Felts (random/unique per note)
- let serial_num = Word::from([inputs[4], inputs[5], inputs[6], inputs[7]]);
-
- // Tag: single Felt for P2ID note routing
- let tag = inputs[8];
-
- // Note type: 1 = Public, 2 = Private
- let note_type = inputs[9];
-
- // Call the bank account to withdraw the assets
- bank_account::withdraw(depositor, withdraw_asset, serial_num, tag, note_type);
- }
-}
-```
-
-
-
-## Key Takeaways
-
-1. **`Recipient::compute()`** creates a cryptographic commitment from serial number, script root, and inputs
-2. **`output_note::create()`** creates the note with tag, note type, and recipient
-3. **`output_note::add_asset()`** attaches assets to the created note
-4. **P2ID pattern** uses a standard script with account ID as input
-5. **Serial numbers** must be unique to prevent note replay
-6. **Array ordering** - P2ID expects `[suffix, prefix, ...]` not `[prefix, suffix, ...]`
-7. **Always validate before subtraction** to prevent underflow exploits
-
-:::tip View Complete Source
-See the complete implementation in the [miden-bank repository](https://github.com/keinberger/miden-bank).
-:::
-
-## Next Steps
-
-Now that you've built all the components, let's see how they work together in [Part 8: Complete Flows](./complete-flows).
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/08-complete-flows.md b/docs/builder/tutorials/rust-compiler/miden-bank/08-complete-flows.md
deleted file mode 100644
index 9abe925f..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/08-complete-flows.md
+++ /dev/null
@@ -1,585 +0,0 @@
----
-sidebar_position: 8
-title: "Part 8: Complete Flows"
-description: "Walk through end-to-end deposit and withdrawal flows, understanding how all the pieces work together in the banking application."
----
-
-# 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:
-
-```text
-┌────────────────────────────────────────────────────────────────┐
-│ 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:
-
-```text
-┌─────────────────────────────────────────────────────────────────────┐
-│ 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:
-
-```text
-┌─────────────────────────────────────────────────────────────────────┐
-│ 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() │ │
-│ │ 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] │ │
-│ │ ) │ │
-│ │ 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
-
-Let's create a comprehensive test that exercises the entire bank system:
-
-```rust title="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, StorageSlot, StorageSlotName},
- asset::{Asset, FungibleAsset},
- note::{build_p2id_recipient, Note, NoteAssets, NoteMetadata, NoteTag, NoteType},
- transaction::{OutputNote, TransactionScript},
- Felt, Word,
-};
-use miden_testing::{Auth, MockChain};
-use std::{path::Path, sync::Arc};
-
-/// 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 named storage slots
- let initialized_slot =
- StorageSlotName::new("miden::component::miden_bank_account::initialized")
- .expect("Valid slot name");
- let balances_slot =
- StorageSlotName::new("miden::component::miden_bank_account::balances")
- .expect("Valid slot name");
-
- // Create bank account with storage slots
- let bank_cfg = AccountCreationConfig {
- storage_slots: vec![
- StorageSlot::with_value(initialized_slot, Word::default()),
- StorageSlot::with_map(
- balances_slot.clone(),
- StorageMap::with_entries([]).expect("Empty storage map"),
- ),
- ],
- ..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 10-Felt input layout
- let p2id_tag = NoteTag::with_account_target(sender.id());
- let p2id_tag_felt = Felt::new(p2id_tag.as_u32() as u64);
-
- let p2id_output_note_serial_num = Word::from([
- Felt::new(0x1234567890abcdef),
- Felt::new(0xfedcba0987654321),
- Felt::new(0xdeadbeefcafebabe),
- Felt::new(0x0123456789abcdef),
- ]);
-
- let note_type_felt = Felt::new(1); // Public
-
- // Note inputs: 10 Felts
- // [0-3]: withdraw asset (amount, 0, faucet_suffix, faucet_prefix)
- // [4-7]: serial_num
- // [8]: tag
- // [9]: 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,
- 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()));
- builder.add_output_note(OutputNote::Full(withdraw_request_note.clone()));
-
- 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(&balances_slot, 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,
- );
- 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)])
- .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(&balances_slot, 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:
-
-```bash title=">_ Terminal"
-cargo test --package integration test_complete_bank_flow -- --nocapture
-```
-
-
-Expected output
-
-```text
- 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:
-
-| 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` | `Value` | Initialization flag |
-| `balances` | `StorageMap` | Depositor balances |
-
-| API | Purpose |
-|-----|---------|
-| `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:
-
-:::danger Always Validate Before Subtraction
-```rust
-// ❌ 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()
-);
-```
-:::
-
-:::warning Felt Comparison Operators
-Never use `<`, `>` on Felt values directly. Always convert to u64 first:
-```rust
-// ❌ 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
-
-- **[Testing with MockChain](../testing)** - Deep dive into testing patterns
-- **[Debugging Guide](../debugging)** - Troubleshoot common issues
-- **[Common Pitfalls](../pitfalls)** - Avoid known gotchas
-
-### Build More
-
-Use these patterns to build:
-- Token faucets
-- DEX contracts
-- NFT marketplaces
-- Multi-signature wallets
-- And more!
-
-:::tip View Complete Source
-Explore the complete banking application:
-- [All Contracts](https://github.com/keinberger/miden-bank/tree/main/contracts)
-- [Integration Tests](https://github.com/keinberger/miden-bank/tree/main/integration/tests)
-- [Test Helpers](https://github.com/keinberger/miden-bank/blob/main/integration/src/helpers.rs)
-:::
-
-Happy building on Miden! 🚀
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/_category_.json b/docs/builder/tutorials/rust-compiler/miden-bank/_category_.json
deleted file mode 100644
index b4a64e58..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/_category_.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "label": "Miden Bank",
- "position": 1,
- "link": {
- "type": "doc",
- "id": "builder/tutorials/rust-compiler/miden-bank/index"
- }
-}
diff --git a/docs/builder/tutorials/rust-compiler/miden-bank/index.md b/docs/builder/tutorials/rust-compiler/miden-bank/index.md
deleted file mode 100644
index ac9f8ef3..00000000
--- a/docs/builder/tutorials/rust-compiler/miden-bank/index.md
+++ /dev/null
@@ -1,207 +0,0 @@
----
-sidebar_position: 4
-title: "Building a Bank with Miden Rust"
-description: "Learn Miden Rust compiler fundamentals by building a complete banking application with deposits, withdrawals, and asset management."
----
-
-# Building a Bank with Miden Rust
-
-Welcome to the **Miden Rust Compiler Tutorial**! This hands-on guide teaches you how to build smart contracts on Miden using Rust by walking through a complete banking application.
-
-## What You'll Build
-
-You'll create a **banking system** consisting of:
-
-- **Bank Account Component**: A smart contract that manages depositor balances and vault operations
-- **Deposit Note**: A note script that processes deposits into the bank
-- **Withdraw Request Note**: A note script that requests withdrawals from the bank
-- **Initialization Script**: A transaction script to deploy and initialize the bank
-
-Each part ends with a **runnable MockChain test** that verifies what you built works correctly.
-
-## Tutorial Structure
-
-This tutorial is designed for hands-on learning. Each part builds on the previous one, and every part includes:
-
-- **What You'll Build** - Clear objectives for the section
-- **Step-by-step code** - Progressively building functionality
-- **Try It section** - A MockChain test to verify your code works
-- **Complete code** - Full code listing for reference
-
-### Parts Overview
-
-| Part | Topic | What You'll Build |
-|------|-------|-------------------|
-| **Part 0** | [Project Setup](./project-setup) | Create project with `miden new` |
-| **Part 1** | [Account Components](./account-components) | Bank struct with storage |
-| **Part 2** | [Constants & Constraints](./constants-constraints) | Business rules and validation |
-| **Part 3** | [Asset Management](./asset-management) | Deposit logic with balance tracking |
-| **Part 4** | [Note Scripts](./note-scripts) | Deposit note for receiving assets |
-| **Part 5** | [Cross-Component Calls](./cross-component-calls) | How bindings enable calls |
-| **Part 6** | [Transaction Scripts](./transaction-scripts) | Initialization script |
-| **Part 7** | [Output Notes](./output-notes) | Withdraw with P2ID output |
-| **Part 8** | [Complete Flows](./complete-flows) | End-to-end verification |
-
-## Tutorial Cards
-
-import DocCard from '@theme/DocCard';
-
-
-
-
-
-
-
-
-
-
-
-## Prerequisites
-
-Before starting this tutorial, ensure you have:
-
-- Completed the [Get Started guide](../../../get-started/) (familiarity with `midenup`, `miden new`, basic tooling)
-- Basic understanding of Miden concepts (accounts, notes, transactions)
-- Rust programming experience
-
-:::tip No Miden Rust Experience Required
-This tutorial assumes no prior experience with the Miden Rust compiler. We'll explain each concept as we encounter it.
-:::
-
-## Concepts Covered
-
-This tutorial covers the following Miden Rust compiler features:
-
-| Concept | Description | Part |
-| --------------------- | ---------------------------------------------------------- | ---- |
-| `#[component]` | Define account components with storage | 1 |
-| Storage Types | `Value` for single values, `StorageMap` for key-value data | 1 |
-| Constants | Define compile-time business rules | 2 |
-| Assertions | Validate conditions and handle errors | 2 |
-| Asset Handling | Add and remove assets from account vaults | 3 |
-| `#[note]` + `#[note_script]` | Note struct/impl pattern for scripts consumed by accounts | 4 |
-| Cross-Component Calls | Call account methods from note scripts | 5 |
-| `#[tx_script]` | Transaction scripts for account operations | 6 |
-| Output Notes | Create notes programmatically | 7 |
-
-## Source Code Repository
-
-The complete source code for this tutorial is available in the **[miden-bank repository](https://github.com/keinberger/miden-bank)**. You can clone it to follow along or reference the implementation:
-
-```bash title=">_ Terminal"
-git clone https://github.com/keinberger/miden-bank.git
-cd miden-bank
-```
-
-## Supplementary Guides
-
-These standalone guides complement the tutorial:
-
-- **[Testing with MockChain](../testing)** - Learn to test your contracts
-- **[Debugging](../debugging)** - Troubleshoot common issues
-- **[Common Pitfalls](../pitfalls)** - Avoid known gotchas
-
-## Getting Help
-
-If you get stuck during this tutorial:
-
-- Check the [Miden Docs](https://docs.miden.xyz) for detailed technical references
-- Join the [Build On Miden](https://t.me/BuildOnMiden) Telegram community for support
-- Review the complete code in the [miden-bank repository](https://github.com/keinberger/miden-bank)
-
-Ready to build your first Miden banking application? Let's get started with [Part 0: Project Setup](./project-setup)!
diff --git a/docs/builder/tutorials/rust-compiler/pitfalls.md b/docs/builder/tutorials/rust-compiler/pitfalls.md
index 4742d85c..1c34b13a 100644
--- a/docs/builder/tutorials/rust-compiler/pitfalls.md
+++ b/docs/builder/tutorials/rust-compiler/pitfalls.md
@@ -493,4 +493,4 @@ See these patterns in context in the [miden-bank repository](https://github.com/
- **[Debugging Guide](./debugging)** - Troubleshoot errors
- **[Testing Guide](./testing)** - MockChain patterns
-- **[Miden Bank Tutorial](./miden-bank/)** - See these patterns in context
+- **[Miden Bank Tutorial](../miden-bank/)** - See these patterns in context
diff --git a/docs/builder/tutorials/rust-compiler/testing.md b/docs/builder/tutorials/rust-compiler/testing.md
index eb089822..ff2bdc58 100644
--- a/docs/builder/tutorials/rust-compiler/testing.md
+++ b/docs/builder/tutorials/rust-compiler/testing.md
@@ -607,4 +607,4 @@ See the complete test implementations in the [miden-bank repository](https://git
- **[Debugging Guide](./debugging)** - Troubleshoot common issues
- **[Common Pitfalls](./pitfalls)** - Avoid known gotchas
-- **[Miden Bank Tutorial](./miden-bank/)** - See testing in action
+- **[Miden Bank Tutorial](../miden-bank/)** - See testing in action
diff --git a/docusaurus.config.ts b/docusaurus.config.ts
index ac81dc07..0320e1cb 100644
--- a/docusaurus.config.ts
+++ b/docusaurus.config.ts
@@ -106,6 +106,10 @@ const config: Config = {
if (existingPath === "/builder/glossary") {
redirects.push("/glossary");
}
+ if (existingPath.startsWith("/builder/tutorials/miden-bank")) {
+ redirects.push(existingPath.replace("/builder/tutorials/miden-bank", "/builder/tutorials/rust-compiler/miden-bank"));
+ redirects.push(existingPath.replace("/builder/tutorials/miden-bank", "/builder/develop/tutorials/rust-compiler/miden-bank"));
+ }
if (existingPath.startsWith("/builder/tutorials")) {
redirects.push(existingPath.replace("/builder/tutorials", "/miden-tutorials"));
}