diff --git a/versioned_docs/version-0.14/builder/_category_.json b/versioned_docs/version-0.14/builder/_category_.json
new file mode 100644
index 00000000..34d2480b
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/_category_.json
@@ -0,0 +1,6 @@
+{
+ "label": "Builder",
+ "position": 1,
+ "collapsible": false,
+ "collapsed": false
+}
diff --git a/versioned_docs/version-0.14/builder/faq.md b/versioned_docs/version-0.14/builder/faq.md
new file mode 100644
index 00000000..cefcf9cb
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/faq.md
@@ -0,0 +1,101 @@
+# FAQ
+
+## How is privacy implemented in Miden?
+
+Miden leverages zero-knowledge proofs and client side execution and proving to provide security and privacy.
+
+## Does Miden support encrypted notes?
+
+At the moment, Miden does not have support for encrypted notes but it is a planned feature.
+
+## Why does Miden have delegated proving?
+
+Miden leverages delegated proving for a few technical and practical reasons:
+
+1. **Computational:** Generating zero-knowledge proofs is a computationally intensive work. The proving process requires significant processing power and memory, making it impractical for some end-user devices (like smartphones) to generate.
+2. **Technical architecture**:
+Miden's architecture separates concerns between:
+ - **Transaction Creation**: End users create and sign transactions
+ - **Proof Generation**: Specialized provers generate validity proofs
+ - **Verification**: The network verifies these proofs
+3. **Proving efficiency**:
+Delegated provers can use optimized hardware that wouldn't be available to end-user devices, specifically designed for the mathematical operations needed in STARK proof generation.
+
+## What is the lifecycle of a transaction?
+
+### 1. Transaction Creation
+
+- User creates a transaction specifying the operations to perform (transfers, contract interactions, etc.)
+- Client performs preliminary validation of the transaction and its structure
+- The user authorizes the specified state transitions by signing the transaction
+
+### 2. Transaction Submission
+
+- The signed transaction is submitted to Miden network nodes
+- The transaction enters the mempool (transaction pool) where it waits to be selected to be included in the state
+- Nodes perform basic validation checks on the transaction structure and signature
+
+### 3. Transaction Selection
+
+- A sequencer (or multiple sequencers in a decentralized setting) selects transactions from the mempool
+- The sequencer groups transactions into bundles based on state access patterns and other criteria
+- The transaction execution order is determined according to protocol mechanism
+
+### 4. Transaction Execution
+
+- The current state relevant to the transaction is loaded
+- The Miden VM executes the transaction operations
+- **State Transition Computation**: The resulting state transitions are computed
+- An execution trace of the transaction is generated which captures all the computation
+
+### 5. Proof Generation
+
+- A STARK based cryptographic proof is generated attesting to the correctness of the execution
+- A proof for the aggregated transaction is created
+
+### 6. Block Production
+
+- The aggregated bundle of transactions along with their proofs are assembled into a block
+- A recursive proof attesting to all bundle proofs is generated
+- The block data structure is finalized with the aggregated proof
+
+### 7. L1 Submission
+
+- Transaction data is posted to the data availability layer
+- The block proof and state delta commitment are submitted to the Miden contract (that is bridged to Ethereum/Agglayer)
+- The L1 contract verifies validity of the proof
+- Upon successful verification, the L1 contract updates the state root
+
+### 8. Finalization
+
+- Transaction receipts and events are generated
+- The global state commitment is updated to reflect the new state
+- The transaction is now considered finalized on the L1
+- Users and indexers get notified/updated about the transaction completion
+
+## Do notes in Miden support recency conditions?
+
+Yes, Miden enables consumption of notes based on time conditions, such as:
+
+- A specific block height being reached
+- A timestamp threshold being passed
+- An oracle providing specific data
+- Another transaction being confirmed
+
+## What does a Miden operator do in Miden?
+
+A Miden operator is an entity that maintains the infrastructure necessary for the functioning of the Miden rollup. Their roles may involve:
+
+1. Running Sequencer Nodes
+2. Operating the Prover Infrastructure
+3. Submitting Proofs to L1
+4. Maintaining Data Availability
+5. Participating in the Consensus Mechanism
+
+## How does bridging works in Miden?
+
+Miden does not yet have a fully operational bridge, work in progress.
+
+## What does the gas fee model of Miden look like?
+
+Miden does not yet have a fully implemented fee model, work in progress.
diff --git a/versioned_docs/version-0.14/builder/get-started/_category_.json b/versioned_docs/version-0.14/builder/get-started/_category_.json
new file mode 100644
index 00000000..f230021f
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/_category_.json
@@ -0,0 +1,8 @@
+{
+ "label": "Get Started",
+ "position": 1,
+ "link": {
+ "type": "doc",
+ "id": "builder/get-started/index"
+ }
+}
diff --git a/versioned_docs/version-0.14/builder/get-started/accounts.md b/versioned_docs/version-0.14/builder/get-started/accounts.md
new file mode 100644
index 00000000..b25fad61
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/accounts.md
@@ -0,0 +1,411 @@
+---
+sidebar_position: 2
+title: Accounts
+description: Learn how to create and manage Miden accounts programmatically using Rust and TypeScript.
+---
+
+# Accounts
+
+Miden's account model is fundamentally different from traditional blockchains. Let's explore how to create and manage accounts programmatically.
+
+## Understanding Miden Accounts
+
+Before diving into account creation, it's essential to understand what makes Miden accounts unique compared to traditional blockchain addresses.
+
+**What Makes Miden Accounts Special:**
+
+- **Smart Contract Wallets**: Every account is a programmable smart contract that can hold assets and execute custom logic
+- **Modular Design**: Accounts are composed of reusable components (authentication, wallet functionality, etc.)
+- **Privacy Levels**: Choose between public or private storage modes
+
+Miden accounts differ from traditional blockchain addresses in fundamental ways.
+
+**Account Architecture:**
+
+- Every account is a **smart contract** with programmable logic
+- Accounts can store **assets** (fungible and non-fungible tokens) in their vault
+- Each account has **storage slots** for custom data
+- Accounts are composed of **modular components** for different functionalities
+
+**Storage Modes:**
+
+- **Public**: All state visible onchain (transparent operations)
+- **Private**: Only commitments onchain, full state held privately
+
+### Account Structure
+
+Every Miden account contains these core components:
+
+- **Vault**: Secure asset storage
+- **Storage**: Key-value data store (up to 255 slots)
+- **Code**: Smart contract logic
+- **Nonce**: Anti-replay counter
+- **Components**: Modular functionality (authentication, wallet, etc.)
+
+
+Account Struct
+
+```rust
+pub struct MidenAccount {
+ /// Immutable, 120-bit ID encoding type, storage mode and version.
+ pub id: [u8; 15],
+
+ /// Determines mutability of the account (immutable, mutable).
+ pub account_type: AccountType,
+
+ /// Storage placement preference (public/private).
+ pub storage_mode: StorageMode,
+
+ /// Root commitment of the account CODE (MAST root).
+ pub code_commitment: [u8; 32],
+
+ /// Root commitment of the account STORAGE (slots / maps).
+ /// Think of this as the "root hash" of the Account's storage merkle tree.
+ pub storage_commitment: [u8; 32],
+
+ /// Vault commitment. For compact headers we keep only an optional aggregate commitment.
+ /// Indexers can materialize a richer view (e.g., list of assets) offchain.
+ pub vault_commitment: Option<[u8; 32]>,
+
+ /// Monotonically increasing counter; must increment exactly once when state changes.
+ pub nonce: u64,
+
+ /// Merged set of account components that defined this account's interface and storage.
+ /// Accounts are composed by merging components (e.g. wallet component + an auth component).
+ pub components: Vec,
+
+ /// Authentication procedure metadata (e.g. "RpoFalcon512").
+ pub authentication: AuthenticationDescriptor,
+}
+```
+
+**Account Types:**
+
+```rust
+pub enum AccountType {
+ // Faucet that can issue fungible assets
+ FungibleFaucet = 2,
+ // Faucet that can issue non-fungible assets
+ NonFungibleFaucet = 3,
+ // Regular account with immutable code
+ RegularAccountImmutableCode = 0,
+ /// Regular account with updateable code
+ RegularAccountUpdatableCode = 1,
+}
+```
+
+**Storage Modes:**
+
+```rust
+pub enum StorageMode {
+ /// State stored onchain and publicly readable.
+ Public,
+ /// Only a commitment is onchain; full state is held privately by the owner.
+ Private,
+}
+```
+
+
+
+## Set Up Development Environment
+
+To run the code examples in this guide, you'll need to set up a development environment for either Rust or TypeScript.
+
+### Rust Environment
+
+If you already created `my-test-project` during [installation](./setup/installation#rust-project), you can reuse it. Otherwise, create a new project:
+
+```bash title=">_ Terminal"
+miden new my-project
+cd my-project/integration/
+```
+
+For each code example, create a new binary file:
+
+```bash title=">_ Terminal"
+touch src/bin/demo.rs
+```
+
+Copy the Rust code example into the file, then run:
+
+```bash title=">_ Terminal"
+cargo run --bin demo --release
+```
+
+### TypeScript Environment
+
+If you already created `miden-app` during [installation](./setup/installation#typescript-project), you can reuse it. Otherwise, scaffold a new Vite vanilla-ts project:
+
+```bash title=">_ Terminal"
+npm create vite@latest miden-app -- --template vanilla-ts
+cd miden-app
+npm install @miden-sdk/miden-sdk
+```
+
+For each code example, save the TypeScript snippet as `src/demo.ts` (overwriting the previous one as you progress):
+
+```bash title=">_ Terminal"
+touch src/demo.ts
+```
+
+Wire it into the entry point once (`src/main.ts`):
+
+```ts title="src/main.ts"
+import { demo } from "./demo";
+
+demo().catch(console.error);
+```
+
+Run the dev server:
+
+```bash title=">_ Terminal"
+npm run dev
+```
+
+Open the dev-server URL in the browser and watch the devtools console for output.
+
+:::tip
+For detailed frontend setup guidance (React, wallets, UI), see the [Tutorials section](../tutorials/).
+:::
+
+## Creating Accounts Programmatically
+
+Let's start by creating accounts using the Miden client libraries:
+
+```rust title="integration/src/bin/account.rs"
+use miden_client::{
+ account::{
+ component::{AuthScheme, AuthSingleSig, BasicWallet},
+ AccountBuilder, AccountStorageMode, AccountType,
+ },
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ rpc::{Endpoint, GrpcClient},
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use rand::RngCore;
+use std::sync::Arc;
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // Initialize RPC connection
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore =
+ Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ // Initialize client to connect with the Miden Testnet.
+ // NOTE: The client is our entry point to the Miden network.
+ // All interactions with the network go through the client.
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ client.sync_state().await?;
+
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2();
+
+ let builder = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(
+ key_pair.public_key().to_commitment(),
+ AuthScheme::Falcon512Poseidon2,
+ ))
+ .with_component(BasicWallet);
+
+ let account = builder.build()?;
+
+ client.add_account(&account, false).await?;
+
+ keystore.add_key(&key_pair, account.id()).await?;
+
+ println!("Account ID: {}", account.id());
+ println!("No assets in Vault: {:?}", account.vault().is_empty());
+
+ Ok(())
+}
+```
+
+```typescript title="src/demo.ts"
+import { MidenClient, AccountType } from "@miden-sdk/miden-sdk";
+
+export async function demo() {
+ // Initialize client to connect with the Miden Testnet.
+ // NOTE: The client is our entry point to the Miden network.
+ // All interactions with the network go through the client.
+ const client = await MidenClient.createTestnet();
+
+ // Create a new wallet account.
+ const wallet = await client.accounts.create({
+ type: AccountType.MutableWallet, // Standard wallet with upgradeable code
+ storage: "public", // Public: account state is visible onchain
+ });
+
+ console.log("Account ID:", wallet.id().toString());
+ console.log(
+ "No Assets in Vault:",
+ wallet.vault().fungibleAssets().length === 0,
+ );
+}
+```
+
+
+Expected output
+
+```text
+Account ID: 0x94733054a40d1610178320cc0c8060
+No Assets in Vault: true
+```
+
+
+
+## Creating a Token Faucet
+
+Before we can work with tokens, we need a source of tokens. Let's create a fungible token faucet:
+
+```rust title="integration/src/bin/faucet.rs"
+use miden_client::{
+ account::{
+ component::{AuthScheme, AuthSingleSig, BasicFungibleFaucet},
+ AccountBuilder, AccountStorageMode, AccountType,
+ },
+ asset::TokenSymbol,
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ rpc::{Endpoint, GrpcClient},
+ Felt,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use rand::RngCore;
+use std::sync::Arc;
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // Initialize RPC connection
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore =
+ Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ // Initialize client to connect with the Miden Testnet.
+ // NOTE: The client is our entry point to the Miden network.
+ // All interactions with the network go through the client.
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ client.sync_state().await?;
+
+ // Faucet seed
+ let mut init_seed = [0u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ // Faucet parameters
+ let symbol = TokenSymbol::new("TEST")?;
+ let decimals = 8;
+ let max_supply = Felt::new(1_000_000);
+
+ // Generate key pair
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2();
+
+ // Build the account
+ let builder = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(
+ key_pair.public_key().to_commitment(),
+ AuthScheme::Falcon512Poseidon2,
+ ))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply)?);
+
+ let faucet_account = builder.build()?;
+
+ client.add_account(&faucet_account, false).await?;
+ keystore.add_key(&key_pair, faucet_account.id()).await?;
+
+ println!("Faucet account ID: {}", faucet_account.id());
+
+ Ok(())
+}
+```
+
+```typescript title="src/demo.ts"
+import { MidenClient, AccountType } from "@miden-sdk/miden-sdk";
+
+export async function demo() {
+ // Initialize client to connect with the Miden Testnet.
+ const client = await MidenClient.createTestnet();
+
+ // Faucet parameters
+ const decimals = 8;
+ const maxSupply = 10_000_000n * 10n ** BigInt(decimals);
+
+ // Create a fungible token faucet.
+ const faucet = await client.accounts.create({
+ type: AccountType.FungibleFaucet,
+ symbol: "TEST",
+ decimals,
+ maxSupply,
+ storage: "public",
+ });
+
+ console.log("Faucet account ID:", faucet.id().toString());
+}
+```
+
+
+Expected output
+
+```text
+Faucet account ID: 0xde0ba31282f7522046d3d4af40722b
+```
+
+
+
+## Key Takeaways
+
+**Account Types:**
+
+- **Regular Accounts**: Standard smart contract wallets that can hold assets and execute custom logic
+- **Faucet Accounts**: Specialized accounts with minting permissions for tokens
+
+**Storage Modes:**
+
+- **Public**: Account state is fully transparent and visible onchain
+- **Private**: Only cryptographic commitments are stored onchain, with full state maintained privately
+
+**Modular Components:**
+
+- **BasicWallet**: Provides asset management functionality
+- **BasicFungibleFaucet**: Enables token minting capabilities
+- **AuthSingleSig**: Handles cryptographic authentication (Falcon512 or ECDSA via the `AuthScheme` enum)
+
+Now that you understand how to create accounts and faucets, you're ready to learn about Miden's unique transaction model. Continue to [Notes & Transactions](./notes) to explore how assets move between accounts using notes.
+
+---
diff --git a/versioned_docs/version-0.14/builder/get-started/index.md b/versioned_docs/version-0.14/builder/get-started/index.md
new file mode 100644
index 00000000..120fb9a1
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/index.md
@@ -0,0 +1,46 @@
+---
+sidebar_position: 0
+title: Quick Start
+description: Get started with Miden by installing Miden tools using the `midenup` toolchain, creating your first wallet, performing basic operations, and building your first smart contract!
+pagination_prev: null
+---
+
+# Quick Start
+
+Welcome to Miden! This guide gets you up and running with the Miden blockchain by walking through the essential setup and core operations.
+
+## What is Miden?
+
+Miden is a privacy-focused, ZK-based blockchain that uses an actor model where each account is a smart contract. Unlike traditional blockchains where accounts simply hold balances, Miden accounts are programmable entities that can execute custom logic, store data, and manage assets autonomously.
+
+Key concepts you'll encounter:
+
+- **Accounts**: smart contracts that hold assets and execute code
+- **Notes**: messages that exchange data and assets between accounts — also programmable
+- **Assets**: tokens that can be fungible or non-fungible
+- **Privacy**: every transaction, note, and account in Miden is private by default — only the involved parties can view asset amounts or transfer details
+
+## Getting started
+
+Follow these guides in order:
+
+
+
+ Install the Miden toolchain with `midenup`.
+
+
+ Essential Miden CLI commands — create a wallet and mint your first tokens.
+
+
+ Create and manage Miden accounts programmatically in Rust and TypeScript.
+
+
+ Miden's note-based transaction model for private asset transfers.
+
+
+ Query account storage data and interact with deployed smart contracts.
+
+
+ Build, test, and deploy a smart contract on Miden using Rust.
+
+
diff --git a/versioned_docs/version-0.14/builder/get-started/notes.md b/versioned_docs/version-0.14/builder/get-started/notes.md
new file mode 100644
index 00000000..8a189c0d
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/notes.md
@@ -0,0 +1,861 @@
+---
+sidebar_position: 3
+title: Notes & Transactions
+description: Learn Miden's unique note-based transaction model for asset transfers between accounts.
+---
+
+# Notes & Transactions
+
+Miden's transaction model is uniquely powerful, combining private asset transfers through notes with zero-knowledge proofs. Let's explore how to mint, consume, and send tokens using this innovative approach.
+
+## Understanding Miden's Transaction Model
+
+Traditional blockchains move tokens directly between account balances. Miden uses a more sophisticated **note-based system** that provides enhanced privacy and flexibility.
+
+**Think of Notes Like Sealed Envelopes:**
+
+- Alice puts 100 tokens in a sealed envelope (note) addressed to Bob
+- She posts the envelope to the public board (network)
+- Only Bob can open envelopes addressed to him
+- When Bob opens it, the 100 tokens move to his vault
+
+**Key Components:**
+
+- **Notes**: Sealed containers that carry data and assets between accounts
+- **P2ID (Pay-To-ID) Notes**: Notes addressed to a specific account ID (like Bob's address)
+- **Nullifiers**: Prevent someone from opening the same envelope twice
+- **Zero-Knowledge Proofs**: Prove transactions are valid without revealing private details
+
+## The Two-Transaction Model
+
+A core principle of Miden is that **a transaction is the state transition of a single account**. This means each transaction only modifies one account's state, which enables parallel execution and strong privacy guarantees.
+
+Miden uses a **two-transaction model** for asset transfers that provides enhanced privacy and scalability:
+
+### Transaction 1: Sender Creates Note
+
+- **Alice's account** creates a P2ID (Pay-To-ID) note containing 100 tokens
+- The note specifies **Bob** as the only valid consumer
+- Alice's balance decreases, note is available for consumption
+- Alice's transaction is complete and final
+
+### Transaction 2: Recipient Consumes Note
+
+- **Bob's client** discovers the note (addressed to his ID)
+- Bob creates a transaction to consume the note
+- Tokens move from the note into Bob's vault
+- Bob's balance increases, note is nullified
+
+### Benefits
+
+This approach provides several advantages over direct transfers:
+
+1. **Privacy**: Alice and Bob's transactions are unlinkable
+2. **Parallelization**: Multiple transactions can be processed concurrently, enabling simultaneous creation of notes.
+3. **Flexibility**: Notes can include complex conditions (time locks, multi-sig, etc.)
+4. **Scalability**: No global state synchronization required
+
+## Set Up Development Environment
+
+To run the code examples in this guide, you'll need to set up a development environment. If you haven't already, follow the setup instructions in the [Accounts](./accounts#set-up-development-environment) guide.
+
+## Minting Tokens
+
+**What is Minting?**
+Minting in Miden creates new tokens and packages them into a **P2ID note** (Pay-to-ID note) addressed to a specific account. Unlike traditional blockchains where tokens appear directly in your balance, Miden uses a two-step process:
+
+1. **Faucet mints tokens** → Creates a P2ID note containing the tokens
+2. **Recipient consumes the note** → Tokens move into their account vault
+
+**Key Concepts:**
+
+- **P2ID Note**: A note that can only be consumed by the account it's addressed to
+- **NoteType**: Determines visibility - `Public` notes are visible onchain and are stored by the Miden network, while `Private` notes are not stored by the network and must be exchanged directly between parties via other channels.
+- **FungibleAsset**: Represents tokens that can be divided and exchanged (like currencies)
+
+Let's see this in action:
+
+```rust title="integration/src/bin/mint.rs"
+use miden_client::{
+ account::{
+ component::{AuthScheme, AuthSingleSig, BasicFungibleFaucet, BasicWallet},
+ AccountBuilder, AccountStorageMode, AccountType,
+ },
+ asset::{FungibleAsset, TokenSymbol},
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::NoteType,
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ Felt,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use rand::RngCore;
+use std::sync::Arc;
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // Initialize RPC connection
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore =
+ Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ // Initialize client to connect with the Miden Testnet.
+ // NOTE: The client is our entry point to the Miden network.
+ // All interactions with the network go through the client.
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ client.sync_state().await?;
+
+ //------------------------------------------------------------
+ // CREATING A FAUCET AND MINTING TOKENS
+ //------------------------------------------------------------
+
+ // Faucet seed
+ let mut init_seed = [0u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ // Faucet parameters
+ let symbol = TokenSymbol::new("TEST")?;
+ let decimals = 8;
+ let max_supply = Felt::new(1_000_000);
+
+ // Generate key pair
+ let alice_key_pair = AuthSecretKey::new_falcon512_poseidon2();
+ let faucet_key_pair = AuthSecretKey::new_falcon512_poseidon2();
+
+ // Build the account
+ let account_builder = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(
+ alice_key_pair.public_key().to_commitment(),
+ AuthScheme::Falcon512Poseidon2,
+ ))
+ .with_component(BasicWallet);
+
+ // Build the faucet
+ let faucet_builder = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(
+ faucet_key_pair.public_key().to_commitment(),
+ AuthScheme::Falcon512Poseidon2,
+ ))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply)?);
+
+ let alice_account = account_builder.build()?;
+ let faucet_account = faucet_builder.build()?;
+
+ println!("Alice's account ID: {:?}", alice_account.id().to_hex());
+ println!("Faucet account ID: {:?}", faucet_account.id().to_hex());
+
+ // Add accounts to client
+ client.add_account(&alice_account, false).await?;
+ client.add_account(&faucet_account, false).await?;
+
+ // Add keys to keystore
+ keystore.add_key(&alice_key_pair, alice_account.id()).await?;
+ keystore.add_key(&faucet_key_pair, faucet_account.id()).await?;
+
+ let amount: u64 = 1000;
+ let fungible_asset = FungibleAsset::new(faucet_account.id(), amount)?;
+
+ // Build transaction request to mint fungible asset to Alice's account
+ // NOTE: This transaction will create a P2ID note (a Miden note containing the minted asset)
+ // for Alice's account. Alice will be able to consume these notes to get the fungible asset in her vault
+ let transaction_request = TransactionRequestBuilder::new().build_mint_fungible_asset(
+ fungible_asset,
+ alice_account.id(),
+ NoteType::Public,
+ client.rng(),
+ )?;
+
+ // Create transaction and submit it to create P2ID notes for Alice's account
+ let tx_id = client
+ .submit_new_transaction(faucet_account.id(), transaction_request)
+ .await?;
+ client.sync_state().await?;
+
+ println!(
+ "Mint transaction submitted successfully, ID: {:?}",
+ tx_id.to_hex()
+ );
+
+ Ok(())
+}
+```
+
+```typescript title="src/demo.ts"
+import { MidenClient, AccountType } from "@miden-sdk/miden-sdk";
+
+export async function demo() {
+ // Initialize client to connect with the Miden Testnet.
+ const client = await MidenClient.createTestnet();
+
+ // Creating Alice's account
+ const alice = await client.accounts.create({
+ type: AccountType.MutableWallet,
+ storage: "public", // Public: account state is visible on-chain
+ });
+ console.log("Alice's account ID:", alice.id().toString());
+
+ // Creating a faucet account
+ const decimals = 8;
+ const maxSupply = 10_000_000n * 10n ** BigInt(decimals);
+ const faucet = await client.accounts.create({
+ type: AccountType.FungibleFaucet,
+ symbol: "TEST",
+ decimals,
+ maxSupply,
+ storage: "public",
+ });
+ console.log("Faucet account ID:", faucet.id().toString());
+
+ // Mint 1000 tokens to Alice.
+ // This creates a P2ID note containing the asset; Alice consumes it
+ // to actually receive the tokens in her vault (see the next section).
+ console.log("Minting 1000 tokens to Alice...");
+ const { txId } = await client.transactions.mint({
+ account: faucet, // faucet is the executing account
+ to: alice,
+ amount: 1000n,
+ type: "public", // note visibility
+ });
+ console.log("Mint transaction submitted successfully, ID:", txId.toString());
+}
+```
+
+
+Expected output
+
+```text
+Alice's account ID: 0x5b2840a923dedc102ea67e0c1eba3c
+Faucet account ID: 0x29dd1dc628d2842032e751ed1b5da7
+Minting 1000 tokens to Alice...
+Mint transaction submitted successfully, ID: 0x7a2dbde87ea2f4d41b396d6d3f6bdb9a8d7e2a51555fa57064a1657ad70fca06
+```
+
+
+
+## Consuming Notes
+
+**Why Consume Notes?**
+After minting creates a P2ID note containing tokens, the recipient must **consume** the note to actually receive the tokens in their account vault. This two-step process provides several benefits:
+
+- **Privacy**: The mint transaction and consume transaction are unlinkable
+- **Flexibility**: Recipients can consume notes when they choose
+- **Atomic Operations**: Each step either succeeds completely or fails safely
+
+**The Process:**
+
+1. **Find consumable notes** addressed to your account
+2. **Create a consume transaction** referencing the note IDs
+3. **Submit the transaction** to move tokens into your vault
+
+Here's how to consume notes programmatically:
+
+:::tip
+This is a complete, self-contained example that includes the setup and minting steps from the previous section. **The new consume logic starts at the `CONSUMING P2ID NOTES` comment.**
+:::
+
+```rust title="integration/src/bin/consume.rs"
+use miden_client::{
+ account::{
+ component::{AuthScheme, AuthSingleSig, BasicFungibleFaucet, BasicWallet},
+ Account, AccountBuilder, AccountStorageMode, AccountType,
+ },
+ asset::{FungibleAsset, TokenSymbol},
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::NoteType,
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ Felt,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use rand::RngCore;
+use std::sync::Arc;
+use tokio::time::Duration;
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // Initialize RPC connection
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore =
+ Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ // Initialize client to connect with the Miden Testnet.
+ // NOTE: The client is our entry point to the Miden network.
+ // All interactions with the network go through the client.
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ client.sync_state().await?;
+
+ //------------------------------------------------------------
+ // CREATING A FAUCET AND MINTING TOKENS
+ //------------------------------------------------------------
+
+ // Faucet seed
+ let mut init_seed = [0u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ // Faucet parameters
+ let symbol = TokenSymbol::new("TEST")?;
+ let decimals = 8;
+ let max_supply = Felt::new(1_000_000);
+
+ // Generate key pair
+ let alice_key_pair = AuthSecretKey::new_falcon512_poseidon2();
+ let faucet_key_pair = AuthSecretKey::new_falcon512_poseidon2();
+
+ // Build the account
+ let account_builder = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(
+ alice_key_pair.public_key().to_commitment(),
+ AuthScheme::Falcon512Poseidon2,
+ ))
+ .with_component(BasicWallet);
+
+ // Build the faucet
+ let faucet_builder = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(
+ faucet_key_pair.public_key().to_commitment(),
+ AuthScheme::Falcon512Poseidon2,
+ ))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply)?);
+
+ let alice_account = account_builder.build()?;
+ let faucet_account = faucet_builder.build()?;
+
+ println!("Alice's account ID: {:?}", alice_account.id().to_hex());
+ println!("Faucet account ID: {:?}", faucet_account.id().to_hex());
+
+ // Add accounts to client
+ client.add_account(&alice_account, false).await?;
+ client.add_account(&faucet_account, false).await?;
+
+ // Add keys to keystore
+ keystore.add_key(&alice_key_pair, alice_account.id()).await?;
+ keystore.add_key(&faucet_key_pair, faucet_account.id()).await?;
+
+ let amount: u64 = 1000;
+ let fungible_asset = FungibleAsset::new(faucet_account.id(), amount)?;
+
+ // Build transaction request to mint fungible asset to Alice's account
+ // NOTE: This transaction will create a P2ID note (a Miden note containing the minted asset)
+ // for Alice's account. Alice will be able to consume these notes to get the fungible asset in her vault
+ let transaction_request = TransactionRequestBuilder::new().build_mint_fungible_asset(
+ fungible_asset,
+ alice_account.id(),
+ NoteType::Public,
+ client.rng(),
+ )?;
+
+ // Create transaction and submit it to create P2ID notes for Alice's account
+ let tx_id = client
+ .submit_new_transaction(faucet_account.id(), transaction_request)
+ .await?;
+ client.sync_state().await?;
+
+ println!(
+ "Mint transaction submitted successfully, ID: {:?}",
+ tx_id.to_hex()
+ );
+
+ //------------------------------------------------------------
+ // CONSUMING P2ID NOTES
+ //------------------------------------------------------------
+
+ // Public notes must be committed to a block before they can be consumed.
+ // Poll until the network includes our mint note in a block.
+ loop {
+ // Sync state to get the latest block
+ client.sync_state().await?;
+
+ let consumable_notes = client
+ .get_consumable_notes(Some(alice_account.id()))
+ .await?;
+
+ if consumable_notes.is_empty() {
+ println!("Waiting for P2ID note to be comitted...");
+ tokio::time::sleep(Duration::from_secs(2)).await;
+ continue;
+ }
+
+ let notes: Vec
= consumable_notes
+ .into_iter()
+ .map(|(record, _)| record.try_into().expect("Failed to convert to Note"))
+ .collect();
+
+ let consume_tx_request = TransactionRequestBuilder::new().build_consume_notes(notes)?;
+
+ // Create transaction and submit it to consume notes
+ let consume_tx_id = client
+ .submit_new_transaction(alice_account.id(), consume_tx_request)
+ .await?;
+
+ println!(
+ "Consume transaction submitted successfully, ID: {:?}",
+ consume_tx_id.to_hex()
+ );
+
+ client.sync_state().await?;
+
+ let alice_account: Account = client
+ .get_account(alice_account.id())
+ .await?
+ .ok_or_else(|| anyhow::anyhow!("Account not found"))?
+ .try_into()?;
+ let vault = alice_account.vault();
+ println!(
+ "Alice's TEST token balance: {:?}",
+ vault.get_balance(faucet_account.id())
+ );
+
+ break; // Exit the loop after consuming the note
+ }
+
+ Ok(())
+}
+```
+
+```typescript title="src/demo.ts"
+import { MidenClient, AccountType } from "@miden-sdk/miden-sdk";
+
+export async function demo() {
+ // Initialize client to connect with the Miden Testnet.
+ const client = await MidenClient.createTestnet();
+
+ // Creating Alice's account
+ const alice = await client.accounts.create({
+ type: AccountType.MutableWallet,
+ storage: "public",
+ });
+ console.log("Alice's account ID:", alice.id().toString());
+
+ // Creating a faucet account
+ const decimals = 8;
+ const maxSupply = 10_000_000n * 10n ** BigInt(decimals);
+ const faucet = await client.accounts.create({
+ type: AccountType.FungibleFaucet,
+ symbol: "TEST",
+ decimals,
+ maxSupply,
+ storage: "public",
+ });
+ console.log("Faucet account ID:", faucet.id().toString());
+
+ // Mint 1000 tokens to Alice. Creates a P2ID note that she'll consume.
+ console.log("Minting 1000 tokens to Alice...");
+ const mintResult = await client.transactions.mint({
+ account: faucet,
+ to: alice,
+ amount: 1000n,
+ type: "public",
+ waitForConfirmation: true,
+ });
+ console.log(
+ "Mint transaction submitted successfully, ID:",
+ mintResult.txId.toString(),
+ );
+
+ // List notes available to Alice and consume them — tokens move into her vault.
+ const notes = await client.notes.listAvailable({ account: alice });
+ const consumeResult = await client.transactions.consume({
+ account: alice,
+ notes: [notes[0]],
+ waitForConfirmation: true,
+ });
+ console.log(
+ "Consume transaction submitted successfully, ID:",
+ consumeResult.txId.toString(),
+ );
+
+ // Read Alice's TEST token balance directly via the accounts resource.
+ const balance = await client.accounts.getBalance(alice, faucet);
+ console.log("Alice's TEST token balance:", Number(balance));
+}
+```
+
+
+Expected output
+
+```text
+Alice's account ID: "0x5b2840a923dedc102ea67e0c1eba3c"
+Faucet account ID: "0x29dd1dc628d2842032e751ed1b5da7"
+Minting 1000 tokens to Alice...
+Mint transaction submitted successfully, ID: "0x7a2dbde87ea2f4d41b396d6d3f6bdb9a8d7e2a51555fa57064a1657ad70fca06"
+Waiting for note to be consumable...
+Consume transaction submitted successfully, ID: "0xa75872c498ee71cd6725aef9411d2559094cec1e1e89670dbf99c60bb8843481"
+Alice's TEST token balance: Ok(1000)
+```
+
+
+
+## Sending Tokens Between Accounts
+
+**How Sending Works in Miden**
+Sending tokens between accounts follows the same note-based pattern. The sender creates a new P2ID note containing tokens from their vault and addresses it to the recipient:
+
+**The Flow:**
+
+1. **Sender creates P2ID note** containing tokens and recipient's account ID
+2. **Sender submits transaction** - their balance decreases, note is published
+3. **Recipient discovers note** addressed to their account ID
+4. **Recipient consumes note** - tokens move into their vault
+
+This approach means Alice and Bob's transactions are completely separate and unlinkable, providing strong privacy guarantees.
+
+Let's implement the complete flow - mint, consume, then send:
+
+:::tip
+This is a complete, self-contained example that includes all previous steps. **The new send logic starts at the `SENDING TOKENS TO BOB` comment.**
+:::
+
+```rust title="integration/src/bin/send.rs"
+use miden_client::{
+ account::{
+ component::{AuthScheme, AuthSingleSig, BasicFungibleFaucet, BasicWallet},
+ Account, AccountBuilder, AccountId, AccountStorageMode, AccountType,
+ },
+ asset::{FungibleAsset, TokenSymbol},
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::{NoteAttachment, NoteType, P2idNote},
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ Felt,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use rand::RngCore;
+use std::sync::Arc;
+use tokio::time::Duration;
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // Initialize RPC connection
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore =
+ Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ // Initialize client to connect with the Miden Testnet.
+ // NOTE: The client is our entry point to the Miden network.
+ // All interactions with the network go through the client.
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ client.sync_state().await?;
+
+ //------------------------------------------------------------
+ // CREATING A FAUCET AND MINTING TOKENS
+ //------------------------------------------------------------
+
+ // Faucet seed
+ let mut init_seed = [0u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ // Faucet parameters
+ let symbol = TokenSymbol::new("TEST")?;
+ let decimals = 8;
+ let max_supply = Felt::new(1_000_000);
+
+ // Generate key pair
+ let alice_key_pair = AuthSecretKey::new_falcon512_poseidon2();
+ let faucet_key_pair = AuthSecretKey::new_falcon512_poseidon2();
+
+ // Build the account
+ let account_builder = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(
+ alice_key_pair.public_key().to_commitment(),
+ AuthScheme::Falcon512Poseidon2,
+ ))
+ .with_component(BasicWallet);
+
+ // Build the faucet
+ let faucet_builder = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(
+ faucet_key_pair.public_key().to_commitment(),
+ AuthScheme::Falcon512Poseidon2,
+ ))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply)?);
+
+ let alice_account = account_builder.build()?;
+ let faucet_account = faucet_builder.build()?;
+
+ println!("Alice's account ID: {:?}", alice_account.id().to_hex());
+ println!("Faucet account ID: {:?}", faucet_account.id().to_hex());
+
+ // Add accounts to client
+ client.add_account(&alice_account, false).await?;
+ client.add_account(&faucet_account, false).await?;
+
+ // Add keys to keystore
+ keystore.add_key(&alice_key_pair, alice_account.id()).await?;
+ keystore.add_key(&faucet_key_pair, faucet_account.id()).await?;
+
+ let amount: u64 = 1000;
+ let fungible_asset = FungibleAsset::new(faucet_account.id(), amount)?;
+
+ // Build transaction request to mint fungible asset to Alice's account
+ // NOTE: This transaction will create a P2ID note (a Miden note containing the minted asset)
+ // for Alice's account. Alice will be able to consume these notes to get the fungible asset in her vault
+ let transaction_request = TransactionRequestBuilder::new().build_mint_fungible_asset(
+ fungible_asset,
+ alice_account.id(),
+ NoteType::Public,
+ client.rng(),
+ )?;
+
+ // Create transaction and submit it to create P2ID notes for Alice's account
+ let tx_id = client
+ .submit_new_transaction(faucet_account.id(), transaction_request)
+ .await?;
+ client.sync_state().await?;
+
+ println!(
+ "Mint transaction submitted successfully, ID: {:?}",
+ tx_id.to_hex()
+ );
+
+ //------------------------------------------------------------
+ // CONSUMING P2ID NOTES
+ //------------------------------------------------------------
+
+ // Public notes must be committed to a block before they can be consumed.
+ // Poll until the network includes our mint note in a block.
+ loop {
+ // Sync state to get the latest block
+ client.sync_state().await?;
+
+ let consumable_notes = client
+ .get_consumable_notes(Some(alice_account.id()))
+ .await?;
+
+ if consumable_notes.is_empty() {
+ println!("Waiting for P2ID note to be comitted...");
+ tokio::time::sleep(Duration::from_secs(2)).await;
+ continue;
+ }
+
+ let notes: Vec = consumable_notes
+ .into_iter()
+ .map(|(record, _)| record.try_into().expect("Failed to convert to Note"))
+ .collect();
+
+ let consume_tx_request = TransactionRequestBuilder::new().build_consume_notes(notes)?;
+
+ // Create transaction and submit it to consume notes
+ let consume_tx_id = client
+ .submit_new_transaction(alice_account.id(), consume_tx_request)
+ .await?;
+
+ println!(
+ "Consume transaction submitted successfully, ID: {:?}",
+ consume_tx_id.to_hex()
+ );
+
+ client.sync_state().await?;
+
+ let alice_account: Account = client
+ .get_account(alice_account.id())
+ .await?
+ .ok_or_else(|| anyhow::anyhow!("Account not found"))?
+ .try_into()?;
+ let vault = alice_account.vault();
+ println!(
+ "Alice's TEST token balance: {:?}",
+ vault.get_balance(faucet_account.id())
+ );
+
+ break; // Exit the loop after consuming the note
+ }
+
+ //------------------------------------------------------------
+ // SENDING TOKENS TO BOB
+ //------------------------------------------------------------
+
+ let bob_account_id = AccountId::from_hex("0x103f8a1ad4b983104aec0412ab0b0d")?;
+ let send_amount = 100;
+ let fungible_asset_to_send = FungibleAsset::new(faucet_account.id(), send_amount)?;
+
+ let p2id_note = P2idNote::create(
+ alice_account.id(),
+ bob_account_id,
+ vec![fungible_asset_to_send.into()],
+ NoteType::Public,
+ NoteAttachment::default(),
+ client.rng(),
+ )?;
+
+ // Create transaction request to send P2ID note to Bob
+ let send_p2id_note_transaction_request = TransactionRequestBuilder::new()
+ .own_output_notes(vec![p2id_note])
+ .build()?;
+
+ // Create transaction and submit it to send P2ID note to Bob
+ let send_p2id_note_tx_id = client
+ .submit_new_transaction(alice_account.id(), send_p2id_note_transaction_request)
+ .await?;
+ client.sync_state().await?;
+
+ println!(
+ "Send 100 tokens to Bob note transaction ID: {:?}",
+ send_p2id_note_tx_id.to_hex()
+ );
+
+ Ok(())
+}
+```
+
+```typescript title="src/demo.ts"
+import { MidenClient, AccountType } from "@miden-sdk/miden-sdk";
+
+export async function demo() {
+ // Initialize client to connect with the Miden Testnet.
+ const client = await MidenClient.createTestnet();
+
+ // Create Alice's account and a faucet.
+ const alice = await client.accounts.create({
+ type: AccountType.MutableWallet,
+ storage: "public",
+ });
+ console.log("Alice's account ID:", alice.id().toString());
+
+ const decimals = 8;
+ const maxSupply = 10_000_000n * 10n ** BigInt(decimals);
+ const faucet = await client.accounts.create({
+ type: AccountType.FungibleFaucet,
+ symbol: "TEST",
+ decimals,
+ maxSupply,
+ storage: "public",
+ });
+ console.log("Faucet account ID:", faucet.id().toString());
+
+ // Mint 1000 tokens to Alice and consume the resulting P2ID note.
+ console.log("Minting 1000 tokens to Alice...");
+ const mintResult = await client.transactions.mint({
+ account: faucet,
+ to: alice,
+ amount: 1000n,
+ type: "public",
+ waitForConfirmation: true,
+ });
+ console.log(
+ "Mint transaction submitted successfully, ID:",
+ mintResult.txId.toString(),
+ );
+
+ const notes = await client.notes.listAvailable({ account: alice });
+ const consumeResult = await client.transactions.consume({
+ account: alice,
+ notes: [notes[0]],
+ waitForConfirmation: true,
+ });
+ console.log(
+ "Consume transaction submitted successfully, ID:",
+ consumeResult.txId.toString(),
+ );
+
+ const balance = await client.accounts.getBalance(alice, faucet);
+ console.log("Alice's TEST token balance:", Number(balance));
+
+ // Send 100 tokens from Alice to Bob.
+ const bobAccountId = "0x103f8a1ad4b983104aec0412ab0b0d";
+ console.log("Sending 100 tokens to Bob...");
+ const { txId } = await client.transactions.send({
+ account: alice,
+ to: bobAccountId,
+ token: faucet,
+ amount: 100n,
+ type: "public",
+ waitForConfirmation: true,
+ });
+ console.log("Send transaction submitted successfully, ID:", txId.toString());
+}
+```
+
+
+Expected output
+
+```text
+Alice's account ID: 0xd6b8bb0ed10b1610282c513501778a
+Faucet account ID: 0xe48c43d6ad6496201bcfa585a5a4b6
+Minting 1000 tokens to Alice...
+Mint transaction submitted successfully, ID: 0x948a0eef754068b3126dd3261b6b54214fa5608fb13c5e5953faf59bad79c75f
+Consume transaction submitted successfully, ID: 0xc69ab84b784120abe858bb536aebda90bd2067695f11d5da93ab0b704f39ad78
+Alice's TEST token balance: 100
+Send 100 tokens to Bob note transaction ID: "0x51ac27474ade3a54adadd50db6c2b9a2ede254c5f9137f93d7a970f0bc7d66d5"
+```
+
+
+
+## Key Takeaways
+
+**Miden's Note-Based Transaction Model:**
+
+- **Notes** enable asset transfers between accounts (both public and private)
+- **Two-transaction model** provides privacy and parallelization benefits
+- **Zero-knowledge proofs** validate transaction execution without revealing details
+- **P2ID notes** target specific recipients using their account IDs
+
+**Transaction Flow:**
+
+1. **Mint** tokens to create notes containing assets
+2. **Consume** notes to add assets to account vaults
+3. **Send** tokens using P2ID notes targeted to recipients
+4. **Nullify** consumed notes to prevent double-spending
+
+This innovative approach provides unprecedented privacy and flexibility while maintaining the security guarantees of blockchain technology. The note-based model enables scalable, private transactions that can be processed in parallel without global state synchronization.
+
+---
diff --git a/versioned_docs/version-0.14/builder/get-started/read-storage.md b/versioned_docs/version-0.14/builder/get-started/read-storage.md
new file mode 100644
index 00000000..a0ee4e1e
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/read-storage.md
@@ -0,0 +1,231 @@
+---
+sidebar_position: 4
+title: Read Storage Values
+description: Learn how to query account storage data and interact with deployed smart contracts.
+---
+
+# Read Storage Values
+
+Let's explore how to interact with public accounts and retrieve their storage data.
+
+## Understanding Account Storage
+
+Miden accounts contain several types of data you can read.
+
+**Account Components:**
+
+- **Vault**: Contains the account's assets (tokens)
+- **Storage**: Key-value data store with up to 255 slots
+- **Code**: The account's smart contract logic (MAST root)
+- **Nonce**: Nonce that increments with each state change to prevent double spend
+
+**Storage Visibility:**
+
+- **Public accounts**: All data is publicly accessible and can be read by anyone
+- **Private accounts**: Only commitments are public; full data is held privately
+
+## Set Up Development Environment
+
+To run the code examples in this guide, you'll need to set up a development environment. If you haven't already, follow the setup instructions in the [Accounts](./accounts#set-up-development-environment) guide.
+
+## Reading from a Public Smart Contract
+
+Let's interact with a counter contract deployed on the Miden testnet. This contract maintains a simple counter value in a named storage map slot.
+
+### Reading the Count of a Counter contract
+
+```rust title="integration/src/bin/read-count.rs"
+use miden_client::{
+ account::{Account, AccountId, StorageSlotName},
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{Endpoint, GrpcClient},
+ Felt, Word,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use std::sync::Arc;
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // Initialize RPC connection
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore =
+ Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ // Initialize client to connect with the Miden Testnet.
+ // NOTE: The client is our entry point to the Miden network.
+ // All interactions with the network go through the client.
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ client.sync_state().await?;
+
+ //------------------------------------------------------------
+ // READ PUBLIC STATE OF THE COUNTER ACCOUNT
+ //------------------------------------------------------------
+
+ let counter_account_id = AccountId::from_hex("0x224a96d294e10d006aef3d4f1b0876")?;
+
+ client.import_account_by_id(counter_account_id).await?;
+
+ let counter_account: Account = client
+ .get_account(counter_account_id)
+ .await?
+ .ok_or_else(|| anyhow::anyhow!("Account not found"))?
+ .try_into()?;
+
+ // Read the count from the counter account's named storage map slot
+ let slot_name = StorageSlotName::new(
+ "miden::component::miden_counter_account::count_map"
+ )?;
+ let count_key = Word::from([Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(1)]);
+ let count = counter_account
+ .storage()
+ .get_map_item(&slot_name, count_key)?;
+
+ println!("Count: {:?}", count);
+
+ Ok(())
+}
+```
+
+```typescript title="src/demo.ts"
+import { MidenClient, Word } from "@miden-sdk/miden-sdk";
+
+export async function demo() {
+ // Initialize client to connect with the Miden Testnet.
+ const client = await MidenClient.createTestnet();
+
+ const counterAccountId = "0x224a96d294e10d006aef3d4f1b0876";
+
+ // Fetch the counter account (imports it into the local store if needed).
+ const counter = await client.accounts.getOrImport(counterAccountId);
+
+ // Get the count from the counter account by querying its storage map
+ // using the named storage slot and counter key.
+ const slotName = "miden::component::miden_counter_account::count_map";
+ const counterKey = new Word(BigUint64Array.from([0n, 0n, 0n, 1n]));
+ const count = counter.storage().getMapItem(slotName, counterKey);
+
+ // The count value is a WORD (array of 4 u64 values).
+ // The 4th value is the counter number.
+ console.log("Count:", Number(count?.toU64s()[3]));
+}
+```
+
+
+Expected output
+
+```text
+Count: 1
+```
+
+
+
+## Reading Account Token Balances
+
+You can also query the assets (tokens) held by an account:
+
+```rust title="integration/src/bin/token-balance.rs"
+use miden_client::{
+ account::{Account, AccountId},
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{Endpoint, GrpcClient},
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use std::sync::Arc;
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ // Initialize RPC connection
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore =
+ Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ // Initialize client to connect with the Miden Testnet.
+ // NOTE: The client is our entry point to the Miden network.
+ // All interactions with the network go through the client.
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ client.sync_state().await?;
+
+ //------------------------------------------------------------
+ // READ TOKEN BALANCE OF AN ACCOUNT
+ //------------------------------------------------------------
+
+ let alice_account_id = AccountId::from_hex("0x5b2840a923dedc102ea67e0c1eba3c")?;
+ let faucet_account_id = AccountId::from_hex("0x29dd1dc628d2842032e751ed1b5da7")?;
+
+ client.import_account_by_id(alice_account_id).await?;
+
+ let alice_account: Account = client
+ .get_account(alice_account_id)
+ .await?
+ .ok_or_else(|| anyhow::anyhow!("Account not found"))?
+ .try_into()?;
+
+ let balance = alice_account
+ .vault()
+ .get_balance(faucet_account_id)?;
+
+ println!("Alice's TEST token balance: {:?}", balance);
+
+ Ok(())
+}
+```
+
+```typescript title="src/demo.ts"
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+export async function demo() {
+ // Initialize client to connect with the Miden Testnet.
+ const client = await MidenClient.createTestnet();
+
+ const aliceId = "0x5b2840a923dedc102ea67e0c1eba3c";
+ const faucetId = "0x29dd1dc628d2842032e751ed1b5da7";
+
+ // Fetch Alice's account (imports it into the local store if needed)
+ // and query her balance for the faucet's token.
+ await client.accounts.getOrImport(aliceId);
+ const balance = await client.accounts.getBalance(aliceId, faucetId);
+
+ console.log("Alice's TEST token balance:", Number(balance));
+}
+```
+
+
+Expected output
+
+```text
+Alice's TEST token balance: 900
+```
+
+
+
+---
diff --git a/versioned_docs/version-0.14/builder/get-started/setup/_category_.json b/versioned_docs/version-0.14/builder/get-started/setup/_category_.json
new file mode 100644
index 00000000..103ee735
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/setup/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Set Up",
+ "position": 1
+}
diff --git a/versioned_docs/version-0.14/builder/get-started/setup/cli-basics.md b/versioned_docs/version-0.14/builder/get-started/setup/cli-basics.md
new file mode 100644
index 00000000..e3736152
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/setup/cli-basics.md
@@ -0,0 +1,189 @@
+---
+sidebar_position: 2
+title: CLI Basics
+description: Learn essential Miden CLI commands to create your wallet and mint your first tokens.
+---
+
+This guide covers essential Miden CLI commands for creating accounts, minting and managing tokens. Make sure to have [installed Miden development tools](./installation.md) using the `midenup` toolchain.
+
+## Create Your First Account
+
+### Generate a New Wallet
+
+Create a new Miden wallet account:
+
+```bash title=">_ Terminal"
+miden client new-wallet
+```
+
+
+Expected output
+
+```text
+Successfully created new wallet.
+To view account details execute miden-client account --show 0x05bd1f642cd368800cc95956b2696a
+Config updated successfully
+Setting account 0x05bd1f642cd368800cc95956b2696a as the default account ID.
+You can unset it with `miden-client account --default none`.
+```
+
+
+
+This command creates a **BasicMutable** account with **private** storage mode, giving you full control while keeping your data confidential.
+
+### View Your Account
+
+List all your accounts:
+
+```bash title=">_ Terminal"
+miden client account
+```
+
+
+Expected output
+
+```text
+| Account ID | Type | Storage Mode | Nonce | Status |
+|------------|------|--------------|-------|--------|
+| 0x970e3e4dbcd09b8035532edaa87bc9 | Regular | private | 0 | New |
+```
+
+
+
+View detailed information about your account:
+
+```bash title=">_ Terminal"
+miden client account -s
+```
+
+
+Expected output
+
+```text
+Account Information
+==================
+
+| Field | Value |
+|-------------------|--------------------------------------------------------------------------|
+| Address | mtst1qztsu0jdhngfhqp42vhd42rme9cqzkzy89e |
+| Account ID (hex) | 0x970e3e4dbcd09b8035532edaa87bc9 |
+| Account Commitment| 0x404a762b9a19e70bc8752381b17f909bc0bbab02c0b4636d8923d088ac8ebc04 |
+| Type | Regular |
+| Storage mode | private |
+| Code Commitment | 0x6a11161925930dae89cc24cbddf0d161cead39b0fe88c262d4e790cff35be01d |
+| Vault Root | 0x3e128c57f6cfa0d44ab1308994171af13cb513422add28d1916b3ff254fef82d |
+| Storage Root | 0x5f95d38174f10c8ce91a0202763b0813fdcbb2714704cda411af6483ebc8d012 |
+| Nonce | 0 |
+
+Assets:
+
+| Asset Type | Faucet | Amount |
+|------------|---------|---------|
+| | | |
+
+Storage:
+
+| Item Slot Index | Item Slot Type | Value/Commitment |
+|-----------------|----------------|------------------|
+| 0 | Value | 0xa52ef6357625c54a2eaefd11b8cfc2ee3429c37d9f8a827e23886857ea284834 |
+```
+
+
+
+**Key Account Components:**
+
+- **Account ID**: Unique 120-bit identifier encoding the account type and storage mode
+- **Vault**: Secure storage for your assets
+- **Storage**: Key-value store for account data (255 slots available)
+- **Code Commitment**: Hash of the account's smart contract logic
+- **Nonce**: Counter that increments with each state change
+
+## Account Management
+
+### Switch Between Accounts
+
+If you have multiple accounts, set which one to use as default:
+
+```bash title=">_ Terminal"
+miden client account --default
+```
+
+### Deploy Your Account
+
+The `miden client new-wallet` command above already deploys your account on-chain automatically. You can verify your account is deployed by syncing and checking its status:
+
+```bash title=">_ Terminal"
+miden client sync
+miden client account
+```
+
+## Mint Your First Tokens
+
+Request tokens from the public testnet faucet:
+
+```bash title=">_ Terminal"
+miden mint --target-account --amount 1000
+```
+
+This sends a mint request to the [public testnet faucet](https://faucet-api.testnet.miden.io) and automatically consumes the resulting note, depositing the tokens into your account.
+
+Display your balance:
+
+```bash title=">_ Terminal"
+miden client sync
+miden client account -s
+```
+
+## Create a New Project
+
+If you already created a project during [installation](./installation.md) (e.g., `my-test-project`), you can continue using it. Otherwise, create a new one:
+
+**Rust Workspace:**
+
+```bash title=">_ Terminal"
+miden new my-project
+```
+
+Creates a **Rust workspace** for developing, testing, and deploying Miden smart contracts using Rust.
+
+**Vite Frontend Project:**
+
+```bash title=">_ Terminal"
+# Using Yarn
+yarn create-miden-app
+# Using NPM
+npx create-miden-app
+```
+
+Creates a minimal **Vite example project with Miden integration**, built on the standard Vite React TypeScript template.
+
+## Custom client configuration
+
+Initialize the client in your working directory when you want to test against a custom network endpoint or use different keys without touching your global config:
+
+```bash title=">_ Terminal"
+miden client init --network devnet
+```
+
+Available networks:
+
+- `testnet` - Miden's public test network
+- `devnet` - Development network
+- `localhost` - Local node for testing
+
+### Important Files Created
+
+When you manually initialize the Miden client in your working directory, several local files are created:
+
+- **`miden-client.toml`**: Configuration file with network settings
+- **`store.sqlite3`**: Database storing your account data and transaction history
+- **`keystore/`**: Directory containing your private keys (keep secure!)
+- **`templates/`**: Pre-built smart contract components
+
+:::danger
+Private keys in the `keystore/` directory are **not encrypted**. Keep these files secure and never share them.
+:::
+
+To return to your global client configuration, remove the local `miden-client.toml` (and any local store/keystore files you no longer need).
+
+---
diff --git a/versioned_docs/version-0.14/builder/get-started/setup/installation.md b/versioned_docs/version-0.14/builder/get-started/setup/installation.md
new file mode 100644
index 00000000..704aec35
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/setup/installation.md
@@ -0,0 +1,206 @@
+---
+sidebar_position: 1
+title: Installation
+description: Get started with Miden development by installing Miden tools using the `midenup` toolchain.
+---
+
+This guide walks you through installing the Miden development tools using the `midenup` toolchain manager.
+
+## Prerequisites
+
+### Install Rust
+
+Miden development requires Rust. Install it using rustup:
+
+```bash title=">_ Terminal"
+curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
+```
+
+Reload your PATH environment variable:
+
+```bash title=">_ Terminal"
+. "$HOME/.cargo/env"
+```
+
+Verify the installation:
+
+```bash title=">_ Terminal"
+rustc --version
+```
+
+
+Expected output
+
+```text
+rustc 1.92.0-nightly (fa3155a64 2025-09-30)
+```
+
+
+
+### Install Node.js & Yarn
+
+For TypeScript development with the Miden Web Client, you'll need Node.js and Yarn.
+
+**Install Node.js:**
+
+```bash title=">_ Terminal"
+# Install Node.js using the official installer or package manager
+# For macOS with Homebrew:
+brew install node
+
+# For Ubuntu/Debian:
+curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
+sudo apt-get install -y nodejs
+
+# For Windows, download from nodejs.org
+```
+
+**Install Yarn:**
+
+```bash title=">_ Terminal"
+# Install Yarn globally via npm
+npm install -g yarn
+```
+
+**Verify installations:**
+
+```bash title=">_ Terminal"
+node --version && yarn --version
+```
+
+
+Expected output
+
+```text
+v22.x.x # or higher
+1.22.x # or higher
+```
+
+
+
+### Install Miden CLI
+
+**Install midenup**
+
+The Miden toolchain installer makes it easy to manage Miden components:
+
+```bash title=">_ Terminal"
+cargo install midenup
+```
+
+:::info
+Until published to crates.io, install using: `cargo install --git https://github.com/0xMiden/midenup.git`
+:::
+
+**Initialize midenup**
+
+```bash title=">_ Terminal"
+midenup init
+```
+
+This creates the `$MIDENUP_HOME` directory and sets up the `miden` command by creating a symlink in your Cargo bin directory (`$CARGO_HOME/bin/`, typically `~/.cargo/bin/`). Since Rust users already have this directory in their PATH, no additional PATH configuration is needed.
+
+Verify it works:
+
+```bash title=">_ Terminal"
+which miden
+```
+
+
+Expected output
+
+```text
+/Users//.cargo/bin/miden # macOS
+/home//.cargo/bin/miden # Linux
+```
+
+
+
+**Install Miden Toolchain**
+
+Install the latest stable Miden components:
+
+```bash title=">_ Terminal"
+midenup install stable
+```
+
+:::note
+You may see `No artifact found. Proceeding to install from source` during installation. This is expected — it means pre-built binaries aren't available for your platform, so midenup compiles components from source. This can take 15-30 minutes.
+:::
+
+### Verify Installation
+
+Check that everything is working correctly:
+
+```bash title=">_ Terminal"
+midenup show active-toolchain
+```
+
+
+Expected output
+
+```text
+stable
+```
+
+
+
+### Troubleshooting
+
+**"miden: command not found"**
+
+Ensure `$CARGO_HOME/bin` (typically `~/.cargo/bin/`) is in your PATH. This should already be configured if you installed Rust via rustup. Verify with:
+
+```bash title=">_ Terminal"
+echo $PATH | tr ':' '\n' | grep cargo
+```
+
+**"config error: missing field" when running `miden client` commands**
+
+If you have config files from a previous Miden installation, they may be incompatible with the current version. Delete the old config and database, then re-initialize:
+
+```bash title=">_ Terminal"
+rm -f miden-client.toml store.sqlite3
+miden client init
+```
+
+## Set Up a Project
+
+The Quick Start guides let you follow along in either Rust or TypeScript. Scaffold whichever language you prefer — the two tabs in every later code example map 1:1 to the files below.
+
+### Rust Project
+
+```bash title=">_ Terminal"
+miden new my-test-project
+cd my-test-project
+```
+
+If successful, you'll see a new directory with Miden project files. For each Rust code example in the following pages, add a new binary under `integration/src/bin/` and run it with `cargo run --bin --release`.
+
+### TypeScript Project
+
+The TypeScript examples use the [`@miden-sdk/miden-sdk`](https://www.npmjs.com/package/@miden-sdk/miden-sdk) package and its `MidenClient` API. The SDK ships WebAssembly that runs in the browser, so the simplest runnable setup is a minimal Vite project:
+
+```bash title=">_ Terminal"
+npm create vite@latest miden-app -- --template vanilla-ts
+cd miden-app
+npm install @miden-sdk/miden-sdk
+```
+
+Open `src/main.ts` and replace its contents with a simple entry point that calls your demo:
+
+```ts title="src/main.ts"
+import { demo } from "./demo";
+
+demo().catch(console.error);
+```
+
+For each TypeScript snippet in the following pages, save it as `src/demo.ts` (or another name imported from `main.ts`) and run:
+
+```bash title=">_ Terminal"
+npm run dev
+```
+
+The SDK initialises WebAssembly on first use; open the Vite dev server URL in your browser and watch the devtools console for output.
+
+---
diff --git a/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/_category_.json b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/_category_.json
new file mode 100644
index 00000000..9041f571
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Your First Smart Contract",
+ "position": 5
+}
diff --git a/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/create.md b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/create.md
new file mode 100644
index 00000000..c930bde7
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/create.md
@@ -0,0 +1,265 @@
+---
+sidebar_position: 2
+title: Create Your Project
+description: Set up a new Miden project and understand the counter contract implementation.
+---
+
+In this section, you'll set up a new Miden project and understand the structure and implementation of both the counter account contract and increment note script.
+
+## Setting Up Your Project
+
+Create a new Miden project using the CLI:
+
+```bash title=">_ Terminal"
+miden new counter-project
+cd counter-project
+```
+
+This creates a workspace with the following structure:
+
+```text
+counter-project/
+├── contracts/ # Each contract as individual crate
+│ ├── counter-account/ # Example: Counter account contract
+│ └── increment-note/ # Example: Increment note contract
+├── integration/ # Integration crate (scripts + tests)
+│ ├── src/
+│ │ ├── bin/ # Rust binaries for on-chain interactions
+│ │ ├── lib.rs
+│ │ └── helpers.rs # Temporary helper file
+│ └── tests/ # Test files
+├── Cargo.toml # Workspace root
+└── rust-toolchain.toml # Rust toolchain specification
+```
+
+The project follows Miden's design philosophy of clean separation:
+
+- **`contracts/`**: Your primary working directory for writing Miden smart contract code
+- **`integration/`**: All on-chain interactions, deployment scripts, and tests
+
+Each contract is organized as its own individual crate, providing independent versioning, dependencies, and clear isolation between different contracts.
+
+## Building Your Contracts
+
+You can build individual contracts by navigating to their directory and running the Miden build command:
+
+```bash title=">_ Terminal"
+# Build the counter account contract
+cd contracts/counter-account
+miden build
+
+# Build the increment note contract
+cd ../increment-note
+miden build
+```
+
+This compiles the Rust contract code into a Miden package (`.masp` file), making it ready for deployment and interaction.
+
+## Understanding the Counter Account Contract
+
+Let's examine the counter account contract that comes with the project template. Open `contracts/counter-account/src/lib.rs`:
+
+```rust title="contracts/counter-account/src/lib.rs"
+// Do not link against libstd (i.e. anything defined in `std::`)
+#![no_std]
+#![feature(alloc_error_handler)]
+
+// However, we could still use some standard library types while
+// remaining no-std compatible, if we uncommented the following lines:
+//
+// extern crate alloc;
+
+use miden::{component, felt, Felt, StorageMap, StorageMapAccess, Word};
+
+/// Main contract structure for the counter example.
+#[component]
+struct CounterContract {
+ /// Storage map holding the counter value.
+ #[storage(description = "counter contract storage map")]
+ count_map: StorageMap,
+}
+
+#[component]
+impl CounterContract {
+ /// Returns the current counter value stored in the contract's storage map.
+ pub fn get_count(&self) -> Felt {
+ // Define a fixed key for the counter value within the map
+ let key = Word::from_u64_unchecked(0, 0, 0, 1);
+ // Read the value associated with the key from the storage map
+ self.count_map.get(&key)
+ }
+
+ /// Increments the counter value stored in the contract's storage map by one.
+ pub fn increment_count(&mut self) -> Felt {
+ // Define the same fixed key
+ let key = Word::from_u64_unchecked(0, 0, 0, 1);
+ // Read the current value
+ let current_value: Felt = self.count_map.get(&key);
+ // Increment the value by one
+ let new_value = current_value + felt!(1);
+ // Write the new value back to the storage map
+ self.count_map.set(key, new_value);
+ new_value
+ }
+}
+```
+
+### Counter Contract Walkthrough
+
+#### No-std Environment
+
+```rust
+#![no_std]
+```
+
+Miden contracts run in a `no_std` environment, meaning they don't link against Rust's standard library. This is essential for blockchain execution where contracts need to be deterministic and lightweight.
+
+#### Miden Library Imports
+
+```rust
+use miden::{component, felt, Felt, StorageMap, StorageMapAccess, Word};
+```
+
+These imports provide:
+
+- **`component`**: Macro for defining contract components
+- **`felt`**: Macro for creating `Felt` literals (e.g., `felt!(1)`)
+- **`Felt`/`Word`**: Miden's native field element and word types
+- **`StorageMap`**: Key-value storage within account storage slots
+- **`StorageMapAccess`**: Needed for reading storage values (`get_count` function)
+
+:::note[`felt` vs `Felt`]
+`Felt` is the field element type representing values in the Goldilocks prime field (p = 2^64 - 2^32 + 1). `felt!(1)` is a compile-time macro that creates `Felt` values from integer literals with compile-time range validation. Currently `felt!` only accepts values up to 2^32 (compiler limitation); for larger values use `Felt::from_u64_unchecked()`.
+:::
+
+#### Contract Structure Definition
+
+```rust
+#[component]
+struct CounterContract {
+ /// Storage map holding the counter value.
+ #[storage(description = "counter contract storage map")]
+ count_map: StorageMap,
+}
+```
+
+The `#[component]` attribute marks this as a Miden [Account component](/core-concepts/protocol/account). The `count_map` field is a `StorageMap` stored in a named storage slot of the account. In v0.13, storage slots are identified by name rather than explicit index numbers — the slot name is derived automatically from the component's package name and field name (e.g., `miden::component::miden_counter_account::count_map`).
+
+**Important**: Storage slots in Miden hold `Word` values, which are composed of four field elements (`Felt`). Each `Felt` is a 64-bit unsigned integer (u64). The `StorageMap` provides a key-value interface within a single storage slot, allowing you to store multiple key-value pairs within the four-element word structure.
+
+#### Contract Implementation
+
+```rust
+impl CounterContract {
+ // Function implementations...
+}
+```
+
+The `CounterContract` implementation defines the external interface that other contracts and notes can call. This is the contract's public API.
+
+#### Storage Key Strategy
+
+```rust
+let key = Word::from_u64_unchecked(0, 0, 0, 1);
+```
+
+Both functions in the counter contract use the same fixed key `[0, 0, 0, 1]` to store and retrieve the counter value within the storage map. The `Word::from_u64_unchecked` constructor creates a `Word` from four `u64` values. This demonstrates a simple but effective storage pattern.
+
+## Understanding the Increment Note Script
+
+Now let's examine the increment note script at `contracts/increment-note/src/lib.rs`:
+
+```rust title="contracts/increment-note/src/lib.rs"
+// Do not link against libstd (i.e. anything defined in `std::`)
+#![no_std]
+#![feature(alloc_error_handler)]
+
+// However, we could still use some standard library types while
+// remaining no-std compatible, if we uncommented the following lines:
+//
+// extern crate alloc;
+// use alloc::vec::Vec;
+
+use miden::*;
+
+use crate::bindings::miden::counter_account::counter_account;
+
+#[note]
+struct IncrementNote;
+
+#[note]
+impl IncrementNote {
+ #[note_script]
+ fn run(self, _arg: Word) {
+ let initial_value = counter_account::get_count();
+ counter_account::increment_count();
+ let expected_value = initial_value + Felt::from_u32(1);
+ let final_value = counter_account::get_count();
+ assert_eq(final_value, expected_value);
+ }
+}
+```
+
+### Increment Note Script Walkthrough
+
+#### No-std Setup
+
+Similar to the account contract, the note script uses `#![no_std]` with the same allocator and panic handler setup.
+
+#### Miden Imports
+
+```rust
+use miden::*;
+
+use crate::bindings::miden::counter_account::counter_account;
+```
+
+The wildcard import brings in all Miden note script functionality. The `counter_account` binding imports the interface functions from the counter contract, allowing the note script to call them.
+
+#### Note Script Structure
+
+Note scripts use a struct-based pattern. The `#[note]` attribute on both the struct and `impl` block marks this as a Miden note script component:
+
+```rust
+#[note]
+struct IncrementNote;
+
+#[note]
+impl IncrementNote {
+ #[note_script]
+ fn run(self, _arg: Word) { ... }
+}
+```
+
+The struct definition (`IncrementNote`) provides a named type for the note script. Unlike account contracts, note scripts don't store persistent data — the struct serves as the entry point container.
+
+Learn more about [note scripts in the Miden documentation](/core-concepts/protocol/note/).
+
+#### The Note Script Function
+
+```rust
+#[note_script]
+fn run(self, _arg: Word) {
+ let initial_value = counter_account::get_count();
+ counter_account::increment_count();
+ let expected_value = initial_value + Felt::from_u32(1);
+ let final_value = counter_account::get_count();
+ assert_eq(final_value, expected_value);
+}
+```
+
+The `#[note_script]` attribute marks this method as the entry point for note execution. The `self` parameter is required for methods in the `impl` block. The function:
+
+1. **Gets the initial counter value** using the imported `counter_account::get_count()` function
+2. **Calls increment_count()** to increment the counter on the target account
+3. **Verifies the operation succeeded** by checking the final value matches expectations
+
+This demonstrates how note scripts interact with account contracts through their public interfaces, calling functions to change state.
+
+The counter example demonstrates a complete interaction pattern: the account contract manages persistent state, while the note script provides a mechanism to trigger state changes through note consumption.
+
+## Next Steps
+
+Now that you understand the contract code structure, let's move on to [deploying your contract](./deploy) and learn how the integration folder enables interaction with your contracts on the Miden network.
+
+---
diff --git a/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/deploy.md b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/deploy.md
new file mode 100644
index 00000000..f4f9da9a
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/deploy.md
@@ -0,0 +1,253 @@
+---
+sidebar_position: 3
+title: Deploy Your Contract
+description: Learn about the integration folder and deploy your counter contract to the Miden testnet.
+---
+
+# Deploy Your Contract
+
+In this section, you'll learn about how to deploy and interact with your counter contract using the included "increment-count" scripts.
+
+## Understanding the Integration Folder
+
+The `integration/` folder is a crucial part of your Miden project workspace. It serves as the command center for all interactions with your smart contracts. Let's explore its structure and purpose.
+
+Navigate to your project's integration folder:
+
+```bash title=">_ Terminal"
+cd integration
+ls -la
+```
+
+You'll see a structure like:
+
+```text
+integration/
+├── Cargo.toml # Integration crate configuration
+├── src/
+│ ├── bin/ # Executable scripts for onchain interactions
+│ │ └── increment_count.rs # Script to deploy and increment counter
+│ ├── helpers.rs # Temporary helper file
+│ └── lib.rs # Exports helpers
+└── tests/ # Integration tests
+ └── counter_test.rs # Tests for counter contract
+```
+
+## Purpose of the Integration Folder
+
+The integration folder serves two essential functions in Miden development:
+
+### 1. Contract Interaction Scripts (Binary Executables)
+
+Think of the scripts in `src/bin/` as Miden's equivalent to [**Foundry scripts**](https://getfoundry.sh/guides/scripting-with-solidity). These are executable Rust binaries that handle all your contract interactions:
+
+- **Contract Deployment**: Scripts that create and deploy accounts to the network
+- **Function/Procedure Calls**: Scripts that interact with deployed contracts through notes or [transaction scripts](/core-concepts/protocol/transaction#transaction-lifecycle)
+- **State Queries**: Scripts that read contract state from the network
+- **Operations**: Scripts for contract upgrades, configuration changes, etc.
+
+Each binary is designed to handle a specific task.
+
+### 2. Testing Infrastructure
+
+All testing logic for your smart contracts lives here:
+
+- **Integration Tests**: End-to-end tests that verify contract behavior on Testnet
+- **Mockchain Tests**: Local testing using Miden's testing framework
+
+This separation ensures your contract logic in `contracts/` remains clean and focused while all interaction complexity is managed in the integration layer.
+
+## The Increment Count Script
+
+Let's examine the `increment_count.rs` script located at `integration/src/bin/increment_count.rs`. This script demonstrates the complete lifecycle of deploying and interacting with your counter contract.
+
+The script performs these key operations:
+
+1. **Sets up a Miden client** connected to the testnet
+2. **Builds both contract packages** (counter account and increment note)
+3. **Creates the counter account** with initial storage configuration
+4. **Creates a sender account** for publishing notes
+5. **Creates and publishes the increment note**
+6. **Consumes the note** to trigger the counter increment
+
+### Running the Script
+
+Execute the increment script to deploy your contract:
+
+```bash title=">_ Terminal"
+cd integration
+cargo run --bin increment_count --release
+```
+
+
+Expected Output
+
+```text
+Account ID: V0(AccountIdV0 { prefix: 14134910893364381952, suffix: 3644349760121494784 })
+Sender account ID: "0xd85b347218c5a80052dbd47b2f36ad"
+Counter note hash: "0xf0e821396a896eb9983e682bc056021d57ddcaa43082f34597bf9e026421e566"
+Note publish transaction ID: "0xc6f080855724402cadf26650ffe993fe97a127a8f6c9c82ec621960e936e6d732
+Consume transaction ID: "0x2d1d8510e546ce0fbc22fa7d1a82322259d73cd1d7e0ca86622d0be70fab0548"
+Account delta: AccountDelta { account_id: V0(AccountIdV0 { prefix: 7255964780328958976, suffix: 2724050564200846336 }), storage: AccountStorageDelta { values: {}, maps: {0: StorageMapDelta({LexicographicWord(Word([0, 0, 0, 1])): Word([0, 0, 0, 1])})} }, vault: AccountVaultDelta { fungible: FungibleAssetDelta({}), non_fungible: NonFungibleAssetDelta({}) }, nonce_delta: 1 }
+```
+
+
+
+Congratulations, you have successfully deployed the Counter Contract to the Miden Testnet, and incremented its count by one! You can verify your transaction on [MidenScan](https://testnet.midenscan.com) by searching for your transaction ID.
+
+### What Happens During Execution
+
+The script demonstrates Miden's deployment flow:
+
+1. **Contract Building**: The script compiles both the counter account and increment note contracts
+2. **Account Creation**: Creates a counter account with initial storage (counter value = 0)
+3. **Note Publishing**: Creates an increment note and publishes it to the network
+4. **Note Consumption**: The counter account consumes the note, executing the increment logic
+5. **State Update**: The counter value increases and the change is recorded onchain
+
+This process shows how Miden contracts are deployed through state changes rather than separate deployment transactions.
+
+**Miden's Deployment Flow**: In Miden, accounts (contracts) become visible onchain only when they undergo a state change. Simply creating an account locally doesn't deploy it - the account must participate in a transaction that modifies its state. In our case, by incrementing the counter, we're effectively "deploying" the contract and making it visible on the Miden testnet explorer. This is why the increment operation serves both as the deployment and the first interaction with the contract.
+
+## How the Scripts Work
+
+The integration scripts work by connecting to the Miden client and then building contracts from the Miden package files. These package files are generated when you run `miden build` inside each contract directory, but the scripts handle this compilation step automatically - you don't need to manually build the contracts before running the scripts.
+
+Next, we look into how the scripts convert your Rust contract code into deployable Miden contracts.
+
+## Script Breakdown
+
+Let's examine key parts of the increment script:
+
+### Client Setup
+
+```rust
+let ClientSetup { mut client, keystore } = setup_client().await?;
+let sync_summary = client.sync_state().await?;
+```
+
+This establishes a connection to the Miden testnet and synchronizes with the latest network state.
+
+### Building Contracts from Source
+
+The first step is building the Rust contracts into Miden packages:
+
+```rust
+// Build the counter account contract from source
+let counter_package = Arc::new(
+ build_project_in_dir(Path::new("../contracts/counter-account"), true)
+ .context("Failed to build counter account contract")?
+);
+
+// Build the increment note script from source
+let note_package = Arc::new(
+ build_project_in_dir(Path::new("../contracts/increment-note"), true)
+ .context("Failed to build increment note contract")?
+);
+```
+
+The `build_project_in_dir()` function:
+
+- Takes the path to your contract's Rust source code
+- Compiles the Rust code into a Miden package (`.masp` file)
+- Generates a package containing the compiled contract bytecode and metadata
+- This is equivalent to manually running `miden build` in each contract directory
+
+These packages contain all the information needed to deploy and interact with your contracts on the Miden network.
+
+### Converting Packages to Deployable Accounts
+
+Once we have the compiled packages, we convert them into deployable accounts and notes:
+
+```rust
+// Configure initial storage for the counter account
+let count_storage_key = Word::from([Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(1)]);
+let initial_count = Word::from([Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(0)]);
+
+// The slot name is constructed as:
+// `miden::component::[to_underscore(Cargo.toml:package.metadata.component.package)]::[field_name]`
+let counter_storage_slot =
+ StorageSlotName::new("miden::component::miden_counter_account::count_map").unwrap();
+let storage_slots = vec![StorageSlot::with_map(
+ counter_storage_slot.clone(),
+ StorageMap::with_entries([(count_storage_key, initial_count)]).unwrap(),
+)];
+let counter_cfg = AccountCreationConfig {
+ storage_slots,
+ ..Default::default()
+};
+
+// Convert the counter package into a deployable account
+let counter_account = create_account_from_package(
+ &mut client,
+ counter_package.clone(),
+ counter_cfg
+)
+.await
+.context("Failed to create counter account")?;
+```
+
+The `create_account_from_package()` function:
+
+- Takes the compiled contract package
+- Combines it with the provided configuration (storage, settings, etc.)
+- Creates a deployable Miden account that can be used in transactions
+
+**Important**: Accounts that use storage must have their storage slots specified when instantiating the account. In v0.13, storage slots are identified by name rather than index. The slot name follows the pattern `miden::component::::`. We define the storage configuration with:
+
+- A named `StorageMap` slot (`miden::component::miden_counter_account::count_map`)
+- The counter key `[0, 0, 0, 1]` with initial value `[0, 0, 0, 0]` (representing count = 0)
+
+This pre-initialization ensures the account's storage is properly configured before deployment.
+
+### Converting Packages to Executable Notes
+
+Similarly, we convert the note package into an executable note:
+
+```rust
+// Convert the increment note package into an executable note
+let counter_note = create_note_from_package(
+ &mut client,
+ note_package.clone(),
+ sender_account.id(),
+ NoteCreationConfig::default()
+)
+.context("Failed to create counter note from package")?;
+
+// Publish the note to the network
+let note_publish_request = TransactionRequestBuilder::new()
+ .own_output_notes(vec![OutputNote::Full(counter_note.clone())])
+ .build()
+ .context("Failed to build note publish transaction request")?;
+```
+
+The `create_note_from_package()` function:
+
+- Takes the compiled note script package
+- Combines it with the sender account ID and configuration
+- Creates an executable note containing the increment script logic
+- The note can then be published to the network and consumed by the target (counter) account
+
+This demonstrates the complete workflow: Rust source code → compiled packages → deployable accounts/notes → network transactions.
+
+### Note Consumption
+
+```rust
+let consume_note_request = TransactionRequestBuilder::new()
+ .input_notes([(counter_note.clone(), None)])
+ .build()
+ .context("Failed to build consume note transaction request")?;
+
+let consume_tx_id = client
+ .submit_new_transaction(counter_account.id(), consume_note_request)
+ .await
+ .context("Failed to create consume note transaction")?;
+```
+
+The counter account consumes the increment note, executing the note script which calls the counter's increment function.
+
+## Next Steps
+
+Congratulations! You've successfully deployed and interacted with your first Miden smart contract. The integration folder provides the foundation for managing all aspects of your contract lifecycle.
+
+This completes the core smart contract development workflow on Miden. You're now equipped to build and deploy your own smart contracts using these patterns and tools!
diff --git a/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/index.md b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/index.md
new file mode 100644
index 00000000..826cec03
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/index.md
@@ -0,0 +1,71 @@
+---
+sidebar_position: 1
+title: Your First Smart Contract
+description: Learn to build, test, and deploy smart contracts on Miden using Rust.
+---
+
+# Your First Smart Contract
+
+Welcome to the **Your First Smart Contract** guide! This tutorial will walk you through building, testing, and deploying your first Miden smart contract using Rust.
+
+## What You'll Learn
+
+By the end of this tutorial, you will have:
+
+- **Set up a Miden project** with the proper workspace structure
+- **Written and understood** a counter smart contract and increment note
+- **Deployed your contract** to the Miden testnet using integration scripts
+- **Mastered the fundamentals** of Miden's account-based smart contract model
+
+This guide focuses on practical, hands-on learning. You'll work with real code and deploy to a live network, giving you everything needed to start building on Miden.
+
+## What We'll Build
+
+You'll create a **counter contract system** consisting of:
+
+- **Counter Account**: A smart contract that stores and manages a counter value in its storage
+- **Increment Note**: A note script that increments the counter when executed
+- **Integration Scripts**: Deployment and interaction scripts for managing the contract lifecycle
+
+The counter example is designed to teach core Miden concepts through a simple, understandable use case that you can extend for your own projects.
+
+## Prerequisites
+
+Before starting this guide, ensure you have completed the [Installation](../setup/installation) tutorial and have:
+
+- **Rust toolchain** installed and configured
+- **midenup toolchain** installed with Miden CLI tools
+
+:::tip Prerequisites Required
+You need those development tools installed for this guide. If you haven't set up your environment yet, please complete the [installation](../setup/installation) guide first.
+:::
+
+## No Prior Experience Required
+
+This tutorial is designed for developers new to Miden. You don't need prior experience with:
+
+- Miden's account model or note-based transactions
+- Smart contract development on Miden
+- The specifics of Rust development for blockchain
+
+We'll explain these concepts as we encounter them in the tutorial.
+
+## Getting Help
+
+If you get stuck during this tutorial:
+
+- Check the rest of the Miden Docs for detailed technical references
+- Join the [Build On Miden](https://t.me/BuildOnMiden) Telegram community for support
+- Review the code examples in your project's `contracts` and `integration/` folder
+
+## Guide Structure
+
+This tutorial is divided into focused sections:
+
+1. **[Create Your Project](./create)** - Set up your workspace and understand the counter contract code
+2. **[Deploy Your Contract](./deploy)** - Learn the integration folder and deploy to testnet
+3. **[Test Your Contract](./test)** - Learn how to effectively test your contracts
+
+Each section builds on the previous one, so we recommend following them in order.
+
+Ready to build your first Miden smart contract? Let's get started!
diff --git a/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/test.md b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/test.md
new file mode 100644
index 00000000..961c0637
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/get-started/your-first-smart-contract/test.md
@@ -0,0 +1,310 @@
+---
+sidebar_position: 4
+title: Test Your Contract
+description: Learn how to write and run tests for your Miden smart contracts using the integration testing framework.
+---
+
+# Test Your Contract
+
+In this final section, you'll learn how to test your counter contract using Miden's **Mockchain** - a purpose-built testing framework that enables fast, local testing without network dependencies.
+
+## Test Structure and Organization
+
+All tests for your smart contracts should be placed in the `integration/tests/` folder. This follows the same separation of concerns we've seen throughout the project:
+
+- **`contracts/`**: Contains your contract source code
+- **`integration/src/bin/`**: Contains deployment and interaction scripts
+- **`integration/tests/`**: Contains all test files for your contracts
+
+This structure keeps your contract logic clean while providing a dedicated space for comprehensive testing.
+
+## Local Testing with Mockchain
+
+For most testing scenarios, we use Miden's **Mockchain** - a local, mocked blockchain instance specifically designed for testing. While you can also create tests that use the Miden client for end-to-end testing and on-chain interactions, the Mockchain provides the best developer experience for unit and integration testing.
+
+### What is the Mockchain?
+
+The Mockchain is Miden's purpose-built testing framework that provides several key advantages over testing against a live network:
+
+- **Blazing Fast Tests**: Run tests locally without network latency or external dependencies
+- **Full State Control**: Manipulate blockchain state precisely to create specific test scenarios
+- **Simpler Code**: Cleaner, more focused test logic without network complexity
+- **Deterministic Results**: Consistent test outcomes independent of network conditions
+- **Debugging Capabilities**: Detailed inspection of transaction execution and state changes
+
+This makes testing faster, more reliable, and easier to debug than testing against the testnet.
+
+## Running the Tests
+
+Execute your tests from the integration directory using the standard Cargo test command:
+
+```bash title="Terminal"
+cd integration
+cargo test --release
+```
+
+You should see output confirming the test passes:
+
+```text title="Expected Output"
+running 1 test
+Test passed!
+test counter_test ... ok
+
+test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+```
+
+## Understanding the Mockchain Test
+
+Your project includes a comprehensive test file at `integration/tests/counter_test.rs` that demonstrates how to test the counter contract using the Mockchain. Let's walk through this test to understand the testing patterns:
+
+
+Test File
+
+```rust title="integration/tests/counter_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, StorageMapKey, StorageSlot, StorageSlotName},
+ auth::AuthSchemeId,
+ transaction::RawOutputNote,
+ Word,
+};
+use miden_testing::{Auth, MockChain};
+use std::{path::Path, sync::Arc};
+
+#[tokio::test]
+async fn counter_test() -> anyhow::Result<()> {
+ // Test that after executing the increment note, the counter value is incremented by 1
+ let mut builder = MockChain::builder();
+
+ // Create note sender account
+ let sender = builder.add_existing_wallet(Auth::BasicAuth {
+ auth_scheme: AuthSchemeId::Falcon512Poseidon2,
+ })?;
+
+ // Build contracts
+ let contract_package = Arc::new(build_project_in_dir(
+ Path::new("../contracts/counter-account"),
+ true,
+ )?);
+ let note_package = Arc::new(build_project_in_dir(
+ Path::new("../contracts/increment-note"),
+ true,
+ )?);
+
+ // Create the counter account with initial storage and no-auth auth component
+ let count_storage_key = Word::from([0u32, 0, 0, 1]);
+ let initial_count = Word::default();
+
+ // The slot name is constructed as
+ // `miden::component::[to_underscore(Cargo.toml:package.metadata.component.package)]::[field_name]`
+ let counter_storage_slot =
+ StorageSlotName::new("miden::component::miden_counter_account::count_map").unwrap();
+ let storage_slots = vec![StorageSlot::with_map(
+ counter_storage_slot.clone(),
+ StorageMap::with_entries([(StorageMapKey::new(count_storage_key), initial_count)]).unwrap(),
+ )];
+ let counter_cfg = AccountCreationConfig {
+ storage_slots,
+ ..Default::default()
+ };
+
+ // create testing counter account
+ let mut counter_account =
+ create_testing_account_from_package(contract_package.clone(), counter_cfg).await?;
+
+ // create testing increment note
+ let counter_note = create_testing_note_from_package(
+ note_package.clone(),
+ sender.id(),
+ NoteCreationConfig::default(),
+ )?;
+
+ // add counter account and note to mockchain
+ builder.add_account(counter_account.clone())?;
+ builder.add_output_note(RawOutputNote::Full(counter_note.clone()));
+
+ // Build the mock chain
+ let mut mock_chain = builder.build()?;
+ // Build the transaction context
+ let tx_context = mock_chain
+ .build_tx_context(counter_account.id(), &[counter_note.id()], &[])?
+ .build()?;
+
+ // Execute the transaction
+ let executed_transaction = tx_context.execute().await?;
+
+ // Apply the account delta to the counter account
+ counter_account.apply_delta(executed_transaction.account_delta())?;
+
+ // Add the executed transaction to the mockchain
+ mock_chain.add_pending_executed_transaction(&executed_transaction)?;
+ mock_chain.prove_next_block()?;
+
+ // Get the count from the updated counter account
+ let count = counter_account
+ .storage()
+ .get_map_item(&counter_storage_slot, count_storage_key)
+ .expect("Failed to get counter value from storage slot");
+
+ // Assert that the count value is equal to 1 after executing the transaction
+ assert_eq!(
+ count,
+ Word::from([0u32, 0, 0, 1]),
+ "Count value is not equal to 1"
+ );
+
+ println!("Test passed!");
+ Ok(())
+}
+```
+
+
+
+## Test Code Walkthrough
+
+Let's break down this test step by step to understand how Mockchain testing works.
+
+### 1. Setting Up the Mockchain Builder
+
+```rust
+let mut builder = MockChain::builder();
+let sender = builder.add_existing_wallet(Auth::BasicAuth {
+ auth_scheme: AuthSchemeId::Falcon512Poseidon2,
+})?;
+```
+
+**What's happening:**
+
+- We instantiate the **Mockchain builder**, which is used to configure our testing environment
+- We create a **sender account** using basic authentication - this account will publish the increment note
+- The builder pattern allows us to incrementally add all the components needed for our test
+
+### 2. Building the Contract Packages
+
+```rust
+let contract_package = Arc::new(build_project_in_dir(
+ Path::new("../contracts/counter-account"),
+ true,
+)?);
+let note_package = Arc::new(build_project_in_dir(
+ Path::new("../contracts/increment-note"),
+ true,
+)?);
+```
+
+**What's happening:**
+
+- Just like in the deployment script, we **build both contract packages** (counter account and increment note)
+- The `build_project_in_dir()` function compiles the Rust contracts into Miden packages
+- We wrap them in `Arc` for efficient memory sharing across the test
+
+### 3. Creating the Test Account and Note
+
+```rust
+// Create the counter account with initial storage and no-auth auth component
+let count_storage_key = Word::from([0u32, 0, 0, 1]);
+let initial_count = Word::default();
+
+let counter_storage_slot =
+ StorageSlotName::new("miden::component::miden_counter_account::count_map").unwrap();
+let storage_slots = vec![StorageSlot::with_map(
+ counter_storage_slot.clone(),
+ StorageMap::with_entries([(StorageMapKey::new(count_storage_key), initial_count)]).unwrap(),
+)];
+let counter_cfg = AccountCreationConfig {
+ storage_slots,
+ ..Default::default()
+};
+
+// Create testing entities
+let mut counter_account = create_testing_account_from_package(contract_package.clone(), counter_cfg).await?;
+let counter_note = create_testing_note_from_package(
+ note_package.clone(),
+ sender.id(),
+ NoteCreationConfig::default(),
+)?;
+```
+
+**What's happening:**
+
+- We configure the **counter account's initial storage** with count = 0 at storage key `[0, 0, 0, 1]`
+- We create the **testing counter account** from the compiled package using `create_testing_account_from_package()`
+- We create the **testing increment note** using `create_testing_note_from_package()`
+- These helper functions create test-specific versions optimized for the Mockchain environment
+
+### 4. Adding Components to the Mockchain
+
+```rust
+builder.add_account(counter_account.clone())?;
+builder.add_output_note(RawOutputNote::Full(counter_note.clone()));
+let mut mock_chain = builder.build()?;
+```
+
+**What's happening:**
+
+- We **add the counter account** to the mockchain builder
+- We **add the increment note** as a full output note to the mockchain
+- We **build the mockchain** - now we have a complete testing environment ready to use
+
+### 5. Creating and Executing the Transaction
+
+```rust
+let tx_context = mock_chain
+ .build_tx_context(counter_account.id(), &[counter_note.id()], &[])?
+ .build()?;
+
+let executed_transaction = tx_context.execute().await?;
+```
+
+**What's happening:**
+
+- We **build the transaction context** using the counter account and counter note
+- We **execute the transaction** - this runs the increment logic locally in the mockchain
+
+### 6. Verifying the Results
+
+```rust
+// Apply the account delta to the counter account
+counter_account.apply_delta(executed_transaction.account_delta())?;
+
+// Add the executed transaction to the mockchain
+mock_chain.add_pending_executed_transaction(&executed_transaction)?;
+mock_chain.prove_next_block()?;
+
+// Get the count from the updated counter account
+let count = counter_account
+ .storage()
+ .get_map_item(&counter_storage_slot, count_storage_key)
+ .expect("Failed to get counter value from storage slot");
+
+// Assert that the count value is equal to 1 after executing the transaction
+assert_eq!(
+ count,
+ Word::from([0u32, 0, 0, 1]),
+ "Count value is not equal to 1"
+);
+```
+
+**What's happening:**
+
+- We **apply the account delta** from the executed transaction to the counter account to update its state
+- We **add the executed transaction** to the mockchain
+- We **read the counter value** from storage using the same key we initialized
+- We **assert that the count equals 1** - verifying the increment operation worked correctly
+
+The test verifies the complete flow: the increment note successfully increments the counter from 0 to 1, proving our smart contract works as expected.
+
+## Next Steps
+
+Congratulations! You've successfully completed the Miden smart contract quick start guide. You're now equipped to build more sophisticated smart contracts on Miden. Consider exploring:
+
+To deepen your knowledge, we recommend exploring the following resources:
+
+- Visit the [Tutorials section](../../tutorials/) for detailed, hands-on guides on topics such as contract interactions, advanced storage, custom note scripting, and integrating with external applications.
+- For in-depth technical explanations of core concepts, consult the [Core Concepts section](../../../core-concepts/) of the documentation. Here you'll find comprehensive information on Miden's architecture, account model, transaction lifecycle, and the underlying zero-knowledge technology that powers the network.
+
+The foundational patterns and concepts you've practiced in this Quick Start will enable you to build complex, privacy-preserving applications on the Miden network. Continue with the resources above to take your development further!
diff --git a/versioned_docs/version-0.14/builder/glossary.md b/versioned_docs/version-0.14/builder/glossary.md
new file mode 100644
index 00000000..36fdbaf1
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/glossary.md
@@ -0,0 +1,140 @@
+---
+title: Glossary
+description: "Key terms and definitions used throughout the Miden docs — grouped by area (accounts, notes, protocol, Guardian, cryptography)."
+pagination_next: null
+---
+
+# Glossary
+
+Key terms and definitions used throughout the Miden docs. Grouped thematically — if you're not sure where something lives, use your browser's find (⌘F / Ctrl+F).
+
+
+
+ Account, AccountCode, AccountComponent, AccountId, AccountStorage, MultiSig, AccountBuilder.
+
+
+ Note, Note script, Note tag, Note ID, Nullifier, Asset, AssetVault.
+
+
+ Block, Batch, Kernel, Prover, Miden Assembly, Felt, Word.
+
+
+ Miden Guardian, Canonicalization, Delta, Delta Proposal, Threshold Signature.
+
+
+
+## Accounts
+
+### Account
+
+An account is a data structure that represents an entity (user account, smart contract) on the Miden blockchain — analogous to smart contracts.
+
+### Account builder
+
+Account builder provides a structured way to create and initialize new accounts on the Miden network with specific properties, permissions, and initial state.
+
+### AccountCode
+
+The executable code associated with an account.
+
+### AccountComponent
+
+A modular unit of code representing a piece of an account's functionality. Each `AccountCode` is composed of multiple `AccountComponent`s.
+
+### AccountId
+
+A value that uniquely identifies each account on Miden.
+
+### AccountIdVersion
+
+Represents the different versions of account identifier formats supported by Miden.
+
+### AccountStorage
+
+A key-value store associated with an account. Made up of storage slots.
+
+### MultiSig
+
+A multi-signature account on Miden that requires a configurable threshold (N-of-M) of authorized signers to approve transactions before execution. MultiSig workflows are coordinated through [Miden Guardian](./miden-guardian/).
+
+## Notes & assets
+
+### Note
+
+A fundamental data structure that represents an off-chain asset or a piece of information that can be transferred between accounts. Miden's UTXO-like model is designed around notes. **Output notes** are new notes created by a transaction; **input notes** are those consumed (spent) by a transaction.
+
+### Note script
+
+A program that defines the rules and conditions under which a note can be consumed.
+
+### Note tag
+
+An identifier or metadata associated with notes that provides additional filtering capabilities.
+
+### Note ID
+
+A unique identifier assigned to each note to distinguish it from other notes.
+
+### Nullifier
+
+A cryptographic commitment that marks a note as spent, preventing it from being consumed again.
+
+### Asset
+
+A digital resource with value that can be owned, transferred, and managed within the Miden blockchain.
+
+### AssetVault
+
+The container used for managing assets within accounts. Provides a way to store and transfer assets associated with each account.
+
+## Protocol & VM
+
+### Block
+
+A fundamental data structure that groups multiple batches together and forms the blockchain's state.
+
+### Batch
+
+A collection of transactions grouped together, to be aggregated into blocks — improves network throughput.
+
+### Kernel
+
+A fundamental module of the Miden VM that acts as a base layer, providing core functionality and security guarantees for the protocol.
+
+### Prover
+
+Responsible for generating zero-knowledge proofs that attest to the correctness of program execution without revealing the underlying data.
+
+### Miden Assembly
+
+An assembly language specifically designed for the Miden VM — a low-level language with specialized instructions optimized for zero-knowledge proof generation.
+
+### Felt
+
+A Felt (Field Element) is the primitive cryptographic data type used by the Miden VM. It represents an element in the finite (Goldilocks) field: `p = 2^64 − 2^32 + 1`.
+
+### Word
+
+A data structure that represents the basic unit of computation and storage in Miden. Composed of four `Felt`s.
+
+## Guardian & multisig
+
+### Miden Guardian
+
+Infrastructure built by OpenZeppelin for managing private account state on Miden. Guardian provides a server and client SDKs for backing up, syncing, and coordinating state across devices and parties without trust assumptions. See the [Miden Guardian documentation](./miden-guardian/).
+
+### Canonicalization
+
+The background process by which [Miden Guardian](./miden-guardian/) promotes candidate deltas to canonical status by verifying them against the Miden network.
+
+### Delta
+
+A Delta represents the changes between two states `s` and `s'`. Applying a Delta `d` to `s` produces `s'`.
+
+### Delta Proposal
+
+A coordination mechanism in [Miden Guardian](./miden-guardian/) that allows multiple signers to propose, review, and co-sign state changes before they are promoted to a canonical delta.
+
+### Threshold Signature
+
+A cryptographic scheme where a minimum number of signers (the threshold) out of a total group must sign for a transaction to be valid. Used in Miden's MultiSig accounts.
diff --git a/versioned_docs/version-0.14/builder/index.md b/versioned_docs/version-0.14/builder/index.md
new file mode 100644
index 00000000..a8abbed2
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/index.md
@@ -0,0 +1,83 @@
+---
+sidebar_label: Introduction
+sidebar_position: 0
+pagination_next: null
+---
+
+# Build on Miden
+
+Accounts, notes, and transactions — authored in Rust, compiled to MASM, proved client-side.
+
+## Start here
+
+
+
+ Install midenup, create a wallet, and send your first transaction — in under ten minutes.
+
+
+ Walk through writing, proving, and deploying a counter contract in Rust.
+
+
+
+## Build
+
+
+
+ Accounts, notes, storage, components, transactions — the full Rust SDK surface.
+
+
+ Real-world examples: the Miden Bank, private multisig, custom note scripts.
+
+
+ Testing, debugging, and common pitfalls when writing Miden programs.
+
+
+ Rust, Web, and React SDKs · playground · block explorer · CLI.
+
+
+
+## Ship
+
+
+
+ Breaking changes, renames, and new features across accounts, notes, transactions, MASM, and the client.
+
+
+ Backup, sync, and coordinate private account state across devices.
+
+
+ Multi-party threshold signature workflows built on Miden.
+
+
+
+## Reference
+
+
+
+ Frequently asked questions about Miden.
+
+
+ Key terms and definitions used throughout the docs.
+
+
+
+## Community
+
+- [Telegram](https://t.me/BuildOnMiden) — technical discussion
+- [GitHub](https://github.com/0xMiden) — source code
+- [Roadmap](https://miden.xyz/roadmap) — what's coming next
+
+import SectionLinks from '@site/src/components/SectionLinks';
+
+
+
+---
+
+Licensed under the [MIT License](http://opensource.org/licenses/MIT).
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/_category_.json b/versioned_docs/version-0.14/builder/miden-guardian/_category_.json
new file mode 100644
index 00000000..12835209
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Guardian",
+ "position": 4
+}
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/_category_.json b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/_category_.json
new file mode 100644
index 00000000..722a5299
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Core Concepts",
+ "position": 1
+}
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/architecture.md b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/architecture.md
new file mode 100644
index 00000000..ad632e1f
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/architecture.md
@@ -0,0 +1,110 @@
+---
+title: Architecture
+sidebar_position: 1
+---
+
+# Architecture
+
+Guardian sits between Miden clients and the Miden network, providing an off-chain coordination layer for private account state.
+
+## System overview
+
+```mermaid
+graph LR
+ A["Miden Client A (Desktop)"] <-->|"gRPC / HTTP"| Guardian["Guardian Server"]
+ B["Miden Client B (Mobile)"] <-->|"gRPC / HTTP"| Guardian
+ Guardian <-->|"Validates state"| Node["Miden Node"]
+```
+
+- **Miden Client** handles transaction execution, proving, and local state management.
+- **Guardian Server** stores state snapshots and deltas, authenticates requests, validates changes against the network, and coordinates multi-party workflows.
+- **Miden Node** is the network's RPC endpoint that Guardian validates state against.
+
+Each account is independently configured on Guardian with its own authentication policy and storage. Clients interact with Guardian through either gRPC or HTTP — both interfaces expose the same semantics.
+
+## End-to-end transaction flow
+
+Transactions proceed through a step-by-step process to ensure consistency and verifiability:
+
+```mermaid
+sequenceDiagram
+ participant Client as Miden Client
+ participant Guardian as Guardian Server
+ participant Chain as Miden Network
+
+ Client->>Client: 1. Execute transaction locally Generate delta
+ Client->>Guardian: 2. Submit delta for acknowledgment
+ Guardian->>Guardian: 3. Validate delta against policies
+ Guardian->>Guardian: Co-sign as "candidate"
+ Guardian-->>Client: Return ack signature
+ Client->>Chain: 4. Submit ZK proof + state update
+ Chain-->>Guardian: 5. Guardian monitors on-chain commitment
+ alt Commitment matches candidate
+ Guardian->>Guardian: Promote to "canonical"
+ Guardian-->>Client: Propagate confirmed delta
+ else Commitment mismatch
+ Guardian->>Guardian: Mark as "discarded"
+ Guardian-->>Client: Signal resync needed
+ end
+```
+
+1. **Local execution**: The user computes a transaction locally, generating a delta (state change).
+2. **Delta submission**: The user sends the delta to Guardian for acknowledgment.
+3. **Guardian acknowledgment**: Guardian validates the delta and co-signs it, designating it as a "candidate" state.
+4. **Proof submission**: The user generates the ZK proof and submits it to the chain.
+5. **Canonical confirmation**: Guardian monitors the chain. If the on-chain commitment matches the candidate, the state becomes "canonical" and is propagated to other devices or signers.
+
+## Multi-device sync
+
+For users with multiple devices, Guardian keeps state synchronized seamlessly:
+
+```mermaid
+sequenceDiagram
+ participant Desktop as Desktop
+ participant Guardian as Guardian Server
+ participant Mobile as Mobile
+
+ Desktop->>Desktop: Execute transaction
+ Desktop->>Guardian: Push delta
+ Guardian->>Guardian: Validate & acknowledge
+ Desktop->>Desktop: Submit proof to chain
+ Guardian->>Guardian: Confirm canonical
+ Mobile->>Guardian: Get state
+ Guardian-->>Mobile: Return latest state
+ Mobile->>Mobile: Replay delta locally
+ Note over Mobile: State now matches Desktop
+```
+
+The desktop executes a transaction and pushes the delta to Guardian. After on-chain confirmation, Guardian propagates the canonical delta to the mobile device, which replays it locally — all without querying the chain directly.
+
+## Account management
+
+Accounts are configured with per-account authentication based on public keys (commitments). During setup, Guardian records which keys are authorized to manage the account.
+
+For each request, the client signs a payload with one of those keys and the server verifies the signature against the account's authorized keys. See [Components](./components.md) for details on the auth model.
+
+## Canonicalization
+
+Canonicalization is the process of validating that a state transition (delta) is valid against the on-chain commitment. It is optional and mainly used in multi-user setups.
+
+```mermaid
+stateDiagram-v2
+ [*] --> candidate : push_delta
+ candidate --> canonical : On-chain commitment matches
+ candidate --> discarded : On-chain commitment mismatch
+ canonical --> [*]
+ discarded --> [*]
+```
+
+- **Candidate mode** (default): A background worker promotes or discards deltas after a configurable delay and network verification.
+- **Optimistic mode**: Deltas become canonical immediately, skipping the verification window.
+
+| Parameter | Default | Description |
+|---|---|---|
+| `delay_seconds` | 900 (15 min) | How long a candidate waits before the worker checks it. |
+| `check_interval_seconds` | 60 (1 min) | How often the worker runs. |
+
+## Common use cases
+
+- **Single-user accounts**: Back up and sync state securely. If a device is lost, recover state from Guardian.
+- **Multi-user accounts**: Coordinate state and transactions between participants. Guardian helps keep everyone on the latest canonical state.
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/components.md b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/components.md
new file mode 100644
index 00000000..c14f9bf2
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/components.md
@@ -0,0 +1,120 @@
+---
+title: Components
+sidebar_position: 3
+---
+
+# Components
+
+The Guardian server is composed of several pluggable components that handle different responsibilities.
+
+## API
+
+The API exposes a consistent interface for operating on states and deltas over HTTP and gRPC. Behavior is identical across transports, so clients can switch between them without semantic changes.
+
+**HTTP endpoints** (default port 3000):
+
+| Method | Path | Description |
+|---|---|---|
+| `POST` | `/configure` | Create account with auth policy and initial state |
+| `POST` | `/delta` | Push a delta (server validates, signs, sets status) |
+| `GET` | `/delta?account_id&nonce` | Fetch delta by nonce |
+| `GET` | `/delta/since?account_id&from_nonce` | Merged canonical snapshot since a nonce |
+| `GET` | `/state?account_id` | Latest account state |
+| `POST` | `/delta/proposal` | Create pending proposal for multi-party signing |
+| `GET` | `/delta/proposal?account_id` | List pending proposals |
+| `PUT` | `/delta/proposal` | Append cosigner signature to a proposal |
+| `GET` | `/pubkey` | Server acknowledgment public key (unauthenticated) |
+
+**gRPC** (default port 50051) mirrors all HTTP endpoints with the same semantics. Credentials are provided via metadata headers.
+
+For the full API specification, see the [spec/api.md](https://github.com/OpenZeppelin/guardian/blob/main/spec/api.md) in the repository.
+
+## Auth
+
+Request authentication is configured per account. All endpoints except `/pubkey` require authentication.
+
+### Falcon RPO
+
+The current authentication policy uses Miden Falcon RPO signatures with an allowlist of **cosigner commitments** — hashes of authorized public keys.
+
+Every authenticated request includes three headers:
+
+| Header | Description |
+|---|---|
+| `x-pubkey` | Signer's public key (full serialized key or 32-byte commitment hex) |
+| `x-signature` | Falcon RPO signature over the request digest |
+| `x-timestamp` | Unix timestamp in milliseconds |
+
+The signature is computed over:
+
+```
+RPO256_hash([account_id_prefix, account_id_suffix, timestamp_ms, 0])
+```
+
+### Verification flow
+
+```mermaid
+sequenceDiagram
+ participant Client
+ participant Guardian as Guardian Server
+
+ Client->>Guardian: Request + x-pubkey, x-signature, x-timestamp
+
+ Guardian->>Guardian: 1. Derive commitment from public key
+ Guardian->>Guardian: 2. Check commitment is in account allowlist
+ Guardian->>Guardian: 3. Verify timestamp within 300s window
+ Guardian->>Guardian: 4. Check timestamp > last_auth_timestamp
+ Guardian->>Guardian: 5. Verify Falcon signature over (account_id, timestamp) digest
+
+ alt All checks pass
+ Guardian->>Guardian: Update last_auth_timestamp (CAS)
+ Guardian-->>Client: Process request
+ else Any check fails
+ Guardian-->>Client: 400 AuthenticationFailed
+ end
+```
+
+### Replay protection
+
+Guardian prevents replay attacks through two mechanisms:
+
+1. **Timestamp window**: The signed timestamp must be within **300 seconds** (5 minutes) of the server's current time.
+2. **Monotonic timestamps**: Each request's timestamp must be strictly greater than the account's `last_auth_timestamp`, enforced atomically via compare-and-swap.
+
+## Acknowledger
+
+The Acknowledger produces tamper-evident acknowledgments for accepted deltas:
+
+- Signs the digest of `new_commitment` and returns the signature as `ack_sig`.
+- The server's acknowledgment key is exposed via the `/pubkey` endpoint for clients to cache and verify against.
+
+Clients should verify `ack_sig` after every `push_delta` to confirm the server processed the change correctly.
+
+## Network
+
+The Network component handles interactions with the Miden blockchain:
+
+- Computes commitments and validates deltas against the target network's rules.
+- Validates account identifiers and request credentials against network-owned state.
+- Merges multiple deltas into a single snapshot payload (for `get_delta_since`).
+- Surfaces suggested auth updates (e.g., rotated cosigner commitments) so metadata remains aligned with the network.
+
+## Storage
+
+Storage persists account snapshots, deltas, and delta proposals:
+
+- Provides retrieval by account and nonce, plus range queries for canonicalization.
+- Stores pending delta proposals in a per-account namespace keyed by proposal commitment.
+- Backends are pluggable without altering API semantics.
+
+Available backends:
+- **Filesystem** (default): Stores data on disk. Suitable for **testing and development**. No external dependencies.
+- **PostgreSQL** (optional): Recommended for **production** deployments. Requires the `postgres` feature flag at build time. Migrations run automatically on startup.
+
+## Metadata
+
+The Metadata store holds per-account configuration:
+
+- `account_id`, authentication policy, storage backend type, timestamps, and `last_auth_timestamp` for replay protection.
+- Supports CRUD operations and list iteration over accounts.
+- Can use filesystem or PostgreSQL as its backing store, independent of the Storage backend choice.
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/data-structures.md b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/data-structures.md
new file mode 100644
index 00000000..0ba0e185
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/data-structures.md
@@ -0,0 +1,120 @@
+---
+title: Data Structures
+sidebar_position: 2
+---
+
+# Data Structures
+
+Guardian models account state as an append-only chain of snapshots and changes.
+
+## State
+
+A **state** is a canonical snapshot of an account at a point in time. It includes the account's ID, commitment, nonce, vault (assets), storage, and other account data.
+
+```json
+{
+ "account_id": "0xabc123...",
+ "commitment": "0xdef456...",
+ "nonce": 10,
+ "assets": [
+ { "balance": 12000, "asset_id": "USDC" },
+ { "balance": 2, "asset_id": "ETH" }
+ ]
+}
+```
+
+When you first register an account with Guardian, you provide an **initial state** — the baseline from which all subsequent changes are tracked.
+
+## Delta
+
+A **delta** represents a set of changes applied to a state. Deltas are append-only — each delta references the commitment of the state it was applied to, forming an unbroken chain.
+
+```json
+{
+ "account_id": "0xabc123...",
+ "nonce": 11,
+ "prev_commitment": "0xdef456...",
+ "delta_payload": {
+ "data": ""
+ }
+}
+```
+
+A useful mental model: a delta is a compact, replayable description of "what changed" in an account's local state. Deltas can sync, back up, and reconstruct state without shipping full snapshots.
+
+Key properties:
+
+- **Ordered**: Each delta has a nonce that determines its position in the chain.
+- **Linked**: The `prev_commitment` field references the state the delta was applied to. This prevents forks — if two deltas reference different base states, the server rejects the conflicting one.
+- **Validated**: The server verifies each delta against the Miden network before accepting it.
+- **Acknowledged**: Once accepted, the server signs the delta's `new_commitment`, providing cryptographic proof that it was processed.
+
+### Delta status lifecycle
+
+Each delta goes through a state machine:
+
+```mermaid
+stateDiagram-v2
+ [*] --> candidate : push_delta
+ candidate --> canonical : On-chain commitment matches
+ candidate --> discarded : On-chain commitment mismatch
+ canonical --> [*]
+ discarded --> [*]
+```
+
+| Status | Meaning |
+|---|---|
+| `candidate` | Accepted by Guardian but not yet verified on-chain. Awaiting canonicalization. |
+| `canonical` | Verified against the network and permanently recorded. |
+| `discarded` | Failed on-chain verification. Removed from the active delta chain. |
+
+In **optimistic mode**, deltas skip the `candidate` stage and are immediately marked `canonical`.
+
+## Commitments
+
+A **commitment** is a cryptographic hash that uniquely identifies a particular version of an account's state. Commitments are the integrity backbone of Guardian:
+
+```mermaid
+graph LR
+ S0["State₀ commitment₀"] -->|"Delta₁ prev: commitment₀"| S1["State₁ commitment₁"]
+ S1 -->|"Delta₂ prev: commitment₁"| S2["State₂ commitment₂"]
+ S2 -->|"Delta₃ prev: commitment₂"| S3["State₃ commitment₃"]
+```
+
+- Each state snapshot has a commitment.
+- Each delta includes a `prev_commitment` (the base state) and produces a `new_commitment` (the resulting state).
+- The chain ensures that any tampering — inserting, reordering, or dropping deltas — is detectable by any client that tracks commitments.
+
+## Delta proposals
+
+A **delta proposal** is a coordination mechanism for multi-party accounts. When multiple signers must agree on a transaction:
+
+1. **Propose**: One signer creates a delta proposal containing a `TransactionSummary`. Guardian validates the proposal against the current account state.
+2. **Sign**: Other authorized cosigners fetch the pending proposal, verify it locally, and submit their signatures.
+3. **Execute**: Once enough signatures are collected (meeting the threshold), any cosigner can promote the proposal to a canonical delta via `push_delta`.
+
+```mermaid
+sequenceDiagram
+ participant P as Proposer
+ participant Guardian as Guardian Server
+ participant C as Cosigner
+
+ P->>Guardian: push_delta_proposal (tx_summary + initial signature)
+ Guardian->>Guardian: Validate against current state
+ Guardian-->>P: Return proposal with commitment
+
+ C->>Guardian: get_delta_proposals
+ Guardian-->>C: Return pending proposals
+
+ C->>C: Verify proposal details locally
+ C->>Guardian: sign_delta_proposal (commitment + signature)
+ Guardian-->>C: Updated proposal with signatures
+
+ P->>Guardian: push_delta (with all collected signatures)
+ Guardian->>Guardian: Validate & acknowledge
+ Guardian-->>P: Canonical delta
+```
+
+Proposals remain in `pending` status until promoted. Once the corresponding delta becomes canonical, the proposal is automatically cleaned up.
+
+Delta proposals have their own commitment, derived from `(account_id, nonce, tx_summary)`, used as a stable identifier.
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/security.md b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/security.md
new file mode 100644
index 00000000..dc55b0d6
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/core-concepts/security.md
@@ -0,0 +1,101 @@
+---
+title: Security
+sidebar_position: 4
+---
+
+# Edge Cases & Security Considerations
+
+Guardian is designed around a clear trust model with well-defined security boundaries.
+
+## Trust model
+
+The right threat model for Guardian is **honest-but-curious**: the server is expected to follow the protocol and availability guarantees, but may try to learn as much as it can from any data it is given.
+
+Guardian is non-custodial. The provider holds no keys that can unilaterally move funds.
+
+### What Guardian can do
+
+- **Store and relay** state snapshots and deltas.
+- **Validate** deltas against the Miden network before acknowledging them.
+- **Co-sign** transactions as one party in a threshold scheme.
+- **Enforce policies** (rate limits, timelocks) at the co-signing layer.
+
+### What Guardian cannot do
+
+- **Forge state**: Every delta references the previous commitment. Inserting, reordering, or dropping deltas breaks the commitment chain, detectable by any client.
+- **Move funds unilaterally**: Guardian holds at most one key in a multi-key setup. It always needs the user's key to complete a transaction.
+- **Tamper silently**: The server signs each accepted delta with its acknowledgment key. Clients can verify these signatures to detect any tampering.
+
+### What Guardian can do adversarially
+
+- **Deny service**: The server can refuse to serve data or accept deltas. This is a liveness issue, not a safety issue — users can recover using their own keys.
+- **Withhold updates**: The server could delay propagating deltas to other devices. Clients should verify state freshness against on-chain commitments.
+
+## Integrity guarantees
+
+### Commitment chain
+
+Every delta includes a `prev_commitment` referencing the base state. This creates an unbroken chain:
+
+- If the server drops a delta, subsequent deltas won't validate because their `prev_commitment` won't match.
+- If the server inserts a fake delta, the commitment chain diverges from the on-chain state.
+- Clients can verify the chain independently by tracking commitments locally.
+
+### Server acknowledgment
+
+After accepting a delta, the server signs the `new_commitment` with its acknowledgment key. Clients should:
+
+1. Retrieve the server's public key via `/pubkey`.
+2. Verify `ack_sig` on every `push_delta` response.
+3. Alert on verification failure — it indicates the server may have been compromised or is not processing deltas correctly.
+
+## 2-of-3 key setup
+
+A common Guardian configuration uses a **2-of-3** threshold embedded in the account's authentication code:
+
+| Key | Holder | Purpose |
+|---|---|---|
+| **Key 1** | User hot key | Daily transactions |
+| **Key 2** | User cold key | Recovery and emergency override |
+| **Key 3** | Guardian service key | Co-signing and policy enforcement |
+
+```mermaid
+graph TD
+ subgraph "Normal operation (Hot + Guardian)"
+ Hot["User Hot Key"] --> TX["Transaction"]
+ GuardianKey["Guardian Service Key"] --> TX
+ end
+
+ subgraph "Emergency override (Hot + Cold)"
+ Hot2["User Hot Key"] --> Override["Rotate Guardian / Adjust policies Switch providers"]
+ Cold["User Cold Key"] --> Override
+ end
+```
+
+- **Normal operations**: Hot key + Guardian's co-signature suffice. Guardian verifies the signer is working from the latest state.
+- **Emergency override**: Hot + cold keys alone can rotate out Guardian, adjust policies, or switch providers.
+- **Recovery**: If the Guardian provider disappears, the user's hot + cold keys provide full independent control.
+
+## Device recovery
+
+**Without Guardian**: A lost device means falling back to a cold backup. Any state changes since the last checkpoint are lost. If an attacker has the device PIN, funds may be at risk.
+
+**With Guardian**: The remaining device already has the latest state (synced through Guardian). The user initiates a hot key rotation using their cold key. The stolen device's keys become invalid. Recovery takes minutes.
+
+## Edge cases
+
+### State divergence
+
+If two devices submit deltas referencing different base states, Guardian rejects the conflicting one (commitment mismatch). The rejected device must resync from Guardian before retrying.
+
+### Stale candidates
+
+If a candidate delta's on-chain commitment doesn't match during canonicalization, it is marked `discarded`. Clients are signaled to resync. This is not a rollback of the chain — it prevents clients from drifting onto an invalid local branch.
+
+### Clock skew
+
+Authentication requires timestamps within a 300-second window. Devices with significantly drifting clocks will fail authentication. Ensure NTP synchronization on client devices.
+
+### Provider rotation
+
+Users can switch Guardian providers at any time using their hot + cold keys. The new provider is configured with the account's current state and a fresh cosigner allowlist. The old provider's key is rotated out of the account's authentication policy.
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/index.md b/versioned_docs/version-0.14/builder/miden-guardian/index.md
new file mode 100644
index 00000000..84052546
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/index.md
@@ -0,0 +1,56 @@
+---
+title: Miden Guardian
+sidebar_position: 0
+---
+
+# Miden Guardian
+
+Miden Guardian is a system built by [OpenZeppelin](https://www.openzeppelin.com/) that allows Miden accounts to back up and sync their private state securely without trust assumptions about other participants or the server operator.
+
+## The problem
+
+Miden's execution model requires clients to manage their own private state — accounts, notes, storage — locally on-device. While this provides strong privacy and scalability, it introduces real challenges:
+
+- **Solo-account users** risk losing access if local state is not backed up. Losing any part of the account state means losing access to the account itself.
+- **Shared-account users** risk having stale state due to a faulty or malicious participant withholding updates.
+- **Multi-device users** need all devices to see the same account state, but there is no public ledger to read from.
+
+On a public chain, the ledger is a universally readable source of truth — every device and every signer can independently observe the latest state. In Miden's private account model, the canonical state is defined by the on-chain commitment, but it isn't readable in a way that keeps devices and signers automatically up to date. The coordination surface moves off-chain.
+
+## What Guardian provides
+
+Guardian addresses these challenges by acting as an off-chain coordination layer:
+
+- **Backup and recovery** — Account state is stored on Guardian, recoverable even if a device is lost.
+- **Multi-device sync** — Multiple devices push and pull state through Guardian, staying in sync with the latest canonical state.
+- **Multi-party coordination** — Shared accounts use delta proposals to coordinate threshold signing across participants.
+- **Integrity verification** — Every state change is validated against the Miden network and acknowledged with a cryptographic signature.
+
+Guardian is non-custodial. The provider cannot move funds unilaterally — it stores state and coordinates changes, but users retain cryptographic control over their accounts at all times.
+
+## Learn more
+
+
+
+ How Guardian fits between clients and the Miden network.
+
+
+ State, deltas, commitments, and delta proposals.
+
+
+ API, authentication, storage, and other server components.
+
+
+ Trust model, integrity guarantees, and edge cases.
+
+
+ How to run, deploy, and troubleshoot a Guardian server.
+
+
+ Multi-party threshold signature workflows powered by Guardian.
+
+
+
+## Repository
+
+- [Miden Guardian](https://github.com/OpenZeppelin/guardian) — Guardian server, client SDKs, multisig client libraries, and specification
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/_category_.json b/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/_category_.json
new file mode 100644
index 00000000..4608c549
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Operator Guide",
+ "position": 2
+}
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/deployment.md b/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/deployment.md
new file mode 100644
index 00000000..9031e37b
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/deployment.md
@@ -0,0 +1,97 @@
+---
+title: Deployment
+sidebar_position: 2
+---
+
+# How to Deploy
+
+This page covers Guardian server configuration for production deployments.
+
+## Environment variables
+
+| Variable | Default | Description |
+|---|---|---|
+| `DATABASE_URL` | — | PostgreSQL connection URL (required for Postgres backend) |
+| `PSM_KEYSTORE_PATH` | `/var/psm/keystore` | Path for cryptographic key storage |
+| `PSM_STORAGE_PATH` | — | Storage backend path (states and deltas) |
+| `PSM_METADATA_PATH` | — | Metadata store path |
+| `PSM_ENV` | `dev` | Environment mode |
+| `RUST_LOG` | `info` | Log level (`debug`, `info`, `warn`, `error`) |
+
+### Rate limiting
+
+| Variable | Default | Description |
+|---|---|---|
+| `PSM_RATE_BURST_PER_SEC` | `10` | Max requests per second (burst) |
+| `PSM_RATE_PER_MIN` | `60` | Max requests per minute (sustained) |
+
+Rate limits are applied per client IP, with enhanced keying when `x-pubkey` or `account_id` is present. Exceeded limits return `429 Too Many Requests` with a `Retry-After` header.
+
+### Request size limits
+
+| Variable | Default | Description |
+|---|---|---|
+| `PSM_MAX_REQUEST_BYTES` | `1048576` (1 MB) | Maximum request body size |
+
+Requests exceeding this limit receive `413 Payload Too Large`.
+
+## Storage backends
+
+Guardian uses a single storage backend per instance.
+
+### Filesystem (default)
+
+Stores state and deltas on disk. No external dependencies. Used when the binary is built without the `postgres` feature.
+
+Ensure `PSM_STORAGE_PATH` points to a writable directory with sufficient disk space.
+
+### PostgreSQL (optional)
+
+Requires the `postgres` feature flag at build time. Migrations run automatically on startup.
+
+```bash
+DATABASE_URL=postgres://psm:password@localhost:5432/psm \
+ cargo run --features postgres --package guardian-server
+```
+
+## Metadata store
+
+The metadata store can be configured independently from the storage backend. It supports both filesystem and PostgreSQL backends.
+
+Ensure `PSM_METADATA_PATH` points to a writable directory (filesystem mode) or configure `DATABASE_URL` (Postgres mode).
+
+## Logging
+
+The server uses structured logging via the `tracing` crate.
+
+```bash
+# Debug level for entire server
+RUST_LOG=debug cargo run --package guardian-server
+
+# Trace only canonicalization jobs
+RUST_LOG=server::jobs::canonicalization=trace cargo run
+
+# Multiple modules
+RUST_LOG=server::jobs=debug,server::services=info cargo run
+```
+
+## Canonicalization configuration
+
+When canonicalization is enabled (default), configure the verification window:
+
+| Parameter | Default | Description |
+|---|---|---|
+| `delay_seconds` | 900 (15 min) | How long a candidate waits before verification |
+| `check_interval_seconds` | 60 (1 min) | How often the canonicalization worker runs |
+
+Set to **optimistic mode** to skip the verification window and mark deltas canonical immediately.
+
+## Reproducible builds
+
+The server binary supports reproducible builds. Building from the same source code and target architecture produces bit-for-bit identical binaries:
+
+```bash
+./crates/server/tests/verify-build-hash.sh
+```
+
+This is useful for verifying published binaries match the source code.
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/running.md b/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/running.md
new file mode 100644
index 00000000..b3e58122
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/running.md
@@ -0,0 +1,86 @@
+---
+title: Running
+sidebar_position: 1
+---
+
+# How to Run
+
+This guide covers running a Guardian server locally for development or testing.
+
+## Prerequisites
+
+- [Docker](https://docs.docker.com/get-docker/) and Docker Compose (recommended), or
+- Rust toolchain 1.90+ (to build from source)
+
+## Docker Compose (recommended)
+
+The repository includes a Docker Compose configuration with PostgreSQL:
+
+```bash
+git clone https://github.com/OpenZeppelin/guardian.git
+cd guardian
+docker-compose up -d
+```
+
+This starts:
+
+| Service | Port | Description |
+|---|---|---|
+| Guardian HTTP API | `localhost:3000` | REST endpoints |
+| Guardian gRPC API | `localhost:50051` | gRPC service |
+| PostgreSQL | `localhost:5432` | Metadata and state storage |
+
+View logs:
+
+```bash
+docker-compose logs -f
+```
+
+Stop services:
+
+```bash
+docker-compose down
+```
+
+## Building from source
+
+```bash
+git clone https://github.com/OpenZeppelin/guardian.git
+cd guardian
+
+# With filesystem storage (default)
+cargo build --release --bin server
+cargo run --release --bin server
+
+# With PostgreSQL storage
+DATABASE_URL=postgres://psm:password@localhost:5432/psm \
+ cargo run --features postgres --package guardian-server
+```
+
+## Ports
+
+| Protocol | Default Port | Description |
+|---|---|---|
+| HTTP | `3000` | REST API |
+| gRPC | `50051` | gRPC service |
+
+Both can be configured programmatically via the `ServerBuilder`:
+
+```rust
+use server::builder::ServerBuilder;
+
+let builder = ServerBuilder::new()
+ .http(true, 3000)
+ .grpc(true, 50051);
+```
+
+## Verifying the server
+
+Once running, check the server's acknowledgment public key:
+
+```bash
+curl http://localhost:3000/pubkey
+# Returns: { "pubkey": "0x..." }
+```
+
+This endpoint is unauthenticated and confirms the server is operational.
diff --git a/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/troubleshooting.md b/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/troubleshooting.md
new file mode 100644
index 00000000..5ee316aa
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/miden-guardian/operator-guide/troubleshooting.md
@@ -0,0 +1,74 @@
+---
+title: Troubleshooting
+sidebar_position: 3
+---
+
+# Troubleshooting
+
+Common issues when operating a Guardian server and how to resolve them.
+
+## Server won't start
+
+**Symptom**: Server exits immediately or fails to bind ports.
+
+- Check that ports 3000 (HTTP) and 50051 (gRPC) are not already in use.
+- If using Postgres, ensure `DATABASE_URL` is set and the database is reachable.
+- Check logs with `RUST_LOG=debug` for detailed error messages.
+
+## Acknowledgment key mismatch
+
+**Symptom**: Clients report `ack_sig` verification failures.
+
+- Verify the server's public key via `GET /pubkey` and compare with what clients expect.
+- If the keystore was regenerated (new `PSM_KEYSTORE_PATH`), clients need to re-fetch the server's public key.
+- Ensure `PSM_KEYSTORE_PATH` is persistent across restarts — a new key on every restart will break client verification.
+
+## Authentication failures
+
+**Symptom**: Requests return `400 AuthenticationFailed`.
+
+- **Clock skew**: Client timestamp must be within 300 seconds of the server's time. Ensure NTP synchronization.
+- **Replay rejection**: Each request's timestamp must be strictly greater than the account's `last_auth_timestamp`. Rapid-fire requests with the same timestamp will fail.
+- **Wrong key**: The `x-pubkey` commitment must be in the account's cosigner allowlist. Verify the account's auth configuration.
+
+## Deltas stuck as candidates
+
+**Symptom**: Deltas remain in `candidate` status and never become `canonical`.
+
+- Check that the canonicalization worker is running (default: checks every 60 seconds).
+- Deltas must wait at least `delay_seconds` (default: 15 minutes) before the worker processes them.
+- If the on-chain commitment doesn't match, deltas are `discarded`. Check that the transaction was actually submitted and confirmed on-chain.
+- Inspect canonicalization logs: `RUST_LOG=server::jobs::canonicalization=debug`
+
+## Storage and metadata path issues
+
+**Symptom**: Server returns errors on state or delta operations.
+
+- Ensure `PSM_STORAGE_PATH` and `PSM_METADATA_PATH` point to writable directories.
+- For Postgres: verify the connection string and that migrations have run (they run automatically on startup).
+- Check disk space — filesystem storage can grow with the number of accounts and deltas.
+
+## Rate limiting
+
+**Symptom**: Clients receive `429 Too Many Requests`.
+
+- Default limits: 10 requests/second (burst), 60 requests/minute (sustained).
+- Adjust via `PSM_RATE_BURST_PER_SEC` and `PSM_RATE_PER_MIN` environment variables.
+- The `Retry-After` header in the response indicates how long to wait.
+
+## Common error codes
+
+| Error | Meaning |
+|---|---|
+| `AccountNotFound` | No account configured with this ID |
+| `AuthenticationFailed` | Invalid signature, unknown key, or expired timestamp |
+| `InvalidDelta` | Delta fails validation against current state or network |
+| `CommitmentMismatch` | Delta's `prev_commitment` doesn't match current state |
+| `ConflictPendingDelta` | Another candidate delta is already pending for this account |
+| `TimestampExpired` | Request timestamp is outside the 300-second window |
+| `TimestampReplay` | Request timestamp is not greater than last accepted timestamp |
+
+## Links
+
+- [Server README](https://github.com/OpenZeppelin/guardian/tree/main/crates/server) — full server documentation
+- [Guardian Specification](https://github.com/OpenZeppelin/guardian/tree/main/spec) — protocol specification
diff --git a/versioned_docs/version-0.14/builder/migration/01-imports-dependencies.md b/versioned_docs/version-0.14/builder/migration/01-imports-dependencies.md
new file mode 100644
index 00000000..debc577e
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/01-imports-dependencies.md
@@ -0,0 +1,114 @@
+---
+sidebar_position: 1
+title: "Imports & Dependencies"
+description: "Crate version bumps, import relocations, and MSRV changes in v0.14"
+---
+
+# Imports & Dependencies
+
+:::warning Breaking Change
+Miden VM dependencies move from 0.20 to 0.22 and `miden-crypto` from 0.19 to 0.23. Several types have been relocated across crates, and `Felt::as_int()` has been renamed.
+:::
+
+## Quick Fix
+
+```toml title="Cargo.toml"
+# Replace these
+miden-protocol = "0.13"
+miden-standards = "0.13"
+miden-assembly = "0.20"
+miden-core = "0.20"
+miden-processor = "0.20"
+miden-prover = "0.20"
+miden-crypto = "0.19"
+
+# With these
+miden-protocol = "0.14"
+miden-standards = "0.14"
+miden-assembly = "0.22"
+miden-core = "0.22"
+miden-processor = "0.22"
+miden-prover = "0.22"
+miden-crypto = "0.23"
+```
+
+---
+
+## Version Bumps
+
+| Crate | v0.13 | v0.14 |
+|-------|-------|-------|
+| `miden-protocol` | 0.13 | 0.14 |
+| `miden-standards` | 0.13 | 0.14 |
+| `miden-assembly` | 0.20 | 0.22 |
+| `miden-core` | 0.20 | 0.22 |
+| `miden-core-lib` | 0.20 | 0.22 |
+| `miden-processor` | 0.20 | 0.22 |
+| `miden-prover` | 0.20 | 0.22 |
+| `miden-crypto` | 0.19 | 0.23 |
+
+---
+
+## `ExecutionOptions`, `ProvingOptions`, `ExecutionProof` Relocated
+
+These types moved out of `miden-air` into their respective crates:
+
+```rust
+// Before (0.13)
+use miden_air::{ExecutionOptions, ProvingOptions, ExecutionProof};
+
+// After (0.14)
+use miden_processor::ExecutionOptions;
+use miden_prover::ProvingOptions;
+use miden_core::ExecutionProof;
+```
+
+---
+
+## `Felt::as_int()` → `Felt::as_canonical_u64()`
+
+The `Felt::as_int()` method has been renamed to `Felt::as_canonical_u64()` for clarity:
+
+```rust
+// Before (0.13)
+let value: u64 = felt.as_int();
+
+// After (0.14)
+let value: u64 = felt.as_canonical_u64();
+```
+
+:::tip
+Use find-and-replace across your codebase: `as_int()` → `as_canonical_u64()`.
+:::
+
+---
+
+## MSRV (Minimum Supported Rust Version)
+
+If you depend on `miden-client`, update your `rust-toolchain.toml` to Rust **1.91**:
+
+```toml title="rust-toolchain.toml"
+[toolchain]
+channel = "1.91"
+```
+
+---
+
+## Migration Steps
+
+1. Bump every Miden crate version in `Cargo.toml` per the table above.
+2. Move imports of `ExecutionOptions`, `ProvingOptions`, `ExecutionProof` to their new homes.
+3. Replace all `Felt::as_int()` calls with `Felt::as_canonical_u64()`.
+4. If you depend on `miden-client`, update your `rust-toolchain.toml` to Rust 1.91.
+5. Run `cargo update` to pull the new versions.
+6. Run `cargo build` and fix any remaining import errors.
+
+---
+
+## Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `unresolved import miden_air::ExecutionOptions` | Type moved | Import from `miden_processor::ExecutionOptions`. |
+| `no method named as_int found for Felt` | Method renamed | Use `Felt::as_canonical_u64()`. |
+| `package requires rustc 1.91` | MSRV bumped | Update toolchain. |
diff --git a/versioned_docs/version-0.14/builder/migration/02-hashing-stack.md b/versioned_docs/version-0.14/builder/migration/02-hashing-stack.md
new file mode 100644
index 00000000..aaef8d0d
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/02-hashing-stack.md
@@ -0,0 +1,117 @@
+---
+sidebar_position: 2
+title: "Hashing & Stack Changes"
+description: "Poseidon2 hash function, little-endian stack, and Falcon module rename in v0.14"
+---
+
+# Hashing & Stack Changes
+
+:::warning Breaking Change
+The native hash function changed from RPO to Poseidon2, and the operand stack is now little-endian. These are the most fundamental changes in v0.14 — every digest and every multi-limb operation is affected.
+:::
+
+---
+
+## Native Hash Function: RPO → Poseidon2
+
+### Summary
+
+The VM's native sponge hash flipped from RPO to Poseidon2. Rust type names (`Word`, `Hasher`, digests) are unchanged but **every digest the VM produces is different**: MAST roots, advice-map keys derived from hashing, account/note commitments, transaction IDs — none of them roundtrip with 0.13 artifacts.
+
+### Migration Steps
+
+1. Re-assemble every `.masl` and `.masp` from source under 0.22 (the MAST format version was bumped — old serialized forests will not deserialize anyway).
+2. Re-derive any persisted commitments (account commitments, note commitments, advice-map keys, MAST roots).
+3. Discard any cached transaction IDs, proven transactions, and proofs from 0.13.
+4. If you hard-coded MAST root literals in tests, regenerate them.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `MastForest deserialization failed: unexpected version` | MAST format version bumped along with the hash change | Re-assemble from source under 0.22. |
+| `transaction id mismatch` / `account commitment mismatch` | Old digest computed under RPO | Recompute from current state under 0.14. |
+
+---
+
+## Operand Stack Is Little-Endian
+
+### Summary
+
+Every multi-limb operation in the VM was unified around **"low limb closest to the top of the stack"**. Before 0.14 conventions were mixed: `u64` lived on the stack as `[hi, lo]`, `hperm` consumed `[R1, R0, C]` and produced its digest at indices 4..8, `mem_stream` returned words with the address-highest word on top. After 0.14 the low-significance limb is always on top: `u64` is `[lo, hi]`, `hperm` takes `[R0, R1, C]` with the digest at indices 0..4, and `mem_stream` returns the words in address-ascending order.
+
+This affects MASM that uses `u32split`, `u32widening_mul`, `u32madd`, `hperm`, `hmerge`, `mem_stream`, `adv_pipe`, `adv.insert_hdword`, `adv.insert_hdword_d`, `adv.insert_hqword`, `adv.insert_hperm`, all of `std::math::u64`, all of `std::math::u256`, and `ext2` extension-field values.
+
+### Affected Code
+
+**MASM (u64 add):**
+```masm
+# Before (0.13): [b_hi, b_lo, a_hi, a_lo, ...] -> [c_hi, c_lo, ...]
+push.0.0xFFFFFFFF push.0.1
+exec.::std::math::u64::wrapping_add
+
+# After (0.14): [b_lo, b_hi, a_lo, a_hi, ...] -> [c_lo, c_hi, ...]
+push.0xFFFFFFFF.0 push.1.0
+exec.::std::math::u64::wrapping_add
+```
+
+**MASM (`hperm` and digest extraction):** the input is now `[R0, R1, C, ...]` (was `[R1, R0, C, ...]`) and the digest comes out at indices `0..4` (was `4..8`).
+
+**MASM (`hmerge`):** input is now `[A, B, ...] -> [hash(A || B), ...]` (was `[B, A, ...]`).
+
+**Rust (`StackInputs` / `StackOutputs`):**
+
+For `StackInputs::try_from_ints([1, 2, 3, 4])` the first element (`1`) is the top of the stack — no reversal. When seeding a `u64`, push `[lo, hi]`, not `[hi, lo]`. Same when reading `StackOutputs`: `stack[0]` is the top.
+
+### Migration Steps
+
+1. Audit every MASM call into `std::math::u64` / `u256` and flip the limb order in adjacent `push`/`movdn`/`movup` instructions.
+2. Audit every `hperm`/`hmerge` site and update the index where you extract the digest (it moved from `[4..8]` to `[0..4]`).
+3. Audit `mem_stream` / `adv_pipe` users — the word at the address now lands on top, not buried.
+4. In Rust, drop any `[Felt; 16]` reversal helpers you used to construct stacks "in pretty order".
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `assertion failed: ...` (off-by-one in u64 arithmetic) | Limb order flipped | Push `[lo, hi]` instead of `[hi, lo]`. |
+| `expected hash 0x... at clock cycle ...` | Digest at wrong stack offset | The digest is now at indices 0..4, not 4..8. |
+
+---
+
+## Falcon Module Rename
+
+### Summary
+
+Because the native hash flipped to Poseidon2, the Falcon-512 verifier was rewritten and renamed. The MASM module path moved to `miden::core::crypto::dsa::falcon512_poseidon2` and the Rust auth scheme variant is `Falcon512Poseidon2`.
+
+### Affected Code
+
+**MASM:**
+```masm
+# Before (0.13)
+use.miden::core::crypto::dsa::falcon512rpo
+exec.falcon512rpo::verify
+
+# After (0.14)
+use.miden::core::crypto::dsa::falcon512_poseidon2
+exec.falcon512_poseidon2::verify
+```
+
+**Rust:**
+```rust
+// Before (0.13)
+use miden_protocol::account::auth::AuthScheme;
+let scheme = AuthScheme::Falcon512Rpo;
+
+// After (0.14)
+use miden_protocol::account::auth::AuthScheme;
+let scheme = AuthScheme::Falcon512Poseidon2;
+```
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `unknown module miden::core::crypto::dsa::falcon512rpo` | Module renamed | Use `falcon512_poseidon2`. |
+| `no variant or associated item named Falcon512Rpo for AuthScheme` | Variant renamed | Use `AuthScheme::Falcon512Poseidon2`. |
diff --git a/versioned_docs/version-0.14/builder/migration/03-account-changes.md b/versioned_docs/version-0.14/builder/migration/03-account-changes.md
new file mode 100644
index 00000000..91a206db
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/03-account-changes.md
@@ -0,0 +1,342 @@
+---
+sidebar_position: 3
+title: "Account Changes"
+---
+
+# Account Changes
+
+:::warning Breaking Change
+The v0.14 release introduces significant breaking changes to account construction, authentication components, and related APIs. All of the changes below require code updates when migrating from v0.13.
+:::
+
+## `AccountComponent::new` requires `AccountComponentMetadata`
+
+### Summary
+
+`AccountComponent::new` previously accepted two arguments (library, storage slots). It now requires a third argument: an `AccountComponentMetadata` value that describes the component's name and supported account types. The builder methods `with_metadata`, `with_supported_type`, and `with_supports_all_types` have been removed. Additionally, `AccountComponentTemplateError` has been renamed to `ComponentMetadataError`.
+
+### Affected Code
+
+Before:
+
+```rust
+let component = AccountComponent::new(library, storage_slots)?
+ .with_supports_all_types();
+```
+
+After:
+
+```rust
+let metadata = AccountComponentMetadata::new(
+ "my-component",
+ AccountType::all(),
+)?;
+
+let component = AccountComponent::new(library, storage_slots, metadata)?;
+```
+
+### Migration Steps
+
+1. Create an `AccountComponentMetadata` instance with a name and the set of supported `AccountType` values.
+2. Pass the metadata as the third argument to `AccountComponent::new`.
+3. Remove any chained calls to `with_supports_all_types()`, `with_supported_type()`, or `with_metadata()`.
+4. Replace references to `AccountComponentTemplateError` with `ComponentMetadataError`.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+|---|---|---|
+| `expected 3 arguments, found 2` | Missing metadata argument | Add `AccountComponentMetadata` as the third argument |
+| `no method named with_supports_all_types` | Removed builder method | Use `AccountType::all()` in metadata constructor |
+| `AccountComponentTemplateError not found` | Type renamed | Replace with `ComponentMetadataError` |
+
+## Auth components consolidated into `AuthSingleSig`
+
+### Summary
+
+The six per-scheme authentication types (`AuthFalcon512Rpo`, `AuthFalcon512Poseidon2`, `AuthEcdsaK256Keccak`, `AuthEcdsaK256Rpx`, `AuthEcdsaB256Rpx`, `AuthEcdsaB256Poseidon2`) have been replaced by a single `AuthSingleSig` component that accepts an `AuthScheme` enum. The corresponding ACL type `AuthSingleSigAcl` and multisig type `AuthMultisig` follow the same pattern.
+
+### Affected Code
+
+Before:
+
+```rust
+// Falcon-based auth
+let auth_component: AccountComponent = AuthFalcon512Rpo::new(pk_commitment).into();
+
+// ECDSA-based auth
+let auth_component: AccountComponent = AuthEcdsaK256Keccak::new(pk_commitment).into();
+```
+
+After:
+
+```rust
+// Falcon-based auth
+let auth_component: AccountComponent =
+ AuthSingleSig::new(pk_commitment, AuthScheme::Falcon512Poseidon2).into();
+
+// ECDSA-based auth
+let auth_component: AccountComponent =
+ AuthSingleSig::new(pk_commitment, AuthScheme::EcdsaK256Keccak).into();
+```
+
+### Migration Steps
+
+1. Replace all per-scheme auth constructors with `AuthSingleSig::new(pk_commitment, AuthScheme::...)`.
+2. Choose the correct `AuthScheme` variant that matches the old type name.
+3. Update ACL components to use `AuthSingleSigAcl` and multisig components to use `AuthMultisig`.
+
+## `@auth_script` MASM attribute replaces `auth_` prefix
+
+### Summary
+
+Authentication procedures in MASM are no longer identified by a naming convention (`auth_` prefix). Instead, exactly one procedure per auth component must be annotated with the `@auth_script` attribute.
+
+### Affected Code
+
+Before:
+
+```masm
+use.miden::standards::auth::falcon512_rpo
+
+export.auth_tx_falcon512_rpo
+ exec.falcon512_rpo::authenticate
+end
+```
+
+After:
+
+```masm
+use.miden::standards::auth::signature
+
+@auth_script
+export.auth_tx
+ exec.signature::authenticate
+end
+```
+
+### Migration Steps
+
+1. Replace the scheme-specific `use` import with `use.miden::standards::auth::signature`.
+2. Add the `@auth_script` attribute on the line before the procedure declaration.
+3. Rename the procedure to remove scheme-specific suffixes (e.g., `auth_tx_falcon512_rpo` becomes `auth_tx`).
+4. Replace the scheme-specific `exec` call with `exec.signature::authenticate`.
+
+## `AccountComponent::get_procedures()` replaced by `procedures()`
+
+### Summary
+
+The method `get_procedures()` has been renamed to `procedures()` and now returns an iterator instead of a `Vec<(Word, bool)>`. The `is_auth` flag is now derived from the `@auth_script` attribute rather than a naming convention.
+
+### Affected Code
+
+Before:
+
+```rust
+let procs: Vec<(Word, bool)> = component.get_procedures();
+```
+
+After:
+
+```rust
+for (proc_root, is_auth) in component.procedures() {
+ let digest: Word = proc_root.into();
+ // is_auth is true when @auth_script is present
+}
+```
+
+### Migration Steps
+
+1. Replace `get_procedures()` with `procedures()`.
+2. Update code to consume an iterator instead of a `Vec`.
+3. Note that `is_auth` is now determined by the `@auth_script` attribute, not the procedure name.
+
+## `commitment()` renamed to `to_commitment()` everywhere; `hash_account` removed
+
+### Summary
+
+All `commitment()` methods across the codebase have been renamed to `to_commitment()` for consistency. The standalone `hash_account` function has been removed in favor of calling `to_commitment()` on the account directly.
+
+### Affected Code
+
+Before:
+
+```rust
+let account_hash = account.commitment();
+let note_hash = note_header.commitment();
+let account_hash = hash_account(&id, &nonce, &code_commitment, &storage_commitment);
+```
+
+After:
+
+```rust
+let account_hash = account.to_commitment();
+let note_hash = note_header.to_commitment();
+let account_hash = account.to_commitment();
+```
+
+### Migration Steps
+
+1. Find and replace all calls to `.commitment()` with `.to_commitment()`.
+2. Replace any calls to `hash_account(...)` with `.to_commitment()` on the account instance.
+
+## `TransactionAuthenticator::get_public_key` returns `Arc`
+
+### Summary
+
+`TransactionAuthenticator::get_public_key` now returns `Option>` instead of `Option<&PublicKey>`. This change enables sharing the public key across threads without lifetime constraints.
+
+### Affected Code
+
+Before:
+
+```rust
+fn get_public_key(&self) -> Option<&PublicKey> {
+ Some(&self.public_key)
+}
+```
+
+After:
+
+```rust
+fn get_public_key(&self) -> Option> {
+ Some(Arc::new(self.public_key.clone()))
+}
+```
+
+### Migration Steps
+
+1. Update the return type of any `get_public_key` implementations to `Option>`.
+2. Wrap returned values in `Arc::new(...)`.
+3. Add `use std::sync::Arc;` if not already imported.
+
+## `Ownable2Step` and `MintPolicyConfig` for faucets
+
+### Summary
+
+`NetworkFungibleFaucet::new` no longer accepts an owner account ID. Ownership is now managed through a separate `Ownable2Step` component, and mint policy is configured through an `AuthControlled` component with `AuthControlledInitConfig`. This enables two-step ownership transfer and more flexible access control.
+
+### Affected Code
+
+Before:
+
+```rust
+let faucet = NetworkFungibleFaucet::new(
+ owner_account_id,
+ symbol,
+ 8,
+ max_supply,
+)?;
+```
+
+After:
+
+```rust
+let faucet = NetworkFungibleFaucet::new(symbol, 8, max_supply)?;
+let ownable = Ownable2Step::new(owner_account_id);
+let auth_controlled = AuthControlled::new(AuthControlledInitConfig::AllowAll);
+
+// Add all three components to the account builder
+```
+
+### Migration Steps
+
+1. Remove the `owner_account_id` argument from `NetworkFungibleFaucet::new`.
+2. Create an `Ownable2Step` component with the owner account ID.
+3. Create an `AuthControlled` component with the desired policy (e.g., `AuthControlledInitConfig::AllowAll`).
+4. Add all three components to the account builder.
+
+## `AccountSchemaCommitment` and `build_with_schema_commitment`
+
+### Summary
+
+A new `AccountBuilderSchemaCommitmentExt` extension trait provides a `build_with_schema_commitment()` method on the account builder. This enables building accounts that include a schema commitment for type-safe component validation.
+
+### Affected Code
+
+After:
+
+```rust
+use miden_account::AccountBuilderSchemaCommitmentExt;
+
+let (account, seed) = AccountBuilder::new(init_seed)
+ .with_component(component)
+ .build_with_schema_commitment()?;
+```
+
+### Migration Steps
+
+1. Add `use miden_account::AccountBuilderSchemaCommitmentExt;` to bring the extension trait into scope.
+2. Replace `.build()` with `.build_with_schema_commitment()` where schema commitment is desired.
+
+## `EthAddress` / `EthEmbeddedAccountId` split
+
+### Summary
+
+The single `EthAddressFormat` type has been split into two distinct types: `EthAddress` for raw Ethereum addresses and `EthEmbeddedAccountId` for account IDs that embed an Ethereum address. This provides clearer semantics for different use cases.
+
+### Affected Code
+
+Before:
+
+```rust
+let eth_addr = EthAddressFormat::new(*params.origin_token_address);
+```
+
+After:
+
+```rust
+let eth_addr = EthAddress::new(params.origin_token_address);
+let embedded_id = EthEmbeddedAccountId::from_account_id(account_id);
+```
+
+### Migration Steps
+
+1. Replace `EthAddressFormat::new(...)` with `EthAddress::new(...)` when working with raw Ethereum addresses.
+2. Use `EthEmbeddedAccountId::from_account_id(...)` when extracting an Ethereum address from a Miden account ID.
+3. Update imports to use the new type names.
+
+## `SchemaTypeId` renamed to `SchemaType`
+
+### Summary
+
+`SchemaTypeId` has been renamed to `SchemaType`. This is a pure rename with no behavioral changes.
+
+### Affected Code
+
+Before:
+
+```rust
+use SchemaTypeId;
+
+let t: SchemaTypeId = SchemaType::native_felt();
+```
+
+After:
+
+```rust
+use SchemaType;
+
+let t: SchemaType = SchemaType::native_felt();
+```
+
+### Migration Steps
+
+1. Find and replace all occurrences of `SchemaTypeId` with `SchemaType`.
+
+## Common Errors Reference
+
+| Error Message | Cause | Solution |
+|---|---|---|
+| `expected 3 arguments, found 2` on `AccountComponent::new` | Missing `AccountComponentMetadata` argument | Create metadata and pass as third argument |
+| `no method named with_supports_all_types` | Removed builder method | Use `AccountType::all()` in `AccountComponentMetadata` |
+| `AccountComponentTemplateError not found` | Type renamed | Replace with `ComponentMetadataError` |
+| `AuthFalcon512Rpo not found` | Per-scheme auth types removed | Use `AuthSingleSig` with `AuthScheme::Falcon512Poseidon2` |
+| `AuthEcdsaK256Keccak not found` | Per-scheme auth types removed | Use `AuthSingleSig` with `AuthScheme::EcdsaK256Keccak` |
+| `no method named get_procedures` | Method renamed | Use `procedures()` (returns iterator) |
+| `no method named commitment` | Method renamed | Use `to_commitment()` |
+| `hash_account not found` | Function removed | Call `.to_commitment()` on the account instance |
+| `expected Option<&PublicKey>, found Option>` | Return type changed | Update signature to return `Option>` |
+| `expected 4 arguments, found 3` on `NetworkFungibleFaucet::new` | Owner removed from constructor | Remove owner arg; use `Ownable2Step` component instead |
+| `EthAddressFormat not found` | Type split into two | Use `EthAddress` or `EthEmbeddedAccountId` |
+| `SchemaTypeId not found` | Type renamed | Replace with `SchemaType` |
diff --git a/versioned_docs/version-0.14/builder/migration/04-note-changes.md b/versioned_docs/version-0.14/builder/migration/04-note-changes.md
new file mode 100644
index 00000000..db2e4702
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/04-note-changes.md
@@ -0,0 +1,441 @@
+---
+sidebar_position: 4
+title: "Note Changes"
+description: "Note API renames, script format, asset building, type simplification, and constructor changes in v0.14"
+---
+
+# Note Changes
+
+:::warning Breaking Change
+`NoteInputs` has been renamed to `NoteStorage` across the entire API (Rust and MASM), and note scripts are now MASM libraries annotated with `@note_script` instead of programs with `begin` blocks. These two changes affect virtually every note-related code path.
+:::
+
+---
+
+## NoteInputs → NoteStorage
+
+### Summary
+
+The `NoteInputs` type has been renamed end-to-end to `NoteStorage`. This affects the Rust struct name, its methods, associated constants, `NoteRecipient` construction, the MASM procedure path, and the relevant error variant.
+
+### Affected Code
+
+**Rust:**
+```rust
+// Before (0.13)
+use miden_protocol::note::NoteInputs;
+
+let inputs = NoteInputs::new(values)?;
+let v = inputs.values();
+let n = inputs.num_values();
+assert!(n <= NoteInputs::MAX_INPUTS_PER_NOTE);
+
+let recipient = NoteRecipient::new(serial_num, script, inputs);
+
+// After (0.14)
+use miden_protocol::note::NoteStorage;
+
+let storage = NoteStorage::new(values)?;
+let v = storage.storage();
+let n = storage.num_items();
+assert!(n <= NoteStorage::MAX_NOTE_STORAGE_ITEMS);
+
+let recipient = NoteRecipient::new(serial_num, script, storage);
+```
+
+**MASM:**
+```masm
+# Before (0.13)
+exec.active_note::get_inputs
+
+# After (0.14)
+exec.active_note::get_storage
+```
+
+**Error variants:**
+```rust
+// Before (0.13)
+NoteError::TooManyInputs
+
+// After (0.14)
+NoteError::TooManyStorageItems
+```
+
+### Migration Steps
+
+1. Find-and-replace `NoteInputs` with `NoteStorage` across all Rust files.
+2. Rename method calls: `values()` → `storage()`, `num_values()` → `num_items()`.
+3. Replace `MAX_INPUTS_PER_NOTE` with `MAX_NOTE_STORAGE_ITEMS`.
+4. Update the third argument of `NoteRecipient::new(...)` from inputs to storage.
+5. In MASM, replace `active_note::get_inputs` with `active_note::get_storage`.
+6. Update any error matching on `NoteError::TooManyInputs` to `NoteError::TooManyStorageItems`.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `cannot find type NoteInputs in module note` | Type renamed | Use `NoteStorage`. |
+| `no method named values found for NoteStorage` | Method renamed | Use `.storage()`. |
+| `unknown procedure active_note::get_inputs` | MASM procedure renamed | Use `active_note::get_storage`. |
+
+---
+
+## NoteMetadata::new No Longer Takes a Tag
+
+### Summary
+
+`NoteMetadata::new` previously accepted three arguments including the tag. It now takes only `sender` and `type`. The tag is set separately via the builder method `.with_tag(tag)`.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+let metadata = NoteMetadata::new(sender, note_type, tag);
+
+// After (0.14)
+let metadata = NoteMetadata::new(sender, note_type).with_tag(tag);
+```
+
+### Migration Steps
+
+1. Find every call to `NoteMetadata::new(sender, type, tag)`.
+2. Remove the third argument and chain `.with_tag(tag)` on the result.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `this function takes 2 arguments but 3 arguments were supplied` | Signature changed | Remove the tag argument and use `.with_tag(tag)`. |
+
+---
+
+## Note Scripts Are MASM Libraries with @note_script
+
+### Summary
+
+Note scripts are no longer standalone programs with a `begin ... end` block. They are now MASM libraries that use the `@note_script` attribute on a `pub proc`. In Rust, you construct a `NoteScript` from a compiled `Library` instead of a `Program`.
+
+### Affected Code
+
+**MASM:**
+```masm
+# Before (0.13)
+use.miden::contracts::wallets::basic->wallet
+
+begin
+ exec.wallet::receive_asset
+end
+
+# After (0.14)
+use.miden::contracts::wallets::basic->wallet
+
+@note_script
+pub proc main
+ exec.wallet::receive_asset
+end
+```
+
+**Rust:**
+```rust
+// Before (0.13)
+let program = assembler.assemble_program(source)?;
+let script = NoteScript::new(program);
+
+// After (0.14)
+let library = assembler.assemble_library(source)?;
+let script = NoteScript::from_library(&library)?;
+```
+
+### Migration Steps
+
+1. In every `.masm` note script, replace the `begin ... end` block with `@note_script pub proc main ... end`.
+2. In Rust, switch from `assemble_program` to `assemble_library` and use `NoteScript::from_library(&library)?`.
+3. Ensure the procedure is marked `pub` — non-public procedures cannot be note entry points.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `no note_script attribute found in library` | Missing `@note_script` annotation | Add `@note_script` above the entry `pub proc`. |
+| `no method named new found for NoteScript` | Constructor changed | Use `NoteScript::from_library(&library)?`. |
+
+---
+
+## NoteAssets::add_asset Removed; OutputNoteBuilder Accumulates
+
+### Summary
+
+The `NoteAssets::add_asset` method has been removed. You now construct `NoteAssets` in one shot with the full list of assets. The `OutputNoteBuilder` stores a `Vec` internally and computes the commitment when `.build()` is called.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+let mut assets = NoteAssets::default();
+assets.add_asset(asset_a)?;
+assets.add_asset(asset_b)?;
+
+// After (0.14)
+let assets = NoteAssets::new(vec![asset_a, asset_b])?;
+```
+
+### Migration Steps
+
+1. Collect all assets into a `Vec` before constructing `NoteAssets`.
+2. Replace incremental `add_asset` calls with a single `NoteAssets::new(vec![...])`.
+3. If using `OutputNoteBuilder`, pass assets to the builder — it accumulates them internally and computes the commitment on `.build()`.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `no method named add_asset found for NoteAssets` | Method removed | Use `NoteAssets::new(vec![...])`. |
+
+---
+
+## NoteType::Encrypted Removed
+
+### Summary
+
+The `NoteType` enum has been simplified to two variants: `Private` and `Public`. The former `Encrypted` variant has been removed.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+let note_type = NoteType::Encrypted;
+
+// After (0.14)
+// Use NoteType::Private or NoteType::Public
+let note_type = NoteType::Private;
+```
+
+### Migration Steps
+
+1. Replace all uses of `NoteType::Encrypted` with `NoteType::Private` (or `NoteType::Public` depending on your intent).
+2. Update any match arms that handle `Encrypted` separately.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `no variant named Encrypted found for enum NoteType` | Variant removed | Use `NoteType::Private` or `NoteType::Public`. |
+
+---
+
+## NoteHeader::commitment → to_commitment; NoteLocation Field Rename
+
+### Summary
+
+The method `NoteHeader::commitment` has been renamed to `NoteHeader::to_commitment`. Additionally, the `NoteLocation` field `node_index_in_block` has been renamed to `block_note_tree_index`.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+let commitment = header.commitment();
+let index = location.node_index_in_block;
+
+// After (0.14)
+let commitment = header.to_commitment();
+let index = location.block_note_tree_index;
+```
+
+### Migration Steps
+
+1. Replace `.commitment()` with `.to_commitment()` on `NoteHeader`.
+2. Replace `node_index_in_block` with `block_note_tree_index` on `NoteLocation`.
+
+---
+
+## OutputNote::Header Removed; PrivateNoteHeader Introduced
+
+### Summary
+
+The `OutputNote` enum has been restructured. The old `OutputNote::Full` and `OutputNote::Header` variants are gone. There are now two enums:
+
+- **`RawOutputNote`** with variants `Full` and `Partial`.
+- **`OutputNote`** with variants `Public(PublicOutputNote)` and `Private(PrivateNoteHeader)`.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+match output_note {
+ OutputNote::Full(note) => { /* ... */ }
+ OutputNote::Header(header) => { /* ... */ }
+}
+
+// After (0.14)
+match output_note {
+ OutputNote::Public(public_note) => { /* ... */ }
+ OutputNote::Private(private_header) => { /* ... */ }
+}
+```
+
+### Migration Steps
+
+1. Replace `OutputNote::Full(note)` matches with `OutputNote::Public(public_note)`.
+2. Replace `OutputNote::Header(header)` matches with `OutputNote::Private(private_header)`.
+3. If you need raw full/partial note data, use `RawOutputNote::Full` or `RawOutputNote::Partial`.
+4. Update any type annotations referencing the old variants.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `no variant named Full found for enum OutputNote` | Enum restructured | Use `OutputNote::Public(PublicOutputNote)`. |
+| `no variant named Header found for enum OutputNote` | Variant removed | Use `OutputNote::Private(PrivateNoteHeader)`. |
+
+---
+
+## WellKnownNote / WellKnownComponent → Standard...
+
+### Summary
+
+All `WellKnown*` types have been renamed to `Standard*`:
+
+| Before (0.13) | After (0.14) |
+| --- | --- |
+| `WellKnownComponent` | `StandardAccountComponent` |
+| `WellKnownNote` | `StandardNote` |
+| `WellKnownNoteAttachment` | `StandardNoteAttachment` |
+
+The module path is now `miden_standards::note::StandardNote`.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+use miden_standards::note::WellKnownNote;
+use miden_standards::component::WellKnownComponent;
+use miden_standards::note::WellKnownNoteAttachment;
+
+// After (0.14)
+use miden_standards::note::StandardNote;
+use miden_standards::component::StandardAccountComponent;
+use miden_standards::note::StandardNoteAttachment;
+```
+
+### Migration Steps
+
+1. Find-and-replace `WellKnownComponent` → `StandardAccountComponent`.
+2. Find-and-replace `WellKnownNote` → `StandardNote`.
+3. Find-and-replace `WellKnownNoteAttachment` → `StandardNoteAttachment`.
+4. Update import paths accordingly.
+
+---
+
+## Standard Component Name Prefix
+
+### Summary
+
+The `NAME` constants on standard account components have moved under the `miden::standards::components::*` namespace.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+AuthSingleSig::NAME // "miden::auth::single_sig"
+BasicWallet::NAME // "miden::wallets::basic"
+BasicFungibleFaucet::NAME // "miden::faucets::basic_fungible"
+
+// After (0.14)
+AuthSingleSig::NAME // "miden::standards::components::auth_single_sig"
+BasicWallet::NAME // "miden::standards::components::basic_wallet"
+BasicFungibleFaucet::NAME // "miden::standards::components::basic_fungible_faucet"
+```
+
+### Migration Steps
+
+1. If you match on or compare against `NAME` constants, update the expected string values.
+2. Search your codebase for any hard-coded component name strings and update them to the `miden::standards::components::*` namespace.
+
+---
+
+## NoteExecutionHint Moved to miden-standards
+
+### Summary
+
+`NoteExecutionHint` has been relocated from `miden-protocol` to `miden-standards`.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+use miden_protocol::note::NoteExecutionHint;
+
+// After (0.14)
+use miden_standards::note::NoteExecutionHint;
+```
+
+### Migration Steps
+
+1. Update all imports of `NoteExecutionHint` to use `miden_standards::note::NoteExecutionHint`.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `unresolved import miden_protocol::note::NoteExecutionHint` | Type moved to `miden-standards` | Import from `miden_standards::note::NoteExecutionHint`. |
+
+---
+
+## 256 KiB Note Size Limit
+
+### Summary
+
+Public output notes are now subject to a maximum size of 256 KiB (`NOTE_MAX_SIZE = 2^18` bytes). Notes exceeding this limit will be rejected.
+
+### Migration Steps
+
+1. Audit any note construction that could produce large public notes (e.g., notes with many assets or large storage payloads).
+2. If your notes approach the 256 KiB limit, consider splitting them or reducing their payload.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `note size exceeds maximum allowed` | Public note exceeds 2^18 bytes | Reduce note payload or split into multiple notes. |
+
+---
+
+## Note Constructors Moved to Associated Methods
+
+### Summary
+
+Free-standing note constructor functions have been replaced by associated methods on their respective types.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+let note = create_p2id_note(sender, target, assets, recall_height)?;
+let note = create_swap_note(sender, offered, requested)?;
+let note = create_mint_note(faucet_id, amount, target)?;
+let note = create_burn_note(faucet_id, amount)?;
+let note = create_p2ide_note(sender, target, assets, recall_height)?;
+
+// After (0.14)
+let note = P2idNote::create(sender, target, assets, recall_height)?;
+let note = SwapNote::create(sender, offered, requested)?;
+let note = MintNote::create(faucet_id, amount, target)?;
+let note = BurnNote::create(faucet_id, amount)?;
+let note = P2ideNote::create(sender, target, assets, recall_height)?;
+```
+
+### Migration Steps
+
+1. Replace `create_p2id_note(...)` with `P2idNote::create(...)`.
+2. Replace `create_swap_note(...)` with `SwapNote::create(...)`.
+3. Replace `create_mint_note(...)` with `MintNote::create(...)`.
+4. Replace `create_burn_note(...)` with `BurnNote::create(...)`.
+5. Replace `create_p2ide_note(...)` with `P2ideNote::create(...)`.
+6. Add the appropriate `use` imports for the new types (e.g., `use miden_standards::note::P2idNote`).
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `cannot find function create_p2id_note` | Function moved to associated method | Use `P2idNote::create(...)`. |
+| `cannot find function create_swap_note` | Function moved to associated method | Use `SwapNote::create(...)`. |
diff --git a/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md b/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md
new file mode 100644
index 00000000..e79bba8c
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/05-asset-vault-faucet.md
@@ -0,0 +1,296 @@
+---
+sidebar_position: 5
+title: "Assets, Vault & Faucet"
+description: "Two-word asset representation, vault API changes, faucet burn updates, and TokenSymbol changes in v0.14"
+---
+
+# Assets, Vault & Faucet
+
+:::warning Breaking Change
+Assets are now represented as two words (`ASSET_KEY` + `ASSET_VALUE`) instead of a single `ASSET` word. This is the largest MASM-level change in v0.14 and affects every procedure that creates, inspects, adds, removes, or burns assets.
+:::
+
+---
+
+## ASSET to ASSET_KEY + ASSET_VALUE
+
+### Summary
+
+The single 4-felt `ASSET` word has been split into two words:
+
+- **`ASSET_KEY`** = `[asset_id_suffix, asset_id_prefix, (faucet_id_suffix << 8) | callbacks_enabled, faucet_id_prefix]`
+- **`ASSET_VALUE`** = `[amount, 0, 0, 0]` for fungible assets, or `DATA_HASH` for non-fungible assets.
+
+Every kernel procedure and standard-library helper that previously accepted or returned `ASSET` now works with the `ASSET_KEY, ASSET_VALUE` pair.
+
+### Affected Code
+
+**MASM (`native_account::add_asset`):**
+```masm
+# Before (0.13): stack = [ASSET, pad(12)]
+exec.native_account::add_asset
+# -> [ASSET, pad(12)]
+
+# After (0.14): stack = [ASSET_KEY, ASSET_VALUE, pad(8)]
+exec.native_account::add_asset
+# -> [ASSET_KEY, ASSET_VALUE, pad(8)]
+```
+
+**Rust:**
+```rust
+// Before (0.13)
+let word: Word = fungible_asset.into();
+
+// After (0.14)
+let key: Word = fungible_asset.to_key_word();
+let value: Word = fungible_asset.to_value_word();
+
+// Reconstructing from key/value
+let asset = FungibleAsset::from_key_value_words(key, value)?;
+```
+
+### Migration Steps
+
+1. Find every MASM site that pushes an `ASSET` word onto the stack before a kernel call. Replace the single word with `ASSET_KEY, ASSET_VALUE` and adjust padding from 12 to 8.
+2. In Rust, replace `.into()` conversions to `Word` with `.to_key_word()` and `.to_value_word()`.
+3. Replace any `Asset::from(word)` with `Asset::from_key_value_words(key, value)`.
+4. Update stack comments throughout your MASM to reflect the new two-word layout.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `FailedAssertion` at `add_asset` / `remove_asset` | Single `ASSET` word pushed instead of key+value pair | Split into `ASSET_KEY` and `ASSET_VALUE`. |
+| `no method named into found for FungibleAsset` (when targeting `Word`) | Direct `Word` conversion removed | Use `to_key_word()` and `to_value_word()`. |
+| Stack underflow in asset procedures | Padding not adjusted from 12 to 8 | Reduce padding to account for the extra word. |
+
+---
+
+## build_*_asset Renamed to create_*_asset
+
+### Summary
+
+The `asset::build_fungible_asset` and `asset::build_non_fungible_asset` procedures have been renamed to `create_fungible_asset` and `create_non_fungible_asset`. They also accept a new `enable_callbacks` flag and return the two-word `ASSET_KEY, ASSET_VALUE` pair.
+
+### Affected Code
+
+**MASM (fungible asset creation):**
+```masm
+# Before (0.13): stack = [faucet_id_prefix, faucet_id_suffix, amount, ...]
+exec.asset::build_fungible_asset
+# -> [ASSET, ...]
+
+# After (0.14): stack = [enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount, ...]
+exec.asset::create_fungible_asset
+# -> [ASSET_KEY, ASSET_VALUE, ...]
+```
+
+**Rust:**
+```rust
+// Before (0.13)
+let asset = FungibleAsset::new(faucet_id, amount)?;
+
+// After (0.14) — Rust API may vary; consult crate docs for exact constructor
+let asset = FungibleAsset::new(faucet_id, amount)?;
+// The MASM-level rename is the primary change; Rust constructors
+// now produce assets compatible with the two-word representation.
+```
+
+### Migration Steps
+
+1. Rename all `exec.asset::build_fungible_asset` calls to `exec.asset::create_fungible_asset`.
+2. Rename all `exec.asset::build_non_fungible_asset` calls to `exec.asset::create_non_fungible_asset`.
+3. Add the `enable_callbacks` flag as the new top-of-stack element.
+4. Note the changed argument order: `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]`.
+5. Update consumers to expect `[ASSET_KEY, ASSET_VALUE]` on the stack instead of a single `[ASSET]`.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `unknown procedure asset::build_fungible_asset` | Procedure renamed | Use `asset::create_fungible_asset`. |
+| `unknown procedure asset::build_non_fungible_asset` | Procedure renamed | Use `asset::create_non_fungible_asset`. |
+| `FailedAssertion` in `create_fungible_asset` | Missing `enable_callbacks` flag or wrong argument order | Push `[enable_callbacks, faucet_id_suffix, faucet_id_prefix, amount]`. |
+
+---
+
+## get_balance / has_non_fungible_asset Removed
+
+### Summary
+
+The kernel no longer exposes `get_balance` or `has_non_fungible_asset`. Instead, a single `get_asset` procedure handles both fungible and non-fungible lookups. It takes an `ASSET_KEY` and returns the corresponding `ASSET_VALUE`.
+
+### Affected Code
+
+**MASM (fungible balance check):**
+```masm
+# Before (0.13): stack = [faucet_id, ...]
+exec.native_account::get_balance
+# -> [balance, ...]
+
+# After (0.14): stack = [ASSET_KEY, pad(12)]
+exec.native_account::get_asset
+# -> [ASSET_VALUE, pad(12)]
+# balance is ASSET_VALUE[0] (top of ASSET_VALUE word)
+```
+
+**MASM (non-fungible existence check):**
+```masm
+# Before (0.13): stack = [ASSET, pad(12)]
+exec.native_account::has_non_fungible_asset
+# -> [has_asset, pad(15)]
+
+# After (0.14): stack = [ASSET_KEY, pad(12)]
+exec.native_account::get_asset
+# -> [ASSET_VALUE, pad(12)]
+# If the asset exists, ASSET_VALUE will be the DATA_HASH;
+# if not, ASSET_VALUE will be [0, 0, 0, 0].
+```
+
+**Rust:**
+```rust
+// Before (0.13)
+let balance = account.vault().get_balance(faucet_id)?;
+let has_nft = account.vault().has_non_fungible_asset(&asset)?;
+
+// After (0.14)
+let asset_value = account.vault().get_asset(asset_key)?;
+// For fungible: amount = asset_value[0]
+// For non-fungible: check if asset_value != [0, 0, 0, 0]
+```
+
+### Migration Steps
+
+1. Replace every `exec.native_account::get_balance` with `exec.native_account::get_asset`. Construct the `ASSET_KEY` for the fungible faucet and read the amount from the first element of the returned `ASSET_VALUE`.
+2. Replace every `exec.native_account::has_non_fungible_asset` with `exec.native_account::get_asset`. Construct the `ASSET_KEY` and compare the returned `ASSET_VALUE` against `[0, 0, 0, 0]` to determine existence.
+3. The same pattern applies to `get_initial_asset` (for checking assets at transaction start).
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `unknown procedure native_account::get_balance` | Procedure removed | Use `native_account::get_asset` with the asset key. |
+| `unknown procedure native_account::has_non_fungible_asset` | Procedure removed | Use `native_account::get_asset` and check for zero value. |
+| Wrong balance value | Reading wrong element from `ASSET_VALUE` | The amount is in `ASSET_VALUE[0]` (top of stack after the call). |
+
+---
+
+## AssetVault::remove_asset Returns Remaining Asset
+
+### Summary
+
+In Rust, `AssetVault::remove_asset` previously returned `Result` (the removed asset). It now returns `Result>` where the value is the **remaining** asset in the vault after partial removal. For non-fungible assets, the return is always `None` (the entire asset is removed).
+
+### Affected Code
+
+**Rust:**
+```rust
+// Before (0.13)
+let removed: Asset = vault.remove_asset(asset)?;
+
+// After (0.14)
+let remaining: Option = vault.remove_asset(asset)?;
+// remaining = Some(fungible_asset) if fungible with leftover balance
+// remaining = None if the vault entry was fully consumed (or non-fungible)
+```
+
+### Migration Steps
+
+1. Update all call sites of `AssetVault::remove_asset` to handle `Option` instead of `Asset`.
+2. If you previously used the return value as "the asset that was removed", note that the semantics have changed to "the asset remaining in the vault".
+3. For non-fungible assets, expect `None` — the asset is either fully present or fully removed.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `expected Asset, found Option` | Return type changed | Unwrap or pattern-match the `Option`. |
+| Logic error: treating return as "removed" amount | Semantics changed from removed to remaining | Adjust logic to interpret the value as remaining balance. |
+
+---
+
+## faucet::burn and native_account::remove_asset Stack Effects
+
+### Summary
+
+Both `faucet::burn` and `native_account::remove_asset` have updated stack effects to match the two-word asset representation. Notably, `faucet::burn` no longer returns the asset on the stack.
+
+### Affected Code
+
+**MASM (`faucet::burn`):**
+```masm
+# Before (0.13): stack = [ASSET, ...] -> [ASSET, ...]
+exec.faucet::burn
+
+# After (0.14): stack = [ASSET_KEY, ASSET_VALUE, ...] -> [...]
+exec.faucet::burn
+# Stack is consumed — nothing is returned for the burned asset.
+```
+
+**MASM (`native_account::remove_asset`):**
+```masm
+# Before (0.13): stack = [ASSET, pad(12)] -> [ASSET, pad(12)]
+exec.native_account::remove_asset
+
+# After (0.14): stack = [ASSET_KEY, ASSET_VALUE, pad(8)] -> [REMAINING_ASSET_VALUE, pad(12)]
+exec.native_account::remove_asset
+# Returns the REMAINING asset value in the vault (not the removed one).
+```
+
+### Migration Steps
+
+1. For `faucet::burn`: remove any code that reads the return value from the stack. The procedure now consumes `[ASSET_KEY, ASSET_VALUE]` and leaves nothing.
+2. For `native_account::remove_asset`: update stack expectations. The output is `[REMAINING_ASSET_VALUE, pad(12)]`, not the removed asset.
+3. If you need the removed amount, compute it before calling `remove_asset` (e.g., subtract the remaining value from the original).
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| Stack underflow after `faucet::burn` | Code tries to read a return value that no longer exists | `burn` now returns nothing; remove post-call reads. |
+| Wrong amount after `remove_asset` | Return value is remaining, not removed | Compute removed amount separately if needed. |
+| `FailedAssertion` in `burn` or `remove_asset` | Single `ASSET` word passed instead of key+value | Pass `[ASSET_KEY, ASSET_VALUE]` with correct padding. |
+
+---
+
+## TokenSymbol Changes
+
+### Summary
+
+The `TokenSymbol` type has several breaking changes:
+
+- **`to_string()`** is now available via the `Display` trait (infallible). Previously it could fail.
+- **`default()`** has been removed — there is no zero-value token symbol.
+- **`TryFrom`** now rejects values below `MIN_ENCODED_VALUE`.
+- **`MAX_SYMBOL_LENGTH`** increased from **6** to **12** characters.
+
+### Affected Code
+
+**Rust:**
+```rust
+// Before (0.13)
+let symbol = TokenSymbol::default(); // zero-value placeholder
+let s = symbol.to_string()?; // fallible conversion
+let sym = TokenSymbol::try_from(felt)?; // accepted any felt
+
+// After (0.14)
+// TokenSymbol::default() is removed — use a real symbol
+let s = format!("{}", symbol); // Display trait, infallible
+let sym = TokenSymbol::try_from(felt)?; // rejects below MIN_ENCODED_VALUE
+// Symbols can now be up to 12 characters (was 6)
+```
+
+### Migration Steps
+
+1. Remove any calls to `TokenSymbol::default()`. Replace with an explicit symbol via `TokenSymbol::new("TOKEN")` or equivalent constructor.
+2. Replace fallible `to_string()` calls with `format!("{}", symbol)` or `.to_string()` (now infallible via `Display`).
+3. If you validate token symbol length, update the upper bound from 6 to 12.
+4. If you construct `TokenSymbol` from a `Felt`, ensure the value is at or above `MIN_ENCODED_VALUE`.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `no function or associated item named default found for TokenSymbol` | `default()` removed | Use an explicit symbol value. |
+| `encoded value below minimum` / `InvalidTokenSymbol` | `TryFrom` now rejects small values | Ensure the felt is at or above `MIN_ENCODED_VALUE`. |
+| Compilation warning about unused `Result` on `to_string()` | Now returns `String` directly via `Display` | Remove error handling around `to_string()`. |
diff --git a/versioned_docs/version-0.14/builder/migration/06-transaction-changes.md b/versioned_docs/version-0.14/builder/migration/06-transaction-changes.md
new file mode 100644
index 00000000..6ff5513f
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/06-transaction-changes.md
@@ -0,0 +1,230 @@
+---
+sidebar_position: 6
+title: "Transaction Changes"
+description: "TransactionId hashing, ProvenTransaction construction, kernel event namespacing, stack order, and other transaction-level changes in v0.14"
+---
+
+# Transaction Changes
+
+:::warning Breaking Change
+Transaction identity, construction, event namespacing, summary stack layout, output accessors, and block signing have all changed in v0.14. These changes affect anyone building, proving, verifying, or signing transactions and blocks.
+:::
+
+---
+
+## TransactionId Now Hashes the Fee Asset
+
+### Summary
+
+`TransactionId::new` now requires a `fee_asset: FungibleAsset` parameter so the fee is included in the transaction hash. The function takes 5 arguments (6 words total) instead of the previous 4.
+
+### Affected Code
+
+**Rust:**
+```rust
+// Before (0.13)
+let tx_id = TransactionId::new(init, final_state, input_notes, output_notes);
+
+// After (0.14)
+let tx_id = TransactionId::new(init, final_state, input_notes, output_notes, fee_asset);
+```
+
+### Migration Steps
+
+1. Add the `fee_asset: FungibleAsset` argument to every `TransactionId::new` call site.
+2. Ensure the `fee_asset` value matches the fee used in the transaction.
+3. If you compute or verify transaction IDs externally (e.g., in tests), update the hashing logic to include the fee asset.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `this function takes 5 arguments but 4 arguments were supplied` | Missing `fee_asset` parameter | Add the `FungibleAsset` fee as the fifth argument. |
+| Transaction ID mismatch in verification | Hash computed without fee asset | Recompute the ID with the fee asset included. |
+
+---
+
+## ProvenTransactionBuilder Removed in Favor of ProvenTransaction::new
+
+### Summary
+
+The `ProvenTransactionBuilder` has been removed. Instead, construct a `TxAccountUpdate` and pass it directly to `ProvenTransaction::new`.
+
+### Affected Code
+
+**Rust:**
+```rust
+// Before (0.13)
+let proven_tx = ProvenTransactionBuilder::new(account_id, init_hash, final_hash, proof)
+ .account_update_details(details)
+ .add_input_notes(input_notes)
+ .add_output_notes(output_notes)
+ .build()?;
+
+// After (0.14)
+let account_update = TxAccountUpdate::new(account_id, init_hash, final_hash, details);
+let proven_tx = ProvenTransaction::new(
+ account_update,
+ input_notes,
+ output_notes,
+ ref_block_num,
+ ref_block_commitment,
+ fee,
+ expiration_block_num,
+ proof,
+);
+```
+
+### Migration Steps
+
+1. Remove all `ProvenTransactionBuilder` usage.
+2. Create a `TxAccountUpdate` from the account ID, initial hash, final hash, and update details.
+3. Call `ProvenTransaction::new` with the account update and all remaining fields as positional arguments.
+4. If you previously relied on builder defaults for optional fields, you must now provide them explicitly (e.g., `ref_block_num`, `ref_block_commitment`, `expiration_block_num`).
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `cannot find struct ProvenTransactionBuilder` | Builder removed | Use `ProvenTransaction::new` with `TxAccountUpdate`. |
+| `cannot find struct TxAccountUpdate` | Missing import | Add `use miden_objects::transaction::TxAccountUpdate;` (check crate docs for exact path). |
+| Wrong number of arguments to `ProvenTransaction::new` | Missing required positional parameters | Provide all 8 arguments: account_update, input_notes, output_notes, ref_block_num, ref_block_commitment, fee, expiration_block_num, proof. |
+
+---
+
+## Kernel Events Prefixed with miden::protocol
+
+### Summary
+
+All kernel events have been renamespaced under `miden::protocol::`. The previous `miden::` prefix is no longer recognized.
+
+### Affected Code
+
+**MASM:**
+```masm
+# Before (0.13)
+event("miden::account::vault_before_add_asset")
+event("miden::auth::request")
+
+# After (0.14)
+event("miden::protocol::account::vault_before_add_asset")
+event("miden::protocol::auth::request")
+```
+
+### Migration Steps
+
+1. Search your MASM files for all `event("miden::` occurrences.
+2. Replace `miden::` with `miden::protocol::` in every event name.
+3. If you have event handlers or listeners matching on event names, update those patterns as well.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| Event not firing / handler not triggered | Event name changed but handler still matches old name | Update both the event emission and handler to use `miden::protocol::` prefix. |
+| `unknown event` or silent no-op | Old event name no longer exists | Rename to `miden::protocol::...`. |
+
+---
+
+## Transaction Summary Stack Order Reversed
+
+### Summary
+
+The 4-word message that authentication procedures sign has its order reversed. Previously `SALT` was on top of the stack; now `ACCOUNT_DELTA_COMMITMENT` is on top.
+
+### Affected Code
+
+**MASM (stack layout for auth signing):**
+```masm
+# Before (0.13)
+# [SALT, OUTPUT_NOTES_COMMITMENT, INPUT_NOTES_COMMITMENT, ACCOUNT_DELTA_COMMITMENT, PUB_KEY]
+
+# After (0.14)
+# [ACCOUNT_DELTA_COMMITMENT, INPUT_NOTES_COMMITMENT, OUTPUT_NOTES_COMMITMENT, SALT, PUB_KEY]
+```
+
+### Migration Steps
+
+1. If you implement a custom authentication procedure that reads or signs the transaction summary, update the expected stack layout.
+2. The new order (top to bottom) is: `ACCOUNT_DELTA_COMMITMENT`, `INPUT_NOTES_COMMITMENT`, `OUTPUT_NOTES_COMMITMENT`, `SALT`, then `PUB_KEY`.
+3. Update any manual stack manipulation that assumed the old ordering.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| Signature verification failure | Auth procedure signing words in old order | Update to the new stack order before signing. |
+| `FailedAssertion` in custom auth | Stack words read in wrong positions | Reorder stack reads to match new layout. |
+
+---
+
+## TransactionOutputs Fields Private
+
+### Summary
+
+Fields on `TransactionOutputs` are no longer public. Use accessor methods instead.
+
+### Affected Code
+
+**Rust:**
+```rust
+// Before (0.13)
+let account = outputs.account;
+let fee = outputs.fee;
+let expiration = outputs.expiration_block_num;
+let notes = outputs.output_notes;
+
+// After (0.14)
+let account = outputs.account();
+let fee = outputs.fee();
+let expiration = outputs.expiration_block_num();
+let notes = outputs.output_notes(); // now returns &RawOutputNotes
+```
+
+### Migration Steps
+
+1. Replace all direct field accesses on `TransactionOutputs` with the corresponding method calls.
+2. Note that `output_notes()` now returns `&RawOutputNotes` instead of the previous type. Update downstream code to work with `RawOutputNotes`.
+3. Since accessors return references, add borrows or clones where ownership was previously obtained via field access.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `field account of TransactionOutputs is private` | Fields no longer public | Use `outputs.account()`. |
+| `field fee of TransactionOutputs is private` | Fields no longer public | Use `outputs.fee()`. |
+| Type mismatch on output notes | `output_notes()` returns `&RawOutputNotes` | Update code to handle `RawOutputNotes` instead of the previous type. |
+
+---
+
+## SignedBlock Added; BlockSigner Removed
+
+### Summary
+
+The `BlockSigner` trait has been removed. Instead, sign the block header commitment directly and construct a `SignedBlock`.
+
+### Affected Code
+
+**Rust:**
+```rust
+// Before (0.13)
+let signed = signer.sign(&header);
+
+// After (0.14)
+let sig = sk.sign(header.to_commitment());
+let signed_block = SignedBlock::new(header, body, sig);
+```
+
+### Migration Steps
+
+1. Remove all `BlockSigner` trait implementations and usages.
+2. Sign `header.to_commitment()` with your signing key directly.
+3. Construct `SignedBlock::new(header, body, signature)` with the header, block body, and resulting signature.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `cannot find trait BlockSigner` | Trait removed | Sign `header.to_commitment()` directly with your key. |
+| `cannot find struct SignedBlock` | Missing import | Add the appropriate import for `SignedBlock` from the Miden crate. |
+| Signature verification fails on block | Signing the header directly instead of its commitment | Sign `header.to_commitment()`, not the header itself. |
diff --git a/versioned_docs/version-0.14/builder/migration/07-client-changes.md b/versioned_docs/version-0.14/builder/migration/07-client-changes.md
new file mode 100644
index 00000000..48cd8c3f
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/07-client-changes.md
@@ -0,0 +1,381 @@
+---
+sidebar_position: 7
+title: "Client Changes"
+description: "Web SDK resource-based API, Rust SDK keystore trait, lazy readers, state sync, and CLI changes in v0.14"
+---
+
+# Client Changes
+
+:::info Affects Both Rust and Web SDKs
+v0.14 introduces significant API changes to both the Rust and Web Miden clients. The Web SDK adopts a resource-based API pattern, while the Rust SDK gains a new `Keystore` trait, lazy readers, and streamlined state sync. Review both sections if you maintain cross-platform code.
+:::
+
+---
+
+## Web SDK
+
+### New resource-based MidenClient API
+
+The monolithic `WebClient` god-object has been replaced by `MidenClient` with dedicated resource sub-objects. All operations are now accessed through typed namespaces: `client.accounts`, `client.transactions`, `client.notes`, `client.tags`, `client.settings`, `client.compile`, and `client.keystore`.
+
+#### Construction
+
+```typescript
+// Before (0.13)
+import { WebClient } from "@miden-sdk/miden-sdk";
+
+const client = new WebClient();
+await client.createClient({
+ node_url: "https://rpc.testnet.miden.io",
+ store_name: "my-store",
+});
+```
+
+```typescript
+// After (0.14)
+import { MidenClient, AccountType } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.create({
+ rpcUrl: "https://rpc.testnet.miden.io",
+ noteTransportUrl: "https://ntx.testnet.miden.io",
+ storeName: "my-store",
+});
+
+// Or use the testnet convenience constructor
+const client = await MidenClient.createTestnet();
+```
+
+#### Account creation
+
+```typescript
+// Before (0.13)
+const wallet = await client.newWallet(
+ AccountStorageMode.private(),
+ true,
+ undefined,
+);
+const faucet = await client.newFaucet(
+ AccountStorageMode.public(),
+ false,
+ "DAG",
+ 8,
+ BigInt(10_000_000),
+);
+```
+
+```typescript
+// After (0.14)
+const wallet = await client.accounts.create(); // mutable, private wallet (defaults)
+const faucet = await client.accounts.create({
+ type: AccountType.FungibleFaucet,
+ symbol: "DAG",
+ decimals: 8,
+ maxSupply: 10_000_000n,
+ storage: "public",
+});
+```
+
+#### Sending assets
+
+```typescript
+// Before (0.13)
+const request = client.newSendTransactionRequest(
+ wallet.id(),
+ AccountId.fromHex(targetHex),
+ faucet.id(),
+ NoteType.private(),
+ BigInt(100),
+ null,
+ null,
+);
+const tx = await client.submitNewTransaction(wallet.id(), request);
+```
+
+```typescript
+// After (0.14)
+const { txId } = await client.transactions.send({
+ account: wallet, // executing (sender) account
+ to: targetHex, // string hex ID, AccountId, or Account all accepted
+ token: faucet, // faucet account that minted the asset
+ amount: 100n,
+ waitForConfirmation: true,
+});
+```
+
+#### Consuming notes
+
+```typescript
+// Before (0.13)
+const notes = await client.getConsumableNotes(wallet.id());
+const req = client.newConsumeTransactionRequest(
+ [notes[0].inputNoteRecord().id().toString()],
+);
+await client.submitNewTransaction(wallet.id(), req);
+```
+
+```typescript
+// After (0.14)
+const notes = await client.notes.listAvailable({ account: wallet });
+await client.transactions.consume({ account: wallet, notes: [notes[0]] });
+
+// Or consume every available note in one call:
+await client.transactions.consumeAll({ account: wallet });
+```
+
+#### Listing accounts
+
+```typescript
+// Before (0.13)
+const accounts = await client.getAccounts();
+```
+
+```typescript
+// After (0.14)
+const accounts = await client.accounts.list();
+```
+
+#### Custom contracts
+
+The new API adds first-class support for deploying custom smart contracts:
+
+```typescript
+// After (0.14) — compile and deploy a custom contract
+const component = await client.compile.component({
+ code: contractMasm,
+ slots: [],
+});
+
+const contract = await client.accounts.create({
+ type: AccountType.MutableContract,
+ seed: new Uint8Array(32),
+ auth: secretKey,
+ components: [component],
+});
+
+// Compile a transaction script and execute it against the contract.
+const script = await client.compile.txScript({
+ code: scriptMasm,
+});
+await client.transactions.execute({ account: contract, script });
+```
+
+---
+
+### AccountId.fromHex and key getters return Result
+
+`AccountId.fromHex()` and related key getter methods previously called `unwrap()` internally and panicked on invalid input. They now return a `Result` type that throws on error in JavaScript.
+
+```typescript
+// Before (0.13) — panics on invalid hex
+const id = AccountId.fromHex(hexString);
+
+// After (0.14) — throws a descriptive error on invalid hex
+try {
+ const id = AccountId.fromHex(hexString);
+} catch (e) {
+ console.error("Invalid account ID:", e.message);
+}
+```
+
+---
+
+### Keystore API moved to client.keystore sub-object
+
+Top-level keystore functions have been consolidated into the `client.keystore` namespace:
+
+```typescript
+// Before (0.13)
+import { addAccountSecretKeyToWebStore, getAccountSecretKeyFromWebStore } from "@miden-sdk/miden-sdk";
+
+await addAccountSecretKeyToWebStore(accountId, secretKey);
+const key = await getAccountSecretKeyFromWebStore(accountId);
+```
+
+```typescript
+// After (0.14)
+await client.keystore.insert(accountId, secretKey);
+const key = await client.keystore.get(pubKeyCommitment); // takes a Word commitment
+const commitments = await client.keystore.getCommitments(accountId);
+const id = await client.keystore.getAccountId(pubKeyCommitment);
+```
+
+---
+
+## Rust SDK
+
+### Keystore trait replaces TransactionAuthenticator in Client
+
+The new `Keystore` super-trait extends `TransactionAuthenticator` and consolidates key management. The client no longer requires separate calls to register public key commitments.
+
+```rust
+// Before (0.13)
+keystore.insert_key(&secret).await?;
+client.register_account_public_key_commitments(
+ account_id,
+ &[public_key_commitment],
+).await?;
+```
+
+```rust
+// After (0.14)
+// Keystore::add_key handles both storage and commitment registration
+keystore.add_key(&secret, account_id).await?;
+```
+
+The `Client::new()` constructor now accepts `impl Keystore` instead of `impl TransactionAuthenticator`:
+
+```rust
+// Before (0.13)
+let client = Client::new(rpc, store, authenticator, ...);
+
+// After (0.14)
+let client = Client::new(rpc, store, keystore, ...);
+```
+
+---
+
+### New AccountReader and InputNoteReader
+
+Lazy readers allow you to access account and note data without loading everything into memory at once.
+
+```rust
+// Before (0.13) — loads the full Account object
+let account = client.get_account(account_id).await?.unwrap();
+let vault = account.vault();
+let storage = account.storage();
+```
+
+```rust
+// After (0.14) — lazy reader exposes commitments, balances, and storage items
+let reader = client.account_reader(account_id);
+let (header, status) = reader.header().await?;
+let balance = reader.get_balance(faucet_id).await?;
+let storage_item = reader.get_storage_item(slot_name).await?;
+// plus reader.nonce(), vault_root(), storage_commitment(), code_commitment()
+```
+
+```rust
+// After (0.14) — lazy, iterator-style traversal of notes consumable by an account
+let mut notes = client.input_note_reader(consumer_account_id);
+while let Some(note) = notes.next().await? {
+ // process each InputNoteRecord
+}
+```
+
+---
+
+### Lazy foreign-account loading
+
+Public foreign accounts are now fetched automatically via RPC when needed during transaction execution. You only need to provide `PartialAccount` data upfront for **private** foreign accounts.
+
+```rust
+// Before (0.13) — all foreign accounts had to be provided
+let foreign_accounts = vec![
+ ForeignAccount::public(public_account_id).await?,
+ ForeignAccount::private(private_partial_account),
+];
+let tx_request = TransactionRequestBuilder::new()
+ .with_foreign_accounts(foreign_accounts)
+ .build()?;
+
+// After (0.14) — only private foreign accounts need explicit loading
+let tx_request = TransactionRequestBuilder::new()
+ .with_foreign_accounts(vec![
+ ForeignAccount::private(private_partial_account),
+ ])
+ .build()?;
+// Public foreign accounts are auto-fetched via RPC during execution
+```
+
+---
+
+### StorageMapKey is now a newtype
+
+`StorageMapKey` was previously a type alias for `Word`. It is now a proper newtype, and the `LexicographicWord` wrapper has been removed.
+
+```rust
+// Before (0.13)
+use miden_objects::accounts::StorageMapKey;
+let key: StorageMapKey = [felt0, felt1, felt2, felt3]; // Was just a Word alias
+
+// After (0.14)
+use miden_protocol::account::StorageMapKey;
+use miden_protocol::Word;
+let key = StorageMapKey::new(Word::from([felt0, felt1, felt2, felt3]));
+// LexicographicWord is no longer needed — StorageMapKey handles ordering.
+// For sequential u32 keys, use the StorageMapKey::from_index(idx) shortcut.
+```
+
+---
+
+### `sync_state()` no longer takes arguments; `StateSyncInput` now internal
+
+`Client::sync_state()` builds its own `StateSyncInput` from the current store state — you no longer pass `account_ids`, `note_tags`, or `nullifiers` by hand. For custom sync scenarios, construct a `StateSyncInput` explicitly via `Client::build_sync_input()` and use the lower-level `StateSync` type.
+
+```rust
+// Before (0.13)
+client.sync_state(
+ &mut partial_mmr,
+ account_ids,
+ note_tags,
+ nullifiers,
+).await?;
+```
+
+```rust
+// After (0.14)
+let summary = client.sync_state().await?;
+
+// For custom sync scenarios, build the input manually:
+use miden_client::sync::StateSyncInput;
+let input: StateSyncInput = client.build_sync_input().await?;
+// …tweak `input.note_tags`, `input.input_notes`, etc. and drive
+// a `StateSync` instance directly.
+```
+
+---
+
+### MSRV 1.93
+
+The minimum supported Rust version is now **1.93**. Update your toolchain:
+
+```toml title="rust-toolchain.toml"
+[toolchain]
+channel = "1.93"
+```
+
+---
+
+### CLI: CliConfig::load and CliClient::new
+
+The CLI configuration and client constructors have been renamed for consistency.
+
+```rust
+// Before (0.13)
+let config = CliConfig::from_system()?;
+let client = CliClient::from_system_user_config(config, keystore).await?;
+```
+
+```rust
+// After (0.14)
+let config = CliConfig::load()?;
+let client = CliClient::new(config, keystore).await?;
+```
+
+---
+
+## New Client Features
+
+:::info New in v0.14
+These are new capabilities introduced in v0.14 that do not require migration but are worth adopting.
+:::
+
+- **NoteScreener and batch screening** - The `Client::note_screener()` API provides efficient batch screening of notes against account filters, replacing manual note-by-note checking.
+
+- **Account history pruning** - New methods allow pruning old account state history to reduce local storage usage while preserving the current state.
+
+- **GrpcClient automatic retry on rate limiting** - The gRPC client now automatically retries requests when rate-limited by the node, with up to 5 retries and honoring the server's `retry-after` header.
+
+- **Automatic NTX note script registration** - Note scripts sent via the NTX transport are now registered automatically. Manual `register_note_script()` calls are no longer needed.
+
+- **Typed RPC error parsing** - RPC errors are now parsed into structured Rust types, enabling programmatic error handling instead of string matching on error messages.
diff --git a/versioned_docs/version-0.14/builder/migration/08-masm-changes.md b/versioned_docs/version-0.14/builder/migration/08-masm-changes.md
new file mode 100644
index 00000000..1859170a
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/08-masm-changes.md
@@ -0,0 +1,295 @@
+---
+sidebar_position: 8
+title: "MASM Changes"
+description: "New MASM features and standard library additions in v0.14"
+---
+
+# MASM Changes
+
+:::info New MASM Features
+Miden v0.14 introduces assembly-time string-literal constants (`word("...")`, `event("...")`) and new standard library modules for MASM, including native 128-bit unsigned integer arithmetic.
+:::
+
+---
+
+## String-Literal Constants: `word("...")` and `event("...")`
+
+### Summary
+
+Miden v0.14 adds two constant expressions that derive their values from a string literal at assembly time:
+
+- `word("literal")` — hashes the literal with Blake3 and produces a `Word` (four field elements).
+- `event("literal")` — hashes the literal with Blake3 and reduces to a single `Felt` event id (the first element of the word, reduced modulo the Goldilocks prime).
+
+Both run at assembly time, so the hashing cost is paid by the assembler — the emitted MAST sees only the final word/felt immediate.
+
+:::note Kernel events also renamespaced in v0.14
+Event names on the kernel moved from `miden::…` to `miden::protocol::…` — see [Kernel Events Prefixed with `miden::protocol`](./transaction-changes#kernel-events-prefixed-with-midenprotocol).
+:::
+
+### `word("...")`
+
+`word("literal")` is valid anywhere a `Word` constant expression is accepted, most commonly in a `const` declaration. The literal string is hashed with Blake3; the 32-byte digest is split into four little-endian `u64` limbs and each limb becomes a `Felt`.
+
+**Naming a storage slot:**
+
+```masm
+use.miden::protocol::active_account
+use.miden::protocol::native_account
+use.miden::core::sys
+
+const COUNTER_SLOT = word("miden::tutorials::counter")
+
+#! Inputs: []
+#! Outputs: [count]
+pub proc get_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ # => [count]
+
+ exec.sys::truncate_stack
+end
+
+#! Inputs: []
+#! Outputs: []
+pub proc increment_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ add.1
+ push.COUNTER_SLOT[0..2] exec.native_account::set_item
+ # => []
+
+ exec.sys::truncate_stack
+end
+```
+
+`push.COUNTER_SLOT` pushes the full four-element word; `push.COUNTER_SLOT[0..2]` pushes a slice of the word. The derived word is deterministic — the same literal always hashes to the same word — so components that agree on a string convention share the same slot without a separately distributed constant.
+
+`word("...")` is only valid inside a `const` declaration. `push` and other instructions accept named constants but not inline `word("...")` calls, so always bind the expression to a name first.
+
+### `event("...")`
+
+`event("literal")` derives a `Felt` event id from the literal using the same hashing procedure as `word(...)`, then taking the first limb. This matches `EventId::from_name` in `miden-core`.
+
+Use `event("...")` in a `const` to name an event, then `emit.NAME`:
+
+```masm
+const U64_DIV_EVENT = event("miden::core::math::u64::u64_div")
+
+proc handler
+ # ... stack shaping ...
+ emit.U64_DIV_EVENT
+ # ... continue ...
+end
+```
+
+You can also emit a literal inline, without naming it:
+
+```masm
+begin
+ emit.event("miden::test::equiv")
+end
+```
+
+The assembler guarantees the three forms below produce the same MAST:
+
+```masm
+const EVT = event("miden::test::equiv")
+
+# (1) named constant
+begin
+ emit.EVT
+end
+
+# (2) inline literal
+begin
+ emit.event("miden::test::equiv")
+end
+
+# (3) manual push + emit
+begin
+ push.EVT
+ emit
+ drop
+end
+```
+
+### Where each is valid
+
+| Context | `word("...")` | `event("...")` |
+| ------------------------------------ | :-----------: | :------------: |
+| `const NAME = ...` | yes | yes |
+| `push.NAME` (immediate Word context) | yes | — |
+| `push.NAME` (immediate Felt context) | — | yes |
+| `emit.NAME` | — | yes |
+| `emit.event("...")` inline | — | yes |
+| Inline `word("...")` in `push.` | no | — |
+
+`emit.CONST` is accepted when `CONST` resolves (directly or via an alias chain) to an `event("...")` hash. A constant declared with `word("...")` or a plain integer will be rejected by the assembler.
+
+### Why prefer literal hashing?
+
+- **Readable MASM.** `event("miden::core::math::u64::u64_div")` documents intent directly in the source; the equivalent `push.` loses all context.
+- **Decoupled components.** Two MASM libraries that agree on a string name (e.g. `"miden::tutorials::counter"`) share a storage slot or event id without passing a constant between them.
+- **Assembly-time cost.** The Blake3 hash is computed once by the assembler; the running VM sees a plain immediate.
+
+### Notes and limitations
+
+- Hashing uses **Blake3**, not the VM's native hash. The derived values are convenient identifiers, not commitments produced inside a proof.
+- `word("")` and `event("")` (empty string) are valid and hash like any other input; collisions are vanishingly unlikely but not impossible.
+- Event ids are `Felt`s, so they are reduced modulo the Goldilocks prime ($p = 2^{64} - 2^{32} + 1$). Two distinct Blake3 digests whose first 8 little-endian bytes coincide modulo $p$ would collide — again, vanishingly unlikely for meaningful names.
+- These constants are evaluated inside the assembler (`miden-assembly` / `miden-assembly-syntax`). Code generated before v0.14 that did its own string-to-felt mapping for event ids must drop that logic — the assembler now owns it.
+
+---
+
+## 128-bit Integer Math: `std::math::u128`
+
+### Summary
+
+A new `std::math::u128` module provides full 128-bit unsigned integer arithmetic in MASM. A `u128` value is represented as four `u32` limbs in **little-endian** order `[a0, a1, a2, a3]`, where `a0` (the low limb) sits on top of the stack.
+
+For example, the value `0x00000001_00000002_00000003_00000004` is pushed as:
+
+```masm
+# Stack (top → bottom): [0x4, 0x3, 0x2, 0x1]
+# a0 a1 a2 a3
+push.0x00000001.0x00000002.0x00000003.0x00000004
+```
+
+### Usage Examples
+
+**Wrapping addition:**
+
+```masm
+use.std::math::u128
+
+# Push two u128 values (low limb on top)
+# a = 0x00000000_00000000_00000000_00000005
+push.0.0.0.5
+# b = 0x00000000_00000000_00000000_00000003
+push.0.0.0.3
+
+exec.u128::wrapping_add
+# Stack: [8, 0, 0, 0] (a + b = 8)
+```
+
+**Comparison (`lt`, `gte`):**
+
+```masm
+use.std::math::u128
+
+# a = 10
+push.0.0.0.10
+# b = 20
+push.0.0.0.20
+
+exec.u128::lt
+# Stack: [1] (10 < 20 is true)
+```
+
+**Bitwise operations (`and`, `xor`):**
+
+```masm
+use.std::math::u128
+
+push.0.0.0.0xFF
+push.0.0.0.0x0F
+
+exec.u128::and
+# Stack: [0x0F, 0, 0, 0]
+```
+
+**Shift left:**
+
+```masm
+use.std::math::u128
+
+push.0.0.0.1
+push.4 # shift amount
+
+exec.u128::shl
+# Stack: [16, 0, 0, 0] (1 << 4 = 16)
+```
+
+**Division and divmod:**
+
+```masm
+use.std::math::u128
+
+# a = 100
+push.0.0.0.100
+# b = 7
+push.0.0.0.7
+
+exec.u128::divmod
+# Stack: [quotient (4 limbs), remainder (4 limbs)]
+```
+
+### Available Procedures
+
+The full list of procedures in `std::math::u128`:
+
+| Category | Procedures |
+|----------|-----------|
+| **Arithmetic** | `overflowing_add`, `widening_add`, `wrapping_add`, `overflowing_sub`, `wrapping_sub`, `overflowing_mul`, `widening_mul`, `wrapping_mul`, `div`, `mod`, `divmod` |
+| **Comparison** | `eq`, `neq`, `eqz`, `lt`, `gt`, `lte`, `gte`, `min`, `max` |
+| **Bitwise** | `and`, `or`, `xor`, `not` |
+| **Bit counting** | `clz`, `ctz`, `clo`, `cto` |
+| **Shifts / Rotates** | `shl`, `shr`, `rotl`, `rotr` |
+
+---
+
+## `u32overflowing_mul` → `u32widening_mul`
+
+### Summary
+
+Three rename pairs that better reflect that the result is *wider* than the operands (not arithmetic overflow):
+
+```masm
+# Before (0.13)
+u32overflowing_mul
+u32overflowing_madd
+exec.::std::math::u64::overflowing_mul
+
+# After (0.14)
+u32widening_mul
+u32widening_madd
+exec.::std::math::u64::widening_mul
+```
+
+`u32overflowing_add` is unchanged (the rename only applies to multiplication and madd). v0.14 also adds matching `u32widening_add` and `std::math::u64::widening_add` helpers.
+
+---
+
+## `breakpoint` Instruction Removed
+
+The `breakpoint` MASM instruction was a no-op used with the old `miden debug` REPL. Both the REPL and the instruction are gone — any `.masm` source containing `breakpoint` will now fail to assemble. Delete those lines.
+
+```masm
+# Before (0.13)
+breakpoint # used for debugging
+
+# After (0.14)
+# Just remove the line — breakpoint no longer exists
+```
+
+---
+
+## Migration Steps
+
+1. Replace any hand-rolled string-to-felt mapping for event ids with `event("...")` — the assembler now owns this derivation.
+2. Where a storage-slot name or event id is shared across components, prefer `word("...")` / `event("...")` over distributing a pre-computed `Word` / `Felt` constant.
+3. If you have custom 128-bit arithmetic helpers in MASM, consider replacing them with the new `std::math::u128` module.
+4. Remember that `u128` values follow the same little-endian convention as the rest of v0.14 — the low limb (`a0`) is on top of the stack.
+5. Rename `u32overflowing_mul` → `u32widening_mul` and `u32overflowing_madd` → `u32widening_madd`.
+6. Rename `std::math::u64::overflowing_mul` → `std::math::u64::widening_mul`.
+7. Remove any `breakpoint` instructions from your MASM source.
+
+---
+
+## Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `unknown module std::math::u128` | Using an older assembler version | Update to `miden-assembly` 0.22. |
+| Incorrect `u128` results | Limbs pushed in big-endian order | Push low limb last so it lands on top: `push.a3.a2.a1.a0`. |
+| `unknown instruction u32overflowing_mul` | Instruction renamed | Use `u32widening_mul`. |
+| `unknown instruction breakpoint` | Instruction removed | Delete the line. |
diff --git a/versioned_docs/version-0.14/builder/migration/09-vm-assembler.md b/versioned_docs/version-0.14/builder/migration/09-vm-assembler.md
new file mode 100644
index 00000000..c8abd415
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/09-vm-assembler.md
@@ -0,0 +1,265 @@
+---
+sidebar_position: 9
+title: "VM & Assembler Changes"
+description: "Host trait consolidation, FastProcessor builder API, and other VM-level breaking changes in v0.14"
+---
+
+# VM & Assembler Changes
+
+:::warning Breaking Change
+The VM host interface has been collapsed from three traits to one, the processor construction API has changed to a builder pattern, and several types have been cleaned up or relocated. These changes affect anyone embedding the Miden VM directly.
+:::
+
+---
+
+## `SyncHost` / `BaseHost` Removed; `AsyncHost` → `Host`
+
+### Summary
+
+The previous three-trait hierarchy (`BaseHost`, `SyncHost`, `AsyncHost`) has been collapsed into a single `Host` trait. All methods now return `impl FutureMaybeSend<...>`, making the trait async-compatible by default. Additionally, `ProcessState` was renamed from `ProcessorState`, and the `on_assert_failed` callback was removed.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+impl BaseHost for MyHost {}
+
+impl SyncHost for MyHost {
+ fn get_mast_forest(
+ &self,
+ node_digest: &Digest,
+ ) -> Option> {
+ // ...
+ }
+
+ fn on_event(
+ &mut self,
+ process: &ProcessState,
+ event_id: u32,
+ ) -> Result<(), ExecutionError> {
+ // ...
+ }
+}
+```
+
+```rust
+// After (0.14)
+impl Host for MyHost {
+ fn get_mast_forest(
+ &self,
+ node_digest: &Digest,
+ ) -> impl FutureMaybeSend>> {
+ async {
+ // ...
+ }
+ }
+
+ fn on_event(
+ &mut self,
+ process: &ProcessorState,
+ event_id: u32,
+ ) -> impl FutureMaybeSend> {
+ async {
+ // ...
+ }
+ }
+}
+```
+
+### Migration Steps
+
+1. Remove `impl BaseHost for ...` and `impl SyncHost for ...` blocks.
+2. Implement the single `Host` trait instead.
+3. Wrap each method body in `async { ... }` and return `impl FutureMaybeSend<...>`.
+4. Rename any `ProcessState` references to `ProcessorState`.
+5. Remove any `on_assert_failed` implementations — the callback no longer exists.
+
+---
+
+## `FastProcessor` Builder API
+
+### Summary
+
+`FastProcessor::new_with_advice_inputs()` and `FastProcessor::new_debug()` have been removed. Construction now follows a builder pattern starting from `FastProcessor::new(stack_inputs)`.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+let processor = FastProcessor::new_with_advice_inputs(stack_inputs, advice_inputs);
+let processor = FastProcessor::new_debug(stack_inputs);
+```
+
+```rust
+// After (0.14)
+let processor = FastProcessor::new(stack_inputs)
+ .with_advice(advice_inputs)
+ .with_debugging(true)
+ .with_tracing(true);
+
+// Or with execution options:
+let processor = FastProcessor::new(stack_inputs)
+ .with_advice(advice_inputs)
+ .with_options(execution_options);
+```
+
+### Migration Steps
+
+1. Replace `new_with_advice_inputs(stack, advice)` with `new(stack).with_advice(advice)`.
+2. Replace `new_debug(stack)` with `new(stack).with_debugging(true)`.
+3. Chain `.with_options(...)` or `.with_tracing(true)` as needed.
+
+---
+
+## `StackInputs` / `StackOutputs` API Cleanup
+
+### Summary
+
+Both `StackInputs` and `StackOutputs` now derive `Copy`, so you can drop explicit `.clone()` calls. `StackInputs::new()` now takes a `&[Felt]` slice instead of a `Vec`.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+let inputs = StackInputs::new(vec![felt_a, felt_b, felt_c]);
+let inputs_copy = inputs.clone();
+
+// After (0.14)
+let inputs = StackInputs::new(&[felt_a, felt_b, felt_c]);
+let inputs_copy = inputs; // Copy, no clone needed
+```
+
+### Migration Steps
+
+1. Replace `StackInputs::new(vec![...])` with `StackInputs::new(&[...])`.
+2. Remove unnecessary `.clone()` calls on `StackInputs` and `StackOutputs`.
+
+---
+
+## `MastForest::strip_decorators` → `clear_debug_info`
+
+### Summary
+
+`MastForest::strip_decorators()` has been renamed to `MastForest::clear_debug_info()` and now wipes the entire `DebugInfo` structure, not just decorators. A new convenience method `MastForest::write_stripped()` writes the forest with debug info removed without mutating the original.
+
+The MAST serialization format version was also bumped — `.masl` and `.masp` files produced by v0.13 will **not** deserialize under v0.14.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+forest.strip_decorators();
+
+// After (0.14)
+forest.clear_debug_info();
+
+// Or write a stripped copy without mutating:
+forest.write_stripped(&mut output)?;
+```
+
+### Migration Steps
+
+1. Replace `strip_decorators()` calls with `clear_debug_info()`.
+2. Consider using `write_stripped()` if you only need stripped output without modifying the forest in memory.
+3. Re-assemble all `.masl` and `.masp` files from source — 0.13 serialized files will fail to deserialize.
+
+### Common Errors
+
+| Error Message | Cause | Solution |
+| --- | --- | --- |
+| `no method named strip_decorators found` | Method renamed | Use `clear_debug_info()`. |
+| `MastForest deserialization failed: unexpected version` | MAST format version bumped | Re-assemble from source under 0.22. |
+
+---
+
+## `Process`, `VmStateIterator`, `execute_iter()` Removed
+
+### Summary
+
+The "slow" `Process` type, `VmStateIterator`, `VmState`, `AsmOpInfo`, `SlowProcessState`, and the top-level `miden_processor::execute_iter()` function have been removed. Execution goes exclusively through `FastProcessor`.
+
+### Affected Code
+
+```rust
+// Before (0.13)
+for state in execute_iter(&program, stack_inputs, advice_inputs, &mut host) {
+ let state: VmState = state?;
+ println!("clk={} op={:?}", state.clk(), state.op());
+}
+
+// After (0.14)
+let mut processor = FastProcessor::new(stack_inputs).with_advice(advice_inputs);
+let mut ctx = processor.get_initial_resume_context(&program)?;
+loop {
+ let outcome = processor.step(&program, &mut ctx, &mut host)?;
+ if outcome.is_done() { break; }
+}
+```
+
+For one-shot non-iterating execution, use `FastProcessor::execute_sync(...)` or the top-level `miden_processor::execute_sync(...)`.
+
+---
+
+## `miden debug` / `analyze` / `repl` Removed
+
+The three CLI subcommands were removed along with `VmStateIterator`. The CLI now exposes only `compile`, `bundle`, `run`, `prove`, `verify`. There is no drop-in replacement; use the external debugger or write a small Rust program that drives `FastProcessor::step` if you need step-by-step inspection.
+
+---
+
+## `Operation` Enum Trimmed to Basic-Block Ops
+
+### Summary
+
+Control-flow opcodes (`Join`, `Split`, `Loop`, `Call`, `Dyn`, `Dyncall`, `SysCall`, `Span`, `End`, `Respan`, `Halt`) were removed from `miden_core::Operation`; they live exclusively at the MAST node level (`MastNode::Join`, etc.). Pattern matches over those variants must move to traversals over `MastNode`.
+
+---
+
+## `ExecutionOptions`, `ProvingOptions`, `ExecutionProof` Relocated
+
+These types have moved crates. See [Imports & Dependencies](./imports-dependencies) for the updated import paths.
+
+---
+
+## Project File Format
+
+### Summary
+
+v0.14 introduces a **first-class Miden project file format** (`miden-project.toml`), implemented in the new `miden-project` crate. Projects describe a single package or a workspace of packages with dependencies (path, git, registry), profiles, lints, and per-package metadata — much like `Cargo.toml`. The assembler compiles a project directly to a `.masp` package via `Assembler::link_package`, with dependency resolution through `pubgrub`.
+
+This is **additive** for existing per-file assembly users — old `Assembler::compile_and_statically_link` / `assemble_library` APIs still work — but it is the recommended layout for any new MASM project of more than a single file.
+
+### Standalone Package Example
+
+```toml title="my-app/miden-project.toml"
+[package]
+name = "example"
+version = "0.1.0"
+
+[lib]
+path = "lib/mod.masm"
+
+[dependencies]
+miden-protocol = { path = "../protocol/userspace" }
+
+[profile.test]
+inherits = "dev"
+network = "local"
+
+[lints.miden]
+unused = "error"
+```
+
+### Building from Rust
+
+```rust
+use miden_assembly::Assembler;
+
+let package = Assembler::default()
+ .link_package("./my-app/miden-project.toml")?;
+```
+
+---
+
+:::tip
+For the full VM changelog, see the [miden-vm releases](https://github.com/0xMiden/miden-vm/releases).
+:::
diff --git a/versioned_docs/version-0.14/builder/migration/index.md b/versioned_docs/version-0.14/builder/migration/index.md
new file mode 100644
index 00000000..c23fb37c
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/migration/index.md
@@ -0,0 +1,151 @@
+---
+title: "v0.14 Migration Guide"
+description: "Complete guide for upgrading from Miden v0.13 to v0.14"
+---
+
+# Miden Testnet 0.14.0
+
+This guide covers all breaking changes you need to migrate an application to Miden 0.14.0. Like the 0.13 guide, it is intentionally user-facing: you do not need to know or care which internal crate (VM, protocol, client) a change came from. If you are:
+
+- building accounts, notes, or transactions
+- running a client, web client or React SDK
+- writing or compiling MASM
+- interacting with storage, auth, or RPCs
+
+this document is for you.
+
+---
+
+## Quick Upgrade
+
+Try upgrading first — most projects can start with a dependency update:
+
+```toml title="Cargo.toml"
+# Replace these
+miden-protocol = "0.13"
+miden-standards = "0.13"
+miden-assembly = "0.20"
+miden-core = "0.20"
+miden-processor = "0.20"
+miden-prover = "0.20"
+miden-crypto = "0.19"
+
+# With these
+miden-protocol = "0.14"
+miden-standards = "0.14"
+miden-assembly = "0.22"
+miden-core = "0.22"
+miden-processor = "0.22"
+miden-prover = "0.22"
+miden-crypto = "0.23"
+```
+
+Then run:
+
+```bash
+cargo update && cargo build
+```
+
+If you encounter errors, continue reading for detailed migration steps.
+
+---
+
+:::info Who should read this?
+This guide is for:
+- **Rust client developers** migrating from v0.13 → v0.14
+- **Web SDK developers** using the JavaScript/TypeScript SDK
+- **Smart contract authors** writing MASM or using protocol APIs
+- **App developers** using `miden-protocol`, `miden-standards`, or client crates
+
+If you're starting fresh on v0.14, you can skip this guide and go directly to the [Get Started guide](../get-started).
+:::
+
+---
+
+## At a Glance
+
+Big themes in 0.14:
+
+| Change | Summary |
+|--------|---------|
+| **Poseidon2 hashing** | The native hash function changed from RPO to Poseidon2. Every MAST root, commitment, and persisted digest changes. Rebuild all artifacts. |
+| **Little-endian stack** | The operand stack is now little-endian. `u64`, `u256`, `hperm`, `hmerge`, `mem_stream`, `adv_pipe` all have the low limb on top. |
+| **Two-word assets** | `ASSET` is now `ASSET_KEY` + `ASSET_VALUE`. Every procedure touching assets has a new stack signature. |
+| **NoteStorage** | `NoteInputs` renamed to `NoteStorage` end-to-end (Rust, MASM, errors). |
+| **MASM library scripts** | Note and auth scripts are now MASM libraries with `@note_script` / `@auth_script` attributes. |
+| **AuthSingleSig** | Six per-scheme auth components consolidated into `AuthSingleSig`, `AuthSingleSigAcl`, `AuthMultisig`. |
+| **Web MidenClient** | The flat `WebClient` replaced by resource-based `MidenClient` API. |
+| **Ownable2Step** | New built-in two-step ownership transfer component for faucets. |
+| **128-bit math** | New `std::math::u128` module in the standard library. |
+
+---
+
+## New Features in v0.14
+
+These are additive features you may want to adopt (not breaking changes):
+
+- **NoteScreener and batch screening**: A new `Client::note_screener()` API adds batch note consumability checks for more efficient note screening workflows.
+- **Account history pruning**: The client now exposes account history pruning APIs for storage and state management.
+- **GrpcClient automatic retry on rate limiting**: `GrpcClient` now automatically retries rate-limited requests up to five times and honors `retry-after` when provided.
+- **Automatic NTX note script registration**: NTX note scripts are now registered automatically — no more manual script registration as part of transaction submission.
+- **Typed RPC error parsing**: RPC errors are now parsed into typed client errors, improving programmatic error handling.
+- **128-bit integer math**: New `std::math::u128` module provides full unsigned integer arithmetic, comparison, bitwise, and shift/rotate operations.
+
+---
+
+## Compatibility
+
+| Component | Required | Tested With |
+|-----------|----------|-------------|
+| Miden VM crates | 0.22+ | 0.22.0 |
+| miden-crypto | 0.23+ | 0.23.0 |
+| miden-protocol | 0.14+ | 0.14.3 |
+| miden-standards | 0.14+ | 0.14.3 |
+| Rust | 1.91+ | 1.91.0 |
+
+---
+
+## Migration Sections
+
+Work through these sections in order for a complete migration:
+
+| Section | Topics |
+|---------|--------|
+| [1. Imports & Dependencies](./imports-dependencies) | Cargo.toml bumps, import relocations, MSRV, `Felt` rename |
+| [2. Hashing & Stack Changes](./hashing-stack) | Poseidon2, little-endian stack, Falcon module rename |
+| [3. Account Changes](./account-changes) | Components, AuthSingleSig, `@auth_script`, Ownable2Step |
+| [4. Note Changes](./note-changes) | NoteStorage, `@note_script`, OutputNote variants, StandardNote |
+| [5. Assets, Vault & Faucet](./asset-vault-faucet) | Two-word assets, create_* rename, get_asset, vault changes |
+| [6. Transaction Changes](./transaction-changes) | TransactionId, ProvenTransaction, events, SignedBlock |
+| [7. Client Changes](./client-changes) | Web MidenClient, Rust Keystore, AccountReader, StateSync |
+| [8. MASM Changes](./masm-changes) | 128-bit math, event namespace |
+| [9. VM & Assembler Changes](./vm-assembler) | Host trait, FastProcessor, type relocations |
+
+---
+
+## Final Checklist
+
+Complete these steps to verify your migration:
+
+- [ ] Bump all Miden crate versions in `Cargo.toml` per section 1
+- [ ] Update `rust-toolchain.toml` to Rust 1.91+ (if using miden-client)
+- [ ] Re-assemble all `.masl` and `.masp` files from source (Poseidon2 hash change)
+- [ ] Discard any cached commitments, transaction IDs, or proofs from v0.13
+- [ ] Audit MASM stack arithmetic for little-endian limb order
+- [ ] Update asset handling to two-word `ASSET_KEY` + `ASSET_VALUE` format
+- [ ] Rename `NoteInputs` → `NoteStorage` everywhere
+- [ ] Add `@note_script` / `@auth_script` attributes to MASM entrypoints
+- [ ] Replace per-scheme auth components with `AuthSingleSig`
+- [ ] Update `AccountComponent::new` to pass `AccountComponentMetadata`
+- [ ] Replace `commitment()` calls with `to_commitment()`
+- [ ] Update `NoteMetadata::new` to use `with_tag()` builder
+- [ ] Replace `WellKnownNote` / `WellKnownComponent` with `Standard…` equivalents
+- [ ] Migrate note constructors to associated methods (`P2idNote::create`, etc.)
+- [ ] Update Web SDK from `WebClient` to `MidenClient` resource API
+- [ ] Replace `TransactionAuthenticator` with `Keystore` trait in Rust client
+- [ ] Run `cargo build` — **no errors**
+- [ ] Run `cargo test` — **all tests pass**
+
+:::tip You're done!
+If your project builds and all tests pass, you've successfully migrated to v0.14.
+:::
diff --git a/versioned_docs/version-0.14/builder/private-multisig/_category_.json b/versioned_docs/version-0.14/builder/private-multisig/_category_.json
new file mode 100644
index 00000000..e1a43e30
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/private-multisig/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Private Multisig",
+ "position": 2
+}
diff --git a/versioned_docs/version-0.14/builder/private-multisig/core-concepts.md b/versioned_docs/version-0.14/builder/private-multisig/core-concepts.md
new file mode 100644
index 00000000..47bcc853
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/private-multisig/core-concepts.md
@@ -0,0 +1,121 @@
+---
+title: Core Concepts
+sidebar_position: 1
+---
+
+# Core Concepts
+
+## Transaction lifecycle
+
+Every multisig transaction follows the same lifecycle: propose, sign, execute, sync.
+
+```mermaid
+sequenceDiagram
+ participant P as Proposer
+ participant Guardian as Guardian Server
+ participant C1 as Cosigner 1
+ participant C2 as Cosigner 2
+
+ P->>P: Build TransactionSummary locally
+ P->>Guardian: push_delta_proposal (tx_summary + proposer signature)
+ Guardian->>Guardian: Validate against current state
+
+ C1->>Guardian: get_delta_proposals
+ Guardian-->>C1: Pending proposals
+ C1->>C1: Verify locally
+ C1->>Guardian: sign_delta_proposal
+
+ C2->>Guardian: get_delta_proposals
+ Guardian-->>C2: Pending proposals
+ C2->>C2: Verify locally
+ C2->>Guardian: sign_delta_proposal
+
+ Note over Guardian: Threshold met
+
+ P->>Guardian: push_delta (with all signatures)
+ Guardian-->>P: Canonical delta with ack_sig
+ P->>P: Execute on-chain with ZK proof
+```
+
+1. **Propose**: The proposer builds a `TransactionSummary` locally, signs it, and pushes it to Guardian as a delta proposal.
+2. **Sign**: Each cosigner fetches pending proposals, verifies the transaction details against their local state, and submits their signature.
+3. **Execute**: Once the threshold is met, any participant pushes the final delta (with all signatures). Guardian returns the acknowledged delta. The executor builds and submits the on-chain transaction.
+4. **Sync**: All participants fetch the latest state from Guardian to stay synchronized.
+
+## Key architecture: 2-of-3 setup
+
+A common configuration uses a 2-of-3 threshold:
+
+| Key | Holder | Purpose |
+|---|---|---|
+| **Key 1** | User hot key | Daily transactions |
+| **Key 2** | User cold key | Recovery and emergency override |
+| **Key 3** | Guardian service key | Co-signing and policy enforcement |
+
+```mermaid
+graph TD
+ subgraph "Normal operation (Hot + Guardian)"
+ Hot["User Hot Key"] --> TX["Transaction"]
+ GuardianKey["Guardian Service Key"] --> TX
+ end
+
+ subgraph "Emergency override (Hot + Cold)"
+ Hot2["User Hot Key"] --> Override["Rotate Guardian / Adjust policies Switch providers"]
+ Cold["User Cold Key"] --> Override
+ end
+```
+
+- **Normal operations**: Hot key + Guardian's co-signature are sufficient.
+- **Emergency override**: Hot + cold keys alone can rotate out Guardian or switch providers.
+- **Guardian alone cannot move funds**: It holds only one key in the threshold.
+
+## Transaction types
+
+| Type | Description |
+|---|---|
+| **Transfer (P2ID)** | Send assets to another account |
+| **Consume notes** | Spend incoming notes |
+| **Add signer** | Add a new cosigner to the multisig account |
+| **Remove signer** | Remove a cosigner |
+| **Change threshold** | Update the required signature count |
+| **Switch Guardian** | Change the Guardian provider endpoint |
+
+## Signer types
+
+The TypeScript SDK supports multiple signer backends:
+
+| Signer | Scheme | Use case |
+|---|---|---|
+| `FalconSigner` | Falcon | Local Falcon key (default) |
+| `EcdsaSigner` | ECDSA | Local ECDSA key |
+| `ParaSigner` | ECDSA | External EVM wallets via [Para](https://getpara.com) SDK |
+| `MidenWalletSigner` | Any | [Miden Wallet](https://github.com/demox-labs/miden-wallet) browser extension |
+
+## Offline fallback
+
+If Guardian is unreachable, the SDKs support fully offline workflows:
+
+```mermaid
+sequenceDiagram
+ participant A as Signer A
+ participant File as JSON File (side channel)
+ participant B as Signer B
+
+ A->>A: Create proposal offline
+ A->>File: Export proposal.json
+
+ File-->>B: Share via email, chat, etc.
+ B->>B: Sign proposal locally
+ B->>File: Export signed proposal
+
+ File-->>A: Return signed file
+ A->>A: Collect enough signatures
+ A->>A: Execute transaction on-chain
+```
+
+1. Create a proposal locally and export it as JSON.
+2. Share the file with cosigners through any side channel.
+3. Each cosigner signs offline and returns the signed file.
+4. Once the threshold is met, execute the transaction on-chain.
+
+This ensures multisig operations remain functional even without Guardian connectivity.
diff --git a/versioned_docs/version-0.14/builder/private-multisig/index.md b/versioned_docs/version-0.14/builder/private-multisig/index.md
new file mode 100644
index 00000000..57e76ecb
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/private-multisig/index.md
@@ -0,0 +1,76 @@
+---
+title: Private Multisig
+sidebar_position: 0
+---
+
+# Private Multisig
+
+Private multisig on Miden allows multiple parties to collectively control an account, requiring a configurable threshold of signatures (N-of-M) to execute transactions — all while keeping account state private.
+
+## The problem
+
+On public chains, multisig coordination is straightforward: every signer can read the same on-chain state and build their next action on top of it. Safe-style multisigs work because the ledger is transparent.
+
+In Miden's private account model, account state lives client-side. The chain stores only cryptographic commitments. This means:
+
+- Signers can't independently observe the latest state from the chain.
+- Proposals and signatures need an off-chain coordination surface.
+- Without a shared state view, participants risk divergent state or stale approvals.
+
+The [Miden Guardian](../miden-guardian/) solves this by acting as the coordination server for multisig accounts — keeping signers synchronized, managing proposal workflows, and ensuring all parties work from the same canonical state.
+
+## How it works
+
+Miden multisigs can be fully private (code, signers, metadata, etc. are not visible). Guardian coordinates the workflow:
+
+1. **Propose**: A signer pushes a delta proposal (containing a `TransactionSummary`) to Guardian. Guardian validates the proposal against the current account state and the Miden network.
+2. **Sign**: Other authorized cosigners fetch the pending proposal from Guardian, verify the transaction details locally, and submit their signatures.
+3. **Ready**: Once enough signatures are collected (meeting the threshold), Guardian emits an acknowledgment.
+4. **Execute**: Any cosigner builds the final transaction using all signatures plus the Guardian acknowledgment, and submits it on-chain.
+5. **Sync**: All participants fetch the latest canonical state from Guardian.
+
+```mermaid
+sequenceDiagram
+ participant A as Signer A (proposer)
+ participant Guardian as Guardian Server
+ participant B as Signer B (cosigner)
+ participant Chain as Miden Network
+
+ A->>A: Build transaction locally
+ A->>Guardian: Push delta proposal (tx_summary + signature)
+ Guardian->>Guardian: Validate against state & network
+
+ B->>Guardian: Fetch pending proposals
+ Guardian-->>B: Return proposal details
+ B->>B: Verify transaction locally
+ B->>Guardian: Sign proposal
+
+ Note over Guardian: Threshold met (2-of-3)
+
+ A->>Guardian: Push delta (with all signatures + Guardian ack)
+ Guardian-->>A: Acknowledged delta
+ A->>Chain: Submit ZK proof
+ Chain-->>Guardian: Commitment confirmed
+ Guardian->>Guardian: Mark canonical
+```
+
+## Learn more
+
+
+
+ Transaction lifecycle, key architecture, and offline fallback.
+
+
+ Rust SDK for building multisig workflows.
+
+
+ TypeScript SDK for building multisig workflows.
+
+
+
+## Repositories
+
+| Repository | Description |
+|---|---|
+| [Miden Guardian](https://github.com/OpenZeppelin/guardian) | Guardian server, client SDKs, and multisig client libraries |
+| [MultiSig](https://github.com/OpenZeppelin/MultiSig) | MultiSig reference application (Next.js frontend + coordinator) |
diff --git a/versioned_docs/version-0.14/builder/private-multisig/rust-sdk.md b/versioned_docs/version-0.14/builder/private-multisig/rust-sdk.md
new file mode 100644
index 00000000..e4a64e21
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/private-multisig/rust-sdk.md
@@ -0,0 +1,113 @@
+---
+title: Rust SDK
+sidebar_position: 2
+---
+
+# Rust Multisig SDK
+
+The `miden-multisig-client` crate provides a high-level Rust SDK for private multisig workflows on Miden. It wraps the on-chain multisig contracts and Guardian coordination into a single API.
+
+**Source**: [`crates/miden-multisig-client`](https://github.com/OpenZeppelin/guardian/tree/main/crates/miden-multisig-client)
+
+## Installation
+
+```toml
+[dependencies]
+miden-multisig-client = { git = "https://github.com/OpenZeppelin/guardian", package = "miden-multisig-client" }
+```
+
+## Setup
+
+```rust
+use miden_client::rpc::Endpoint;
+use miden_multisig_client::MultisigClient;
+use miden_objects::Word;
+
+let signer1: Word = /* RPO Falcon commitment */ Word::default();
+let signer2: Word = Word::default();
+
+let mut client = MultisigClient::builder()
+ .miden_endpoint(Endpoint::new("http://localhost:57291"))
+ .psm_endpoint("http://localhost:50051")
+ .account_dir("/tmp/multisig")
+ .generate_key()
+ .build()
+ .await?;
+```
+
+## Creating a multisig account
+
+```rust
+// Create a 2-of-2 multisig account and register it on Guardian
+let account = client.create_account(2, vec![signer1, signer2]).await?;
+```
+
+## Propose, sign, execute
+
+```rust
+use miden_multisig_client::TransactionType;
+use miden_objects::account::AccountId;
+
+let recipient = AccountId::from_hex("0x...")?;
+let faucet = AccountId::from_hex("0x...")?;
+let tx = TransactionType::transfer(recipient, faucet, 1_000);
+
+// Proposer creates the transaction proposal on Guardian
+let proposal = client.propose_transaction(tx).await?;
+
+// Cosigner lists and signs the proposal
+let proposals = client.list_proposals().await?;
+let to_sign = proposals.iter().find(|p| p.id == proposal.id).unwrap();
+client.sign_proposal(&to_sign.id).await?;
+
+// Once threshold is met, execute
+client.execute_proposal(&proposal.id).await?;
+```
+
+## Offline fallback
+
+If Guardian is unavailable, the SDK automatically produces an offline proposal:
+
+```rust
+use miden_multisig_client::{ProposalResult, TransactionType};
+
+let tx = TransactionType::consume_notes(vec![note_id]);
+match client.propose_with_fallback(tx).await? {
+ ProposalResult::Online(p) => {
+ println!("Proposal {} is live on Guardian", p.id);
+ }
+ ProposalResult::Offline(exported) => {
+ std::fs::write("proposal.json", exported.to_json()?)?;
+ println!("Share proposal.json with cosigners for offline signing");
+ }
+}
+```
+
+### Offline signing and execution
+
+```rust
+// Cosigner signs an imported proposal
+client.sign_imported_proposal(&mut exported)?;
+std::fs::write("proposal_signed.json", exported.to_json()?)?;
+
+// Once enough signatures are collected
+client.execute_imported_proposal(&exported).await?;
+```
+
+## Listing consumable notes
+
+```rust
+use miden_multisig_client::NoteFilter;
+
+// All consumable notes
+let notes = client.list_consumable_notes().await?;
+
+// Filter by faucet and minimum amount
+let faucet = AccountId::from_hex("0x...")?;
+let filter = NoteFilter::by_faucet_min_amount(faucet, 5_000);
+let spendable = client.list_consumable_notes_filtered(filter).await?;
+```
+
+## Full reference
+
+See the [`crates/miden-multisig-client/README.md`](https://github.com/OpenZeppelin/guardian/tree/main/crates/miden-multisig-client) for the complete API reference.
diff --git a/versioned_docs/version-0.14/builder/private-multisig/typescript-sdk.md b/versioned_docs/version-0.14/builder/private-multisig/typescript-sdk.md
new file mode 100644
index 00000000..b951da70
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/private-multisig/typescript-sdk.md
@@ -0,0 +1,146 @@
+---
+title: TypeScript SDK
+sidebar_position: 3
+---
+
+# TypeScript Multisig SDK
+
+The `@openzeppelin/miden-multisig-client` package provides a high-level TypeScript SDK for private multisig workflows on Miden. It supports multiple signer types including external wallets.
+
+**Package**: [`@openzeppelin/miden-multisig-client`](https://www.npmjs.com/package/@openzeppelin/miden-multisig-client)
+**Source**: [`packages/miden-multisig-client`](https://github.com/OpenZeppelin/guardian/tree/main/packages/miden-multisig-client)
+
+## Installation
+
+```bash
+npm install @openzeppelin/miden-multisig-client @miden-sdk/miden-sdk
+```
+
+## Setup
+
+```typescript
+import { MultisigClient, FalconSigner } from '@openzeppelin/miden-multisig-client';
+import { MidenClient, AuthSecretKey } from '@miden-sdk/miden-sdk';
+
+// Initialize the Miden SDK client
+const midenClient = await MidenClient.createTestnet();
+
+// Create a Falcon signer
+const secretKey = AuthSecretKey.rpoFalconWithRNG(undefined);
+const signer = new FalconSigner(secretKey);
+
+// Create the multisig client and fetch Guardian info
+const client = new MultisigClient(midenClient, {
+ psmEndpoint: 'http://localhost:3000',
+});
+const { psmCommitment } = await client.initialize();
+```
+
+:::note
+The multisig client accepts any `MidenClient` instance, so `createTestnet()` / `createDevnet()` / `create({ rpcUrl })` all work. See the [Web SDK setup guide](../tools/clients/web-client/setup.md) for the full factory list.
+:::
+
+## Creating a multisig account
+
+```typescript
+const config = {
+ threshold: 2,
+ signerCommitments: [signer.commitment, otherSignerCommitment],
+ psmCommitment,
+ signatureScheme: 'falcon',
+};
+
+const multisig = await client.create(config, signer);
+console.log('Account ID:', multisig.accountId);
+
+// Register on Guardian
+await multisig.registerOnPsm();
+```
+
+## Loading an existing account
+
+```typescript
+// Configuration is auto-detected from on-chain storage
+const multisig = await client.load(accountId, signer);
+```
+
+## Syncing state
+
+Fetch proposals, state, notes, and config in a single call:
+
+```typescript
+const { proposals, state, notes, config } = await multisig.syncAll();
+```
+
+## Creating and executing proposals
+
+```typescript
+// Send payment
+const { proposal } = await multisig.createSendProposal(recipientId, faucetId, amount);
+
+// Sign a proposal
+await multisig.signTransactionProposal(proposal.commitment);
+
+// Execute when ready
+if (proposal.status.type === 'ready') {
+ await multisig.executeTransactionProposal(proposal.commitment);
+}
+```
+
+Other proposal types:
+
+```typescript
+await multisig.createConsumeNotesProposal(noteIds);
+await multisig.createAddSignerProposal(newCommitment, { newThreshold: 3 });
+await multisig.createRemoveSignerProposal(signerToRemove);
+await multisig.createChangeThresholdProposal(3);
+await multisig.createSwitchPsmProposal(newEndpoint, newPubkey);
+```
+
+## External wallet integration
+
+For browser wallets where the signing key is external:
+
+```typescript
+// Sign the commitment with an external wallet
+const signature = await wallet.sign(proposal.commitment);
+
+// Submit the external signature
+await multisig.signTransactionProposalExternal({
+ commitment: proposal.commitment,
+ signature,
+ publicKey: wallet.publicKey,
+ scheme: 'ecdsa',
+});
+```
+
+### Wallet signers
+
+```typescript
+import { ParaSigner, MidenWalletSigner } from '@openzeppelin/miden-multisig-client';
+
+// Para wallet integration
+const signer = new ParaSigner(paraContext, walletId, commitment, publicKey);
+
+// Miden wallet integration
+const signer = new MidenWalletSigner(walletContext, commitment, 'ecdsa');
+```
+
+## Offline workflows
+
+Export and import proposals for side-channel signing:
+
+```typescript
+// Export a proposal as JSON
+const json = multisig.exportTransactionProposalToJson(proposal.commitment);
+
+// Sign offline
+const signedJson = multisig.signTransactionProposalOffline(proposal.commitment);
+
+// Import on another device
+const { proposal: imported } = multisig.importTransactionProposal(json);
+```
+
+## Full reference
+
+See the [`packages/miden-multisig-client/README.md`](https://github.com/OpenZeppelin/guardian/tree/main/packages/miden-multisig-client) for the complete API reference.
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/_category_.json b/versioned_docs/version-0.14/builder/smart-contracts/_category_.json
new file mode 100644
index 00000000..e5cd9472
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Smart Contracts",
+ "position": 3
+}
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/accounts/_category_.json b/versioned_docs/version-0.14/builder/smart-contracts/accounts/_category_.json
new file mode 100644
index 00000000..65048510
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/accounts/_category_.json
@@ -0,0 +1,8 @@
+{
+ "label": "Accounts",
+ "position": 4,
+ "link": {
+ "type": "generated-index",
+ "slug": "/builder/smart-contracts/accounts"
+ }
+}
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/accounts/account-operations.md b/versioned_docs/version-0.14/builder/smart-contracts/accounts/account-operations.md
new file mode 100644
index 00000000..b0e3a38d
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/accounts/account-operations.md
@@ -0,0 +1,129 @@
+---
+title: "Account Operations"
+sidebar_position: 4
+description: "Query account state and mutate the vault using self methods in Miden components."
+---
+
+# Account Operations
+
+The `#[component]` macro automatically provides methods on `self` for interacting with the current account during a transaction. Read-only queries are available on `&self`, and mutations (add/remove assets, increment nonce) require `&mut self`.
+
+## Read-only queries (`&self`)
+
+```rust
+#[component]
+impl MyAccount {
+ pub fn check_state(&self) {
+ // Account identity
+ let id: AccountId = self.get_id();
+ let nonce: Felt = self.get_nonce();
+
+ // Vault queries
+ let balance: Felt = self.get_balance(faucet_id);
+ let initial: Felt = self.get_initial_balance(faucet_id);
+ let has_nft: bool = self.has_non_fungible_asset(asset);
+ let root: Word = self.get_vault_root();
+ let initial_root: Word = self.get_initial_vault_root();
+
+ // Commitment queries
+ let commitment: Word = self.compute_commitment();
+ let initial_commit: Word = self.get_initial_commitment();
+ let storage: Word = self.compute_storage_commitment();
+ let initial_storage: Word = self.get_initial_storage_commitment();
+ let code: Word = self.get_code_commitment();
+
+ // Procedure queries
+ let count: Felt = self.get_num_procedures();
+ let proc_root: Word = self.get_procedure_root(0);
+ let exists: bool = self.has_procedure(proc_root);
+ }
+}
+```
+
+## Mutations (`&mut self`)
+
+```rust
+#[component]
+impl MyAccount {
+ pub fn receive_asset(&mut self, asset: Asset) {
+ // Add an asset to the vault — returns the asset as stored
+ let stored: Asset = self.add_asset(asset);
+ }
+
+ pub fn send_asset(&mut self, asset: Asset, note_idx: NoteIdx) {
+ // Remove an asset from the vault — returns the removed asset
+ // Proof generation fails if the asset doesn't exist or insufficient balance
+ let removed: Asset = self.remove_asset(asset);
+ output_note::add_asset(removed, note_idx);
+ }
+
+ pub fn auth(&mut self) {
+ // Increment the account nonce (replay protection)
+ let new_nonce: Felt = self.incr_nonce();
+
+ // Compute commitment of all state changes in this transaction
+ let delta: Word = self.compute_delta_commitment();
+
+ // Check if a specific procedure was called during this transaction
+ let called: bool = self.was_procedure_called(proc_root);
+ }
+}
+```
+
+:::warning
+The nonce must be incremented for any transaction that modifies account state. Without it, the same transaction could be replayed.
+:::
+
+## When proof generation fails
+
+Several operations cause proof generation to fail if preconditions aren't met:
+
+| Operation | Fails when |
+|-----------|-----------|
+| `remove_asset(asset)` | Asset not in vault or insufficient balance |
+| `get_balance(faucet_id)` | Referenced asset is non-fungible |
+| `get_procedure_root(index)` | Index out of bounds |
+| Any `assert!()` | Condition is false |
+
+When proof generation fails:
+1. The ZK circuit cannot produce a valid proof
+2. The transaction is rejected **before reaching the network**
+3. No state changes occur
+4. The client receives an error describing the failure
+
+## Example: ManagedWallet
+
+```rust
+#![no_std]
+#![feature(alloc_error_handler)]
+
+use miden::{component, output_note, Asset, AccountId, NoteIdx, Felt};
+
+#[component]
+struct ManagedWallet;
+
+#[component]
+impl ManagedWallet {
+ /// Receive an asset into the vault.
+ pub fn receive_asset(&mut self, asset: Asset) {
+ self.add_asset(asset);
+ }
+
+ /// Send an asset to an output note, with balance check.
+ pub fn send_asset(&mut self, asset: Asset, note_idx: NoteIdx) {
+ let removed = self.remove_asset(asset);
+ output_note::add_asset(removed, note_idx);
+ }
+
+ /// Query the balance of a fungible asset.
+ pub fn balance_of(&self, faucet_id: AccountId) -> Felt {
+ self.get_balance(faucet_id)
+ }
+}
+```
+
+To move assets out of an account, create [output notes](../notes/output-notes) with `output_note::add_asset`. For signature verification and nonce management, see [Authentication](./authentication).
+
+:::info API Reference
+Full API docs on docs.rs: [`miden`](https://docs.rs/miden/latest/miden/)
+:::
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/accounts/authentication.md b/versioned_docs/version-0.14/builder/smart-contracts/accounts/authentication.md
new file mode 100644
index 00000000..07b112c6
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/accounts/authentication.md
@@ -0,0 +1,100 @@
+---
+title: "Authentication"
+sidebar_position: 6
+description: "Authentication component pattern and nonce management for Miden accounts."
+---
+
+# Authentication
+
+Miden uses digital signatures for transaction authentication. Because transactions execute on the client rather than on-chain validators, the system needs a way to prove that a transaction was authorized by the account owner. Without authentication, anyone could construct a valid proof that transfers assets out of an account. The nonce prevents replay attacks — without it, a valid proof could be resubmitted to execute the same state change twice. For details on the cryptographic primitives, see [Cryptography](./cryptography).
+
+v0.14 unifies the previous per-scheme components (`AuthFalcon512Rpo`, `AuthEcdsaK256Keccak`, …) into a single scheme-agnostic [`AuthSingleSig`](https://docs.rs/miden-standards/latest/miden_standards/account/auth/struct.AuthSingleSig.html) component that takes an `AuthScheme` enum (`Falcon512Poseidon2` or `EcdsaK256Keccak`). The native hash function is Poseidon2, and the Falcon-512 verifier MASM module is `miden::core::crypto::dsa::falcon512_poseidon2`.
+
+## How authentication works
+
+The standards `AuthSingleSig` component stores two items under well-known names:
+
+| Storage slot | Name | Description |
+|---|---|---|
+| Public key | `miden::standards::auth::singlesig::pub_key` | Commitment to the account owner's public key |
+| Scheme ID | `miden::standards::auth::singlesig::scheme` | Which signature scheme to use (1 = ECDSA K256 Keccak, 2 = Falcon-512 Poseidon2) |
+
+During transaction execution the kernel invokes the `@auth_script`-annotated procedure on the account. For `AuthSingleSig`, that procedure loads both slots and delegates to `miden::standards::auth::signature::authenticate_transaction`, which:
+
+1. Increments the account nonce (even if the account state did not change — this is required for replay protection).
+2. Computes the transaction summary message: `hash([ACCOUNT_DELTA_COMMITMENT, INPUT_NOTES_COMMITMENT, OUTPUT_NOTES_COMMITMENT, [0, 0, ref_block_num, final_nonce]])`.
+3. Requests the signature from the advice provider and verifies it with the scheme indicated by the stored scheme ID.
+
+If verification fails, proof generation fails and the transaction is rejected before reaching the network. The signature itself isn't passed as a function argument — it's provided through the **advice provider**, a mechanism that supplies auxiliary data to the VM during proof generation. See [Advice Provider](../transactions/advice-provider) for the full API.
+
+## Attaching `AuthSingleSig` to an account
+
+On the client side, attach `AuthSingleSig` via `AccountBuilder::with_auth_component`. `miden-client` re-exports `AuthScheme` as `AuthSchemeId`:
+
+```rust
+use miden_client::{
+ account::{AccountBuilder, AccountStorageMode, AccountType, component::BasicWallet},
+ auth::{AuthSchemeId, AuthSecretKey, AuthSingleSig},
+};
+
+let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+let account = AccountBuilder::new(seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(
+ key_pair.public_key().to_commitment(),
+ AuthSchemeId::Falcon512Poseidon2,
+ ))
+ .with_component(BasicWallet)
+ .build()?;
+```
+
+If you import directly from `miden-protocol`, the same enum is called `AuthScheme` (`miden_protocol::account::auth::AuthScheme`) — `miden-client` just re-exports it under a friendlier name.
+
+## Writing a custom auth component
+
+If you need authentication logic beyond `AuthSingleSig` / `AuthMultisig`, you can write a custom auth component in Rust. Mark exactly one procedure per auth component with `#[auth_script]`. If the procedure returns without panicking, the transaction kernel treats authentication as successful. If it panics (for example via `assert!`), authentication fails.
+
+```rust
+#![no_std]
+#![feature(alloc_error_handler)]
+
+use miden::{component, Word};
+
+#[component]
+struct AuthComponent;
+
+#[component]
+impl AuthComponent {
+ #[auth_script]
+ pub fn verify(&self, _arg: Word) {
+ // Custom authentication checks go here.
+ //
+ // Returning normally = authentication succeeded.
+ // Panicking (e.g. `assert!(false)`) = authentication failed and
+ // the transaction will be rejected before proof generation finishes.
+ todo!()
+ }
+}
+```
+
+The kernel also increments the nonce automatically as part of the `@auth_script` contract — you do not need to call `self.incr_nonce()` from inside the procedure for replay protection.
+
+## Nonce management
+
+The nonce prevents replay attacks — each transaction must use a unique nonce. For accounts using the standards `AuthSingleSig` (or `AuthMultisig`) component, nonce increment is handled inside `authenticate_transaction`. If you implement a fully custom auth script, you are responsible for incrementing the nonce yourself and for binding it into the signed message.
+
+The nonce is committed into the transaction proof. If someone tries to replay a transaction, the nonce won't match the account's current nonce and verification will fail.
+
+Auth components are invoked automatically by the kernel — you do not call them directly from note scripts or [transaction scripts](../transactions/transaction-scripts). For access control and security patterns, see [Patterns](../patterns).
+
+:::info API Reference
+Full API docs on docs.rs: [`miden`](https://docs.rs/miden/latest/miden/), [`AuthSingleSig`](https://docs.rs/miden-standards/latest/miden_standards/account/auth/struct.AuthSingleSig.html), [`AuthScheme`](https://docs.rs/miden-protocol/latest/miden_protocol/account/auth/enum.AuthScheme.html)
+:::
+
+## Related
+
+- [Cryptography](./cryptography) — Falcon-512 / Poseidon2 verification and hashing primitives
+- [Advice Provider](../transactions/advice-provider) — supplying auxiliary data during proof generation
+- [Patterns](../patterns) — access control, rate limiting, and anti-patterns
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/accounts/components.md b/versioned_docs/version-0.14/builder/smart-contracts/accounts/components.md
new file mode 100644
index 00000000..7e012989
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/accounts/components.md
@@ -0,0 +1,172 @@
+---
+title: "Components"
+sidebar_position: 1
+description: "Define Miden account components using the #[component] macro — storage, methods, and auto-generated bindings."
+---
+
+# Components
+
+Components are the building blocks of Miden accounts. Each component defines a [storage](./storage) layout, exposes public methods, and can be composed with other components on the same account — for example, a wallet component + an auth component + custom logic. This modularity lets you reuse a wallet component across many accounts and test or upgrade components independently.
+
+## The `#[component]` macro
+
+Apply `#[component]` to both a struct definition and its impl block:
+
+```rust
+use miden::{component, felt, Felt, StorageMap, Word};
+
+#[component]
+struct CounterContract {
+ #[storage(description = "counter contract storage map")]
+ count_map: StorageMap,
+}
+
+#[component]
+impl CounterContract {
+ pub fn get_count(&self) -> Felt {
+ let key = Word::from_u64_unchecked(0, 0, 0, 1);
+ self.count_map.get(&key)
+ }
+
+ pub fn increment_count(&mut self) -> Felt {
+ let key = Word::from_u64_unchecked(0, 0, 0, 1);
+ let current_value: Felt = self.count_map.get(&key);
+ let new_value = current_value + felt!(1);
+ self.count_map.set(key, new_value);
+ new_value
+ }
+}
+```
+
+The macro generates:
+
+1. **Public API exports** describing the component's callable methods
+2. **Storage metadata** mapping slot names to slot IDs (derived from the component package + field name)
+3. **Runtime bindings** for the Miden execution environment
+
+## Struct definition
+
+The struct defines the component's storage layout:
+
+```rust
+#[component]
+struct MyContract {
+ #[storage(description = "owner account identifier")]
+ owner: Value,
+
+ #[storage(description = "user balances")]
+ balances: StorageMap,
+}
+```
+
+### Storage fields
+
+Each field must be either `Value` (single-slot) or `StorageMap` (map-slot), annotated with `#[storage]`:
+
+```rust
+#[storage(description = "human-readable description")]
+field_name: Value,
+
+#[storage(description = "human-readable description")]
+field_name: StorageMap,
+```
+
+The `description` is optional and becomes part of the generated metadata. Slot IDs are derived from the component package name (from `[package.metadata.component]`) and the field name, so **renaming a field changes the slot ID**. Ordering does not matter, and `slot(N)` is not supported.
+
+## Impl block — methods
+
+### Read methods (`&self`)
+
+Methods that take `&self` are **read-only** — they can query storage and account state but cannot modify anything:
+
+```rust
+pub fn get_balance(&self, depositor: AccountId) -> Felt {
+ let key = Word::from([depositor.prefix, depositor.suffix, felt!(0), felt!(0)]);
+ self.balances.get(&key)
+}
+```
+
+### Write methods (`&mut self`)
+
+Methods that take `&mut self` can **modify state** — write to storage, add/remove assets, create notes:
+
+```rust
+pub fn deposit(&mut self, asset: Asset) {
+ self.add_asset(asset);
+}
+```
+
+:::info ZK proof implications
+Read methods (`&self`) produce proofs that don't include state transitions. Write methods (`&mut self`) produce proofs that do. The distinction is enforced by the compiler and determines which kernel operations are available.
+:::
+
+### Private methods
+
+Methods without `pub` are private — they can be called from other methods in the same component but are not exported:
+
+```rust
+fn require_initialized(&self) {
+ let state: Word = self.initialized.read();
+ assert!(state[0] == felt!(1));
+}
+
+pub fn do_something(&mut self) {
+ self.require_initialized();
+ // ...
+}
+```
+
+### Supported parameter and return types
+
+Public methods can use SDK types (`Felt`, `Word`, `Asset`, `AccountId`, `NoteIdx`) and custom types annotated with [`#[export_type]`](./custom-types).
+
+## Auto-generated methods
+
+The `#[component]` macro automatically provides methods on `self` for account operations.
+
+### Mutation methods (`&mut self`)
+
+```rust
+// Add an asset to the account vault
+self.add_asset(asset: Asset) -> Asset
+
+// Remove an asset from the account vault
+self.remove_asset(asset: Asset) -> Asset
+
+// Increment the account nonce (replay protection)
+self.incr_nonce() -> Felt
+
+// Compute commitment of account state changes (read-only)
+self.compute_delta_commitment() -> Word
+
+// Check if a procedure was called during this transaction (read-only)
+self.was_procedure_called(proc_root: Word) -> bool
+```
+
+### Read-only methods (`&self`)
+
+```rust
+// Get the account ID
+self.get_id() -> AccountId
+
+// Get the account nonce
+self.get_nonce() -> Felt
+
+// Get fungible asset balance for a faucet
+self.get_balance(faucet_id: AccountId) -> Felt
+
+// Check non-fungible asset ownership
+self.has_non_fungible_asset(asset: Asset) -> bool
+
+// Get storage and vault commitments
+self.get_vault_root() -> Word
+self.compute_commitment() -> Word
+self.compute_storage_commitment() -> Word
+// ... and more (see API Reference)
+```
+
+For the full list of auto-generated methods, see [Account Operations](./account-operations). To export your own types for use in public method signatures, see [Custom Types](./custom-types).
+
+:::info API Reference
+Full API docs on docs.rs: [`miden`](https://docs.rs/miden/latest/miden/) (top-level — `#[component]` macro)
+:::
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/accounts/cryptography.md b/versioned_docs/version-0.14/builder/smart-contracts/accounts/cryptography.md
new file mode 100644
index 00000000..803c2e52
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/accounts/cryptography.md
@@ -0,0 +1,62 @@
+---
+title: "Cryptography"
+sidebar_position: 5
+description: "RPO-Falcon512 signature verification and hashing primitives in Miden contracts."
+---
+
+# Cryptography
+
+The Miden SDK exposes cryptographic primitives for signature verification and hashing. These are low-level functions used by authentication components and anywhere message digests or hash-based commitments are needed.
+
+## RPO-Falcon512 verification
+
+The core function for signature verification:
+
+```rust
+use miden::rpo_falcon512_verify;
+
+// Verify a Falcon512 signature
+// pk: RPO256 hash of the public key
+// msg: RPO256 hash of the message
+rpo_falcon512_verify(pk, msg);
+```
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `pk` | `Word` | RPO256 hash of the signer's public key |
+| `msg` | `Word` | RPO256 hash of the message being verified |
+
+The function panics (proof generation fails) if the signature is invalid.
+
+:::info Where's the signature?
+The actual signature data is loaded onto the advice stack by the host. The `rpo_falcon512_verify` function reads it from there. You don't pass the signature as an argument.
+:::
+
+## Hashing
+
+`hash_words` creates a message digest from a slice of Words:
+
+```rust
+use miden::hash_words;
+
+// Hash multiple Words into a Digest
+let words = [commitment, nonce_word, extra_data];
+let digest: Word = hash_words(&words).into();
+```
+
+Other available hash functions:
+
+```rust
+use miden::{blake3_hash, sha256_hash};
+
+// BLAKE3 (32-byte input -> 32-byte output)
+let hash: [u8; 32] = blake3_hash(input_bytes);
+
+// SHA256 (32-byte input -> 32-byte output)
+let hash: [u8; 32] = sha256_hash(input_bytes);
+```
+
+## Related
+
+- [Authentication](./authentication) — auth component pattern and nonce management
+- [Advice Provider](../transactions/advice-provider) — supplying auxiliary data during proof generation
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/accounts/custom-types.md b/versioned_docs/version-0.14/builder/smart-contracts/accounts/custom-types.md
new file mode 100644
index 00000000..1fc5ead0
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/accounts/custom-types.md
@@ -0,0 +1,109 @@
+---
+title: "Custom Types"
+sidebar_position: 3
+description: "Export custom structs and enums for use in public component methods with #[export_type]."
+---
+
+# Custom Types
+
+When public [component](./components) methods use custom structs or enums, those types must be annotated with `#[export_type]` so the compiler can include them in the component's public API. Types used only internally (in private methods or local variables) don't need this annotation.
+
+:::tip
+If you forget `#[export_type]` on a public API type, the compiler will emit an error telling you to add it.
+:::
+
+## Exporting structs
+
+Struct fields must be public and use types that are either SDK types (`Felt`, `Word`, `Asset`, etc.) or themselves marked with `#[export_type]`:
+
+```rust
+use miden::{export_type, Felt, Word, Asset, component};
+
+#[export_type]
+pub struct StructA {
+ pub foo: Word,
+ pub asset: Asset,
+}
+
+#[export_type]
+pub struct StructB {
+ pub bar: Felt,
+ pub baz: Felt,
+}
+
+#[component]
+struct MyAccount;
+
+#[component]
+impl MyAccount {
+ pub fn process(&self, a: StructA, asset: Asset) -> StructB {
+ StructB {
+ bar: a.foo[0],
+ baz: a.foo[1],
+ }
+ }
+}
+```
+
+## Exporting enums
+
+Enums use the same annotation. Enum variants can be unit variants:
+
+```rust
+use miden::{export_type, Felt};
+
+#[export_type]
+pub enum Status {
+ Active,
+ Inactive,
+}
+```
+
+## Nested types
+
+Exported types can reference other exported types:
+
+```rust
+#[export_type]
+pub struct Inner {
+ pub value: Felt,
+}
+
+#[export_type]
+pub struct Outer {
+ pub nested: Inner,
+}
+```
+
+The compiler resolves references regardless of declaration order.
+
+## Types in submodules
+
+Custom types can be defined in submodules. Each type still needs `#[export_type]`:
+
+```rust
+pub mod my_types {
+ use miden::{Felt, export_type};
+
+ #[export_type]
+ pub struct StructC {
+ pub inner1: Felt,
+ pub inner2: Felt,
+ }
+}
+```
+
+## Rules summary
+
+| Rule | Details |
+|------|---------|
+| When needed | Any custom type in a `pub fn` signature on a `#[component]` impl |
+| Struct fields | Must be `pub` |
+| Allowed field types | `Felt`, `Word`, `Asset`, `AccountId`, or other `#[export_type]` types |
+| Enums | Unit variants supported |
+| Modules | Types in submodules work — just apply `#[export_type]` to each |
+| Order | Declaration order doesn't matter — forward references are resolved |
+
+:::info API Reference
+Full API docs on docs.rs: [`miden`](https://docs.rs/miden/latest/miden/) (`#[export_type]` macro)
+:::
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/accounts/introduction.md b/versioned_docs/version-0.14/builder/smart-contracts/accounts/introduction.md
new file mode 100644
index 00000000..e9a708a2
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/accounts/introduction.md
@@ -0,0 +1,73 @@
+---
+title: "What are Accounts?"
+sidebar_position: 0
+description: "Accounts are the primary actors in Miden — they store code, state, and assets, and execute transactions locally with private state."
+---
+
+# What are Accounts?
+
+Accounts are the primary actors in Miden. Every entity on the network — wallets, smart contracts, token faucets — is an account. Unlike traditional blockchains where user wallets and smart contracts are fundamentally different, Miden treats them all as programmable accounts with the same structure.
+
+Each account is an independent state machine that executes transactions locally and generates a zero-knowledge proof of correct execution. This means accounts never share a global execution environment — they run in isolation, which enables parallel execution and privacy by default.
+
+## Anatomy of an account
+
+Every account has four parts:
+
+| Part | Description |
+|------|-------------|
+| **Code** | One or more [components](./components.md) that define the account's behavior — its public API and internal logic |
+| **Storage** | Persistent state — up to 255 typed [slots](./storage.md) of `Value` or `StorageMap` |
+| **Vault** | The fungible and non-fungible assets the account holds |
+| **Nonce** | A counter that increments exactly once per state change, providing replay protection |
+
+The network doesn't store the full account state. Instead, it stores cryptographic commitments — hashes of the code, storage, and vault (see [account design](/core-concepts/protocol/account/)). Only the account owner (or a public account's observers) sees the actual data.
+
+## Components, not contracts
+
+On Ethereum, a smart contract is a single monolithic unit of code deployed to an address. On Miden, accounts are composed of **components** — reusable modules that each contribute their own storage layout and exported procedures.
+
+```rust
+use miden::{component, Asset};
+
+#[component]
+struct MyWallet;
+
+#[component]
+impl MyWallet {
+ pub fn receive_asset(&mut self, asset: Asset) {
+ self.add_asset(asset);
+ }
+}
+```
+
+An account can have multiple components. For example, a DeFi account might combine a wallet component (for holding assets), an auth component (for signature verification), and custom application logic — all in a single account. Components communicate with each other through [cross-component calls](../cross-component-calls.md) using WIT (WebAssembly Interface Types) bindings.
+
+## Account types
+
+Accounts are configured by type and storage mode:
+
+| Type | Description |
+|------|-------------|
+| `RegularAccountUpdatableCode` | Standard account — code can be updated after deployment |
+| `RegularAccountImmutableCode` | Account with fixed code — cannot be changed after deployment |
+| `FungibleFaucet` | Mints and burns fungible tokens |
+| `NonFungibleFaucet` | Mints and burns non-fungible tokens (NFTs) |
+
+Storage mode controls privacy:
+
+| Mode | Description |
+|------|-------------|
+| **Public** | Full state is stored on-chain and visible to everyone — suitable for shared protocols like DEXs and faucets |
+| **Private** | Only a state commitment is stored on-chain — the actual data stays with the account owner |
+
+## How accounts differ from EVM contracts
+
+| | EVM | Miden |
+|---|---|---|
+| **Execution** | Every validator re-executes every transaction | Account owner executes locally, submits a ZK proof |
+| **State visibility** | All state variables are public on-chain | State is private by default (only commitments on-chain) |
+| **Code structure** | Monolithic contract deployed to an address | Multiple reusable components composed into one account |
+| **Identity** | Wallets are EOAs, contracts are separate | Everything is an account — wallets are smart contracts |
+| **Failure** | `revert` consumes gas, leaves an on-chain trace | Proof cannot be generated — no on-chain trace, no cost |
+
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/accounts/storage.md b/versioned_docs/version-0.14/builder/smart-contracts/accounts/storage.md
new file mode 100644
index 00000000..9d284883
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/accounts/storage.md
@@ -0,0 +1,181 @@
+---
+title: "Storage"
+sidebar_position: 2
+description: "Persistent state management with Value slots and StorageMaps in Miden smart contracts."
+---
+
+# Storage
+
+Miden accounts have persistent storage organized into up to 255 name-addressable slots. Each slot holds either a single [`Word`](../types) (via `Value`) or a key-value map (via `StorageMap`). Slots are identified by `StorageSlotId` values derived from slot names, which in turn are derived from the component package name and the field name. Renaming a field changes the slot ID and is a breaking change for stored data.
+
+## Storage slots
+
+An account can have up to **255 storage slots**. Each slot is declared as a field on your component struct:
+
+```rust
+#[component]
+struct MyContract {
+ #[storage(description = "configuration")]
+ config: Value,
+
+ #[storage(description = "user balances")]
+ balances: StorageMap,
+}
+```
+Slot IDs are derived from the component package name and the field name. Ordering does not matter, and `slot(N)` is not supported.
+
+## Value — Single-slot storage
+
+A `Value` stores a single `Word` (4 Felts) at a fixed slot. It provides `read()` and `write()` methods. Note that `write()` returns the **previous** slot value, not the value written.
+
+### Reading
+
+```rust
+pub fn get_config(&self) -> Word {
+ self.config.read()
+}
+
+pub fn is_initialized(&self) -> bool {
+ let state: Word = self.initialized.read();
+ state[0] == felt!(1)
+}
+```
+
+### Writing
+
+`write()` returns the **previous value**:
+
+```rust
+pub fn initialize(&mut self) {
+ let old: Word = self.initialized.write(
+ Word::from([felt!(1), felt!(0), felt!(0), felt!(0)])
+ );
+ // old contains the previous value of the slot
+}
+```
+
+### Packing multiple values
+
+Since each slot holds a `Word` (4 Felts), pack related values together:
+
+```rust
+// Store limits as [max_per_tx, daily_max, 0, 0]
+pub fn set_limits(&mut self, max_per_tx: u64, daily_max: u64) {
+ self.limits.write(Word::from([
+ Felt::from_u64_unchecked(max_per_tx),
+ Felt::from_u64_unchecked(daily_max),
+ felt!(0),
+ felt!(0),
+ ]));
+}
+
+// Read individual fields
+pub fn get_max_per_tx(&self) -> u64 {
+ let limits: Word = self.limits.read();
+ limits[0].as_u64()
+}
+```
+
+## StorageMap — Key-value storage
+
+A `StorageMap` stores key-value pairs where both keys and values are `Word`-sized. It provides `get()` and `set()` methods.
+
+### Reading from a map
+
+```rust
+// Get returns a Felt (the last element of the stored Word)
+pub fn get_balance(&self, account_id: AccountId) -> Felt {
+ let key = Word::from([account_id.prefix, account_id.suffix, felt!(0), felt!(0)]);
+ self.balances.get(&key)
+}
+```
+
+When you call `get()` with a `Felt` return type, it returns index 3 of the stored `Word` (via `From for Felt`). This means single-value maps should store their value at index 3. For the full `Word`, specify the return type:
+
+```rust
+// Get the full Word value
+let full_value: Word = self.my_map.get(&key);
+```
+
+### Writing to a map
+
+`set()` returns the **previous value**:
+
+```rust
+pub fn set_balance(&mut self, account_id: AccountId, amount: Felt) {
+ let key = Word::from([account_id.prefix, account_id.suffix, felt!(0), felt!(0)]);
+ let old: Felt = self.balances.set(key, amount);
+ // old contains the previous balance
+}
+```
+
+### Map with Word values
+
+```rust
+pub fn store_data(&mut self, key: Word, value: Word) {
+ let old: Word = self.data_map.set(key, value);
+}
+
+pub fn read_data(&self, key: Word) -> Word {
+ self.data_map.get(&key)
+}
+```
+
+## Storage layout conventions
+
+Each slot and each Felt within a Word can be documented with inline comments on the struct:
+
+```rust
+#[component]
+struct TokenVault {
+ /// Config slot:
+ /// [0] = max_supply (u64)
+ /// [1] = is_paused (0 or 1)
+ /// [2] = decimals
+ /// [3] = unused
+ #[storage(description = "vault configuration")]
+ config: Value,
+
+ /// State slot:
+ /// [0] = total_supply (u64)
+ /// [1] = total_holders (u64)
+ /// [2] = last_mint_block
+ /// [3] = unused
+ #[storage(description = "vault state")]
+ state: Value,
+
+ /// Balance map slot:
+ /// Key: [account_prefix, account_suffix, 0, 0]
+ /// Value: [balance, last_activity_block, 0, 0]
+ #[storage(description = "user balances")]
+ balances: StorageMap,
+}
+```
+
+## Low-level storage access
+
+Direct storage access outside the component traits uses the bindings:
+
+```rust
+use miden::storage;
+
+// Direct slot access
+let value: Word = storage::get_item(slot_id);
+let old: Word = storage::set_item(slot_id, new_value);
+
+// Direct map access
+let value: Word = storage::get_map_item(slot_id, &key);
+let old: Word = storage::set_map_item(slot_id, key, value);
+
+// Initial values (at transaction start)
+let initial: Word = storage::get_initial_item(slot_id);
+let initial: Word = storage::get_initial_map_item(slot_id, &key);
+```
+
+These functions return values from before any modifications in the current transaction.
+
+For Felt and Word conversion details, see [Types](../types). To export your own types for public APIs, see [Custom Types](./custom-types). For common storage patterns like access control and rate limiting, see [Patterns](../patterns).
+
+:::info API Reference
+Full API docs on docs.rs: [`miden::storage`](https://docs.rs/miden/latest/miden/storage/)
+:::
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/cross-component-calls.md b/versioned_docs/version-0.14/builder/smart-contracts/cross-component-calls.md
new file mode 100644
index 00000000..ff3479d5
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/cross-component-calls.md
@@ -0,0 +1,184 @@
+---
+title: "Cross-Component Calls"
+sidebar_position: 5.5
+description: "Call methods across account components and from note scripts."
+---
+
+# Cross-Component Calls
+
+Miden [components](./accounts/components) can call each other's methods. Since accounts can have multiple components (e.g., wallet + auth + custom logic), those components need to communicate. [Note scripts](./notes/note-scripts) also need to call methods on the account's components to transfer assets.
+
+## How it works
+
+When you build a component with `miden build`, the compiler generates an interface describing its public methods. Other projects can import this interface to call those methods.
+
+```
+counter-contract (component)
+ → generates interface
+ → counter-note imports the interface
+ → calls counter_contract::get_count()
+```
+
+## Using `#[note]` macros (recommended)
+
+The simplest way to make cross-component calls from note scripts is through the `#[note]` macro with an `Account` parameter:
+
+```rust
+use crate::bindings::Account;
+use miden::{active_note, Asset};
+
+#[note]
+struct P2idNote;
+
+#[note]
+impl P2idNote {
+ #[note_script]
+ pub fn run(self, _arg: Word, account: &mut Account) {
+ // Iterate over the note's assets and transfer each to the account
+ for asset in active_note::get_assets() {
+ account.receive_asset(asset);
+ }
+ }
+}
+```
+
+The `_arg: Word` parameter is the note's first input Word, passed automatically when the note is consumed. It's unused in this example (prefixed with `_`), but note scripts can use it for recipient-specific data like expected account IDs or amounts.
+
+The `Account` type is auto-generated from the bindings of the dependent component. Its methods correspond to the component's public methods.
+
+## Using `generate!()` directly
+
+For more control (e.g., calling multiple components), use `miden::generate!()` to manually generate bindings:
+
+```rust title="cross-ctx-note/src/lib.rs"
+#![no_std]
+#![feature(alloc_error_handler)]
+
+#[global_allocator]
+static ALLOC: miden::BumpAlloc = miden::BumpAlloc::new();
+
+use miden::*;
+
+miden::generate!();
+bindings::export!(MyNote);
+
+use bindings::{
+ exports::miden::base::note_script::Guest,
+ miden::cross_ctx_account::foo::process_felt,
+};
+
+struct MyNote;
+
+impl Guest for MyNote {
+ fn run(_arg: Word) {
+ let input = Felt::from_u32(11);
+ let output = process_felt(input);
+ assert_eq!(output, felt!(53));
+ }
+}
+```
+
+Key points:
+- `miden::generate!()` generates the `bindings` module from component interfaces
+- `bindings::export!(MyNote)` registers `MyNote` as the implementation
+- `process_felt` is imported from the `cross-ctx-account` component
+- The import path follows the package structure: `bindings::miden::cross_ctx_account::foo::process_felt`
+
+## Cargo.toml configuration
+
+Cross-component calls require two dependency declarations:
+
+### 1. Miden dependencies (for `cargo-miden` linking)
+
+```toml
+[package.metadata.miden.dependencies]
+"miden:basic-wallet" = { path = "../basic-wallet" }
+```
+
+This tells `cargo-miden` where to find the component for linking.
+
+### 2. Component dependencies (for binding generation)
+
+```toml
+[package.metadata.component.target.dependencies]
+"miden:basic-wallet" = { path = "../basic-wallet/target/generated-wit/" }
+```
+
+This points to the generated interface files used to create Rust bindings.
+
+### Complete example
+
+```toml title="counter-note/Cargo.toml"
+[package]
+name = "counter-note"
+version = "0.1.0"
+edition = "2024"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+miden = { path = "../../sdk/sdk" }
+
+[package.metadata.miden]
+project-kind = "note-script"
+
+[package.metadata.component]
+package = "miden:counter-note"
+
+# Miden dependency — the component we want to call
+[package.metadata.miden.dependencies]
+"miden:counter-contract" = { path = "../counter-contract" }
+
+# Component dependency — generated interface for binding generation
+[package.metadata.component.target.dependencies]
+"miden:counter-account" = { path = "../counter-contract/target/generated-wit/" }
+```
+
+:::info Build order matters
+The dependent component must be built first so its interface files exist. Build `counter-contract` before building `counter-note`.
+:::
+
+## Example: Counter note calling counter contract
+
+```rust title="counter-note/src/lib.rs"
+#![no_std]
+#![feature(alloc_error_handler)]
+
+use miden::*;
+
+// Import the counter contract's generated bindings
+use crate::bindings::miden::counter_contract::counter_contract;
+
+#[note]
+struct CounterNote;
+
+#[note]
+impl CounterNote {
+ #[note_script]
+ pub fn run(self, _arg: Word) {
+ // Call get_count() on the counter contract component
+ let initial_value = counter_contract::get_count();
+
+ // Call increment_count() — modifies the account's storage
+ counter_contract::increment_count();
+
+ // Verify the count increased
+ let expected_value = initial_value + Felt::from_u32(1);
+ let final_value = counter_contract::get_count();
+ assert_eq!(final_value, expected_value);
+ }
+}
+```
+
+## When to use which pattern
+
+| Pattern | Use when |
+|---------|----------|
+| `#[note]` with `&mut Account` | Note needs to call a single account component's methods |
+| `generate!()` + `Guest` trait | Note needs to call multiple components or needs full control |
+| Direct module imports | Calling specific functions from a known component interface |
+
+:::info API Reference
+Full API docs on docs.rs: [`miden`](https://docs.rs/miden/latest/miden/) (`generate!()` macro)
+:::
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/index.md b/versioned_docs/version-0.14/builder/smart-contracts/index.md
new file mode 100644
index 00000000..beffdb67
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/index.md
@@ -0,0 +1,42 @@
+---
+title: "Miden Smart Contracts"
+description: "Reference documentation for building Miden smart contracts in Rust using the Miden SDK."
+pagination_prev: null
+---
+
+# Miden Smart Contracts
+
+This section is the complete reference for building smart contracts on Miden using Rust and the Miden SDK. 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.
+
+## Core concepts
+
+
+
+ Components, storage, custom types, operations, cryptography, and authentication.
+
+
+ Programmable UTXOs for asset transfers.
+
+
+ Transaction context, scripts, and the advice provider.
+
+
+ Calling methods across account components and from note scripts.
+
+
+
+## Reference
+
+
+
+ Core types: Felt, Word, AccountId, NoteId, and more.
+
+
+ Access control, rate limiting, spending limits, and anti-patterns.
+
+
+ Complete API documentation for the miden crate.
+
+
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/notes/_category_.json b/versioned_docs/version-0.14/builder/smart-contracts/notes/_category_.json
new file mode 100644
index 00000000..aa233038
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/notes/_category_.json
@@ -0,0 +1,8 @@
+{
+ "label": "Notes",
+ "position": 4.5,
+ "link": {
+ "type": "generated-index",
+ "slug": "/builder/smart-contracts/notes"
+ }
+}
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/notes/introduction.md b/versioned_docs/version-0.14/builder/smart-contracts/notes/introduction.md
new file mode 100644
index 00000000..f9ac4f41
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/notes/introduction.md
@@ -0,0 +1,77 @@
+---
+title: "What are Notes?"
+sidebar_position: 0
+description: "Miden's cross-account communication mechanism — programmable UTXOs that carry assets, execute scripts, and trigger logic on consuming accounts."
+---
+
+# What are Notes?
+
+Notes are Miden's primary mechanism for cross-account communication — they carry assets, execute programmable logic, and trigger state changes on the consuming account. Like UTXOs, notes are created and consumed atomically. Unlike Bitcoin's UTXOs, each Miden note carries an arbitrary executable script — written in Rust — that runs when the note is consumed, enabling programmable conditions far beyond simple locking scripts.
+
+While asset transfers are the most common use of notes, notes are how accounts communicate with one another in general: a note can trigger a counter increment, initiate a swap, delegate an operation, or carry arbitrary data to be acted on by the recipient's logic.
+
+Assets never transfer directly between accounts. Instead, they always move through notes. This indirection is what makes Miden private: the network sees notes being created and consumed, but it can't link sender and recipient accounts because those operations happen in separate transactions.
+
+## Anatomy of a note
+
+Every note has four parts:
+
+| Part | Description |
+|------|-------------|
+| **Assets** | The fungible or non-fungible tokens the note carries |
+| **Script** | Code that executes when the note is consumed — determines who can claim it and what side effects occur |
+| **Storage** | Custom data stored with the note that the script can read at consumption time (e.g., a target account ID, an expiration block) |
+| **Metadata** | Sender ID, note tag (for discovery routing), and auxiliary data |
+
+The **recipient** is a cryptographic hash that encodes who can consume the note. When creating notes programmatically (via [`output_note::create`](./output-notes#create-a-note)), you compute a `Recipient` from the note's serial number, script root, and storage commitment:
+
+```
+recipient = hash(hash(hash(serial_num, [0;4]), script_root), storage_commitment)
+```
+
+Only someone who knows these values can construct a valid consumption proof. See [Computing a Recipient](./output-notes#computing-a-recipient) for the SDK API.
+
+## The two-transaction model
+
+Unlike Ethereum where a transfer is a single atomic call, Miden transfers happen across two separate transactions:
+
+```
+Transaction 1 (Sender) Transaction 2 (Recipient)
+┌─────────────────────────┐ ┌─────────────────────────┐
+│ 1. Create note │ │ 1. Discover note │
+│ 2. Attach assets │ │ 2. Consume note │
+│ 3. Note published │──────────▶│ 3. Script runs │
+│ (on-chain or private) │ │ 4. Assets move to vault │
+│ │ │ 5. Note nullified │
+└─────────────────────────┘ └─────────────────────────┘
+```
+
+**Transaction 1**: The sender's account creates an output note, attaches assets to it, and the note is published (either on-chain or kept private).
+
+**Transaction 2**: The recipient discovers the note, consumes it in their own transaction, the note script runs and verifies the consumer is authorized, and assets transfer into the recipient's vault. A **nullifier** is recorded to prevent the same note from being consumed again (see [note design](/core-concepts/protocol/note)).
+
+This separation is what enables privacy and parallelism — the two transactions are independent and unlinkable from the network's perspective.
+
+## Public vs. private notes
+
+Notes come in two visibility modes:
+
+| Mode | Description |
+|------|-------------|
+| **Public** | The note's full data (assets, script, storage) is stored by the Miden network and visible on-chain. Anyone can discover and attempt to consume it. |
+| **Private** | Only a commitment (hash) is stored on-chain. The actual note data must be communicated off-chain between sender and recipient. |
+
+Private notes provide stronger privacy guarantees — the network can't even see what assets a note carries — but they require the sender and recipient to have a communication channel outside the protocol.
+
+Miden provides built-in note patterns (P2ID, P2IDE, SWAP) for common transfer scenarios — see [Standard Note Types](./note-types). You can also write fully custom note scripts for arbitrary consumption logic.
+
+## How notes differ from EVM transfers
+
+| | EVM | Miden |
+|---|---|---|
+| **Transfer model** | Single `transfer()` call on a token contract | Two transactions: create note, then consume note |
+| **Privacy** | Sender, recipient, and amount are public | Transactions are unlinkable; private notes hide all data |
+| **Programmability** | Token contracts control transfer logic | Each note carries its own script with custom conditions |
+| **Failure** | Revert on-chain, gas consumed | Proof can't be generated — no on-chain trace |
+| **Parallelism** | Transfers contend for contract state | Notes are independent — unlimited parallel creation |
+
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/notes/note-scripts.md b/versioned_docs/version-0.14/builder/smart-contracts/notes/note-scripts.md
new file mode 100644
index 00000000..17160e9f
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/notes/note-scripts.md
@@ -0,0 +1,153 @@
+---
+title: "Note Scripts"
+sidebar_position: 1
+description: "Write note scripts using #[note] and #[note_script] macros to define logic that executes when notes are consumed."
+---
+
+# Note Scripts
+
+Note scripts define the logic that executes when a note is consumed. They determine **who** can consume a note and **what happens** to its assets.
+
+## The `#[note]` pattern
+
+A note script consists of a struct (holding note inputs) and an impl block with a `#[note_script]` method:
+
+```rust
+use miden::{AccountId, Word, active_note, note};
+
+#[note]
+struct MyNote {
+ target_account_id: AccountId,
+}
+
+#[note]
+impl MyNote {
+ #[note_script]
+ pub fn run(self, _arg: Word, account: &mut Account) {
+ // Script logic here
+ }
+}
+```
+
+The `#[note]` macro:
+1. Deserializes note inputs into struct fields
+2. Exports the `run` function as the note's entry point
+
+## Struct fields as note inputs
+
+Fields on the `#[note]` struct are populated from the note's input data when the note is consumed:
+
+```rust
+#[note]
+struct MyNote {
+ target_account_id: AccountId, // Deserialized from note inputs
+}
+```
+
+The compiler maps struct fields to note inputs based on their order and type. Supported field types include `AccountId`, `Felt`, `Word`, and other SDK types.
+
+If you don't need inputs, use a unit struct:
+
+```rust
+#[note]
+struct CounterNote;
+```
+
+## `#[note_script]` method requirements
+
+The `#[note_script]` method has specific signature constraints:
+
+| Constraint | Details |
+|------------|---------|
+| Receiver | `self` (by value only — not `&self` or `&mut self`) |
+| Return type | `()` |
+| Required arg | One `Word` argument (the note script argument) |
+| Account arg | `&Account` or `&mut Account` for calling account methods |
+
+### Parameter ordering
+
+The `Word` and `&mut Account` parameters can appear in either order:
+
+```rust
+// Both are valid:
+pub fn run(self, _arg: Word, account: &mut Account) { ... }
+pub fn run(self, account: &mut Account, _arg: Word) { ... }
+```
+
+### With account access
+
+When you include `&mut Account` (or `&Account`), the note script can call methods on the account's components:
+
+```rust
+#[note_script]
+pub fn run(self, _arg: Word, account: &mut Account) {
+ let assets = active_note::get_assets();
+ for asset in assets {
+ account.receive_asset(asset); // Cross-component call
+ }
+}
+```
+
+The `Account` type is auto-generated from the bindings of the dependent component — see [Cross-Component Calls](../cross-component-calls).
+
+### Without account access
+
+Use this pattern for **trigger or command notes** that carry no assets and only execute logic (e.g., calling a counter contract). If your note transfers assets, include `&mut Account`.
+
+```rust
+#[note_script]
+pub fn run(self, _arg: Word) {
+ // For logic-only notes that carry no assets.
+ // Cannot call account methods — see the counter note example below.
+}
+```
+
+## Example: Counter note (cross-component calls)
+
+A note that calls methods on the account's component:
+
+:::note
+All note script crates require `#![no_std]` and `#![feature(alloc_error_handler)]` at the crate root. These are omitted from examples for brevity.
+:::
+
+```rust title="counter-note/src/lib.rs"
+use miden::*;
+
+// generated by the build process from the counter component's interface
+use crate::bindings::miden::counter_contract::counter_contract;
+
+#[note]
+struct CounterNote;
+
+#[note]
+impl CounterNote {
+ #[note_script]
+ pub fn run(self, _arg: Word) {
+ let initial_value = counter_contract::get_count();
+ counter_contract::increment_count();
+ let expected_value = initial_value + Felt::from_u32(1);
+ let final_value = counter_contract::get_count();
+ assert_eq!(final_value, expected_value);
+ }
+}
+```
+
+This note doesn't take `&mut Account` — instead it calls the counter contract's methods directly through generated bindings. See [Cross-Component Calls](../cross-component-calls).
+
+## Cargo.toml for note scripts
+
+Note scripts require `project-kind = "note-script"` and must declare dependencies on any account components they interact with:
+
+```toml
+[package.metadata.miden]
+project-kind = "note-script"
+
+# Miden dependencies — account components this note calls
+[package.metadata.miden.dependencies]
+"miden:basic-wallet" = { path = "../basic-wallet" }
+```
+
+## Related
+
+- [Cross-Component Calls](../cross-component-calls) — how `bindings::Account` and `counter_contract::` calls work
+- [Transaction Context](../transactions/transaction-context) — transaction scripts with `#[tx_script]`
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/notes/note-types.md b/versioned_docs/version-0.14/builder/smart-contracts/notes/note-types.md
new file mode 100644
index 00000000..0d9d7d25
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/notes/note-types.md
@@ -0,0 +1,166 @@
+---
+title: "Standard Note Types"
+sidebar_position: 3
+description: "Built-in note types from miden-standards: P2ID, P2IDE (with expiration), and SWAP (atomic exchange)."
+---
+
+# Standard Note Types
+
+The `miden-standards` crate provides built-in note patterns for common asset transfer scenarios. These are pre-compiled note scripts you can use directly via the builder API in client code.
+
+## P2ID (Pay to ID)
+
+The most common pattern — a note that can only be consumed by a specific account. The note script checks that the consuming account's ID matches the target, then transfers all assets.
+
+### When to use
+
+Use P2ID for standard asset transfers where only the intended recipient should be able to consume the note. This is the most common note type.
+
+:::info
+P2ID notes use `P2idNote::create` from the `miden-standards` crate (`miden_standards::note::P2idNote`). The script is pre-compiled MASM — use the builder API to create P2ID notes in client code.
+:::
+
+### How it works
+
+1. Creator creates a P2ID note containing the assets and the target account ID as a note storage item
+2. Consumer's transaction processes the note — the script verifies the consuming account's ID matches the target
+3. If the IDs match, all assets transfer to the consuming account; otherwise proof generation fails
+
+### Note storage
+
+| Item | Type | Description |
+|------|------|-------------|
+| `target_account_id` | `AccountId` | The account allowed to consume this note |
+
+### Builder API
+
+```rust
+use miden_standards::note::P2idNote;
+
+P2idNote::create(
+ sender, // AccountId: who sends the note
+ target, // AccountId: the only account that can consume this note
+ assets, // Vec: assets to attach
+ note_type, // NoteType: Public or Private
+ attachment, // NoteAttachment: auxiliary data
+ rng, // &mut impl FeltRng
+) -> Result
+```
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `sender` | `AccountId` | Account sending the note |
+| `target` | `AccountId` | The only account that can consume this note |
+| `assets` | `Vec` | Assets to attach to the note |
+| `note_type` | `NoteType` | `Public` or `Private` |
+| `attachment` | `NoteAttachment` | Auxiliary data for the note |
+| `rng` | `&mut impl FeltRng` | Random number generator |
+
+## P2IDE (Pay to ID with Expiration)
+
+P2IDE extends P2ID with a timelock and a reclaim window. The note can't be consumed before `timelock_height`, and if the target hasn't consumed it by `reclaim_height`, the creator can reclaim the assets.
+
+### When to use
+
+Use P2IDE when the sender wants the option to reclaim assets if the recipient doesn't consume the note within a time window.
+
+:::info
+P2IDE notes use `P2ideNote::create` from the `miden-standards` crate (`miden_standards::note::P2ideNote`). The script is pre-compiled MASM — use the builder API to create P2IDE notes in client code.
+:::
+
+### How it works
+
+1. Creator creates a P2IDE note with the target account ID, a timelock height, and a reclaim height as note storage items
+2. **Target consumes after `timelock_height`** — assets transfer to the target account
+3. **Creator reclaims after `reclaim_height`** — assets return to the creator
+4. **Before timelock or between timelock and reclaim by a non-target** — any consumption attempt fails (proof generation fails)
+
+### Note storage
+
+| Item | Type | Description |
+|------|------|-------------|
+| `target_account_id_prefix` | `Felt` | Target account ID prefix |
+| `target_account_id_suffix` | `Felt` | Target account ID suffix |
+| `reclaim_height` | `Felt` | Block height after which the creator can reclaim |
+| `timelock_height` | `Felt` | Block height before which the note can't be consumed |
+
+### Builder API
+
+```rust
+use miden_standards::note::{P2ideNote, P2ideNoteStorage};
+
+P2ideNote::create(
+ sender, // AccountId: who sends the note
+ P2ideNoteStorage::new(target, reclaim_height, timelock_height),
+ assets, // Vec: assets to attach
+ note_type, // NoteType: Public or Private
+ attachment, // NoteAttachment: auxiliary data
+ rng, // &mut impl FeltRng
+) -> Result
+```
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `sender` | `AccountId` | Account sending the note |
+| `target` | `AccountId` | The only account that can consume this note (set on `P2ideNoteStorage`) |
+| `reclaim_height` | `Option` | Block height after which sender can reclaim; `None` = no reclaim (set on `P2ideNoteStorage`) |
+| `timelock_height` | `Option` | Block height before which note can't be consumed; `None` = no timelock (set on `P2ideNoteStorage`) |
+| `assets` | `Vec` | Assets to attach to the note |
+| `note_type` | `NoteType` | `Public` or `Private` |
+| `attachment` | `NoteAttachment` | Auxiliary data for the note |
+| `rng` | `&mut impl FeltRng` | Random number generator |
+
+## SWAP (Atomic Exchange)
+
+SWAP enables atomic asset exchange. The creator offers one asset; any consumer who provides the requested asset in return can consume the note. The swap is atomic — both sides happen in a single transaction or neither does.
+
+### When to use
+
+Use SWAP for trustless atomic exchanges where two parties trade assets without intermediaries.
+
+:::info
+SWAP notes use `SwapNote::create` from the `miden-standards` crate (`miden_standards::note::SwapNote`). The script is pre-compiled MASM — use the builder API to create SWAP notes in client code.
+:::
+
+### How it works
+
+1. Creator creates a SWAP note containing the offered asset and metadata describing the requested asset + payback recipient
+2. Consumer's transaction processes the note — the script creates a P2ID payback note targeted at the original creator containing the requested asset
+3. Consumer receives the offered asset into their vault
+4. Both transfers happen atomically in one transaction
+
+### Builder API
+
+```rust
+use miden_standards::note::SwapNote;
+
+SwapNote::create(
+ sender,
+ offered_asset,
+ requested_asset,
+ swap_note_type,
+ swap_note_attachment,
+ payback_note_type,
+ payback_note_attachment,
+ rng,
+) -> Result<(Note, NoteDetails), NoteError>
+```
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `sender` | `AccountId` | Account that receives the payback P2ID note |
+| `offered_asset` | `Asset` | Asset the note carries (what the consumer receives) |
+| `requested_asset` | `Asset` | Asset the consumer must provide in return |
+| `swap_note_type` | `NoteType` | `Public` or `Private` for the SWAP note |
+| `swap_note_attachment` | `NoteAttachment` | Auxiliary data for the SWAP note |
+| `payback_note_type` | `NoteType` | `Public` or `Private` for the P2ID payback note |
+| `payback_note_attachment` | `NoteAttachment` | Auxiliary data for the payback note |
+| `rng` | `&mut impl FeltRng` | Random number generator |
+
+Returns a tuple of `(Note, NoteDetails)` — the SWAP note to submit and the expected payback note details (for tracking).
+
+`NoteAttachment` is defined in the `miden-standards` crate (not the core `miden` SDK). It wraps the attachment data that gets set on output notes — see [note attachments](./output-notes#note-attachments) for the underlying SDK API.
+
+## More note types
+
+For writing custom note scripts, see [Note Scripts](./note-scripts). For the transaction context and `#[tx_script]`, see [Transaction Context](../transactions/transaction-context).
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/notes/output-notes.md b/versioned_docs/version-0.14/builder/smart-contracts/notes/output-notes.md
new file mode 100644
index 00000000..e3a1bc77
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/notes/output-notes.md
@@ -0,0 +1,111 @@
+---
+title: "Output Notes"
+sidebar_position: 4
+description: "Create output notes, attach assets, set attachments, and compute recipients."
+---
+
+# Output Notes
+
+The `output_note` module creates notes from inside account component code and transaction scripts. Use it to send assets to other accounts by creating notes that carry assets and a recipient hash.
+
+```rust
+use miden::{output_note, Asset, NoteIdx, Tag, NoteType, Recipient};
+```
+
+## Create a note
+
+```rust
+let note_idx: NoteIdx = output_note::create(tag, note_type, recipient);
+```
+
+
+To construct a tag targeting a specific account, use `NoteTag::with_account_target(account_id)` from `miden_protocol::note`.
+
+Returns a `NoteIdx` used to reference this note in subsequent operations within the same transaction.
+
+## Add assets to a note
+
+```rust
+output_note::add_asset(asset, note_idx);
+```
+
+Call `add_asset` multiple times with the same `note_idx` to attach several assets to one note. A note can carry both fungible and non-fungible assets.
+
+## Query output note state
+
+```rust
+// Asset commitment and count
+let info: OutputNoteAssetsInfo = output_note::get_assets_info(note_idx);
+
+// All assets on the note
+let assets: Vec = output_note::get_assets(note_idx);
+
+// The recipient hash
+let recipient: Recipient = output_note::get_recipient(note_idx);
+```
+
+`OutputNoteAssetsInfo` contains `commitment: Word` and `num_assets: Felt`.
+
+### Note metadata
+
+Returns a `NoteMetadata` struct (not a raw `Word`):
+
+```rust
+let metadata: NoteMetadata = output_note::get_metadata(note_idx);
+```
+
+See [Reading Notes — Note metadata](./reading-notes#note-metadata) for the `NoteMetadata` struct definition.
+
+## Note attachments
+
+Notes can carry auxiliary data as attachments. The attachment API uses `Felt`-typed discriminants to select the scheme and kind:
+
+```rust
+// Full form — specify scheme, kind, and payload
+output_note::set_attachment(note_idx, attachment_scheme, attachment_kind, attachment_word);
+
+// Word attachment — a single Word of inline data
+output_note::set_word_attachment(note_idx, attachment_scheme, word_data);
+
+// Array attachment — a commitment to data stored in the advice map
+output_note::set_array_attachment(note_idx, attachment_scheme, commitment_word);
+```
+
+
+**Word attachments** store data directly in the note. **Array attachments** store a commitment (hash) to larger data that lives in the advice map — the consumer must have access to the corresponding advice map entries to read the full data.
+
+## Computing a Recipient
+
+When creating notes programmatically, you need a `Recipient` to pass to `output_note::create`. The `Recipient` is a hash that encodes the note script and inputs, ensuring only someone who knows these values can consume the note.
+
+```rust
+use miden::{Recipient, Digest};
+
+let recipient = Recipient::compute(
+ serial_num, // Word: unique serial number for this note
+ script_digest, // Digest: hash of the note script
+ inputs, // Vec: note inputs (e.g., target account ID)
+);
+```
+
+The computation is: `hash(hash(hash(serial_num, [0;4]), script_root), inputs_commitment)`. `script_digest` and `script_root` refer to the same value — the hash of the note script program. The formula uses `script_root`; the Rust parameter is named `script_digest`.
+
+## Example: creating and funding a note
+
+A complete flow for creating a note inside an account component:
+
+```rust
+use miden::{output_note, Asset, Tag, NoteType, Recipient, Digest};
+
+pub fn send_assets(recipient: Recipient, asset: Asset, tag: Tag) {
+ // 1. Create the note
+ let note_idx = output_note::create(tag, NoteType::Public, recipient);
+
+ // 2. Attach assets
+ output_note::add_asset(asset, note_idx);
+}
+```
+
+:::info API Reference
+Full API docs on docs.rs: [`miden::output_note`](https://docs.rs/miden/latest/miden/output_note/)
+:::
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/notes/reading-notes.md b/versioned_docs/version-0.14/builder/smart-contracts/notes/reading-notes.md
new file mode 100644
index 00000000..42c9f3ab
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/notes/reading-notes.md
@@ -0,0 +1,163 @@
+---
+title: "Reading Notes"
+sidebar_position: 3
+description: "Read the active note's data and access input notes by index during transactions."
+---
+
+# Reading Notes
+
+Miden provides two modules for reading note data, each for a different execution context:
+
+- **`active_note`** — used inside **note scripts**. Reads data from the note currently being executed (the note whose `#[note_script]` is running).
+- **`input_note`** — used inside **transaction scripts** and **account code**. Reads data from any input note by index, useful when a transaction consumes multiple notes and needs to inspect them.
+
+## `active_note` — the executing note
+
+When a note script runs, `active_note` provides access to the current note's storage, assets, and metadata:
+
+```rust
+use miden::active_note;
+```
+
+### Storage
+
+Note storage is a sequence of `Felt` values set by the note creator (e.g., a target account ID, an expiration block height). The recommended way to access it is through the `#[note]` struct — fields are automatically deserialized from the note's storage:
+
+```rust
+#[note]
+struct MyNote {
+ target_account_id: AccountId, // Deserialized from note storage automatically
+}
+```
+
+See [Note Scripts](./note-scripts) for the full `#[note]` pattern. The low-level `active_note::get_storage()` function is also available for advanced use cases:
+
+```rust
+let storage: Vec = active_note::get_storage();
+```
+
+### Assets
+
+```rust
+let assets: Vec = active_note::get_assets();
+```
+
+### Identity and metadata
+
+```rust
+let sender: AccountId = active_note::get_sender();
+let recipient: Recipient = active_note::get_recipient();
+let script_root: Word = active_note::get_script_root();
+let serial_num: Word = active_note::get_serial_number();
+```
+
+### Note metadata
+
+`get_metadata()` returns a `NoteMetadata` struct containing the note's attachment and header:
+
+```rust
+let metadata: NoteMetadata = active_note::get_metadata();
+```
+
+`NoteMetadata` has two fields:
+
+```rust
+pub struct NoteMetadata {
+ pub attachment: Word, // auxiliary data attached to the note
+ pub header: Word, // metadata header (sender, tag, etc.)
+}
+```
+
+## `input_note` — querying notes by index
+
+Inside transaction scripts or account code, use `input_note` to read data from any input note being consumed in the current transaction. Each function takes a `NoteIdx` identifying which note to query:
+
+```rust
+use miden::input_note;
+```
+
+### Assets
+
+```rust
+let info: InputNoteAssetsInfo = input_note::get_assets_info(note_idx);
+let assets: Vec = input_note::get_assets(note_idx);
+```
+
+`InputNoteAssetsInfo` contains `commitment: Word` and `num_assets: Felt`.
+
+### Identity and metadata
+
+```rust
+let sender: AccountId = input_note::get_sender(note_idx);
+let recipient: Recipient = input_note::get_recipient(note_idx);
+let script_root: Word = input_note::get_script_root(note_idx);
+let serial_num: Word = input_note::get_serial_number(note_idx);
+```
+
+### Storage
+
+```rust
+let storage_info: InputNoteStorageInfo = input_note::get_storage_info(note_idx);
+```
+
+:::note
+Unlike `active_note::get_storage()` which returns the full `Vec` of storage values, `input_note` only exposes the storage commitment and count — not the actual values. The transaction kernel only has commitments for input notes that are not currently executing. To read actual storage values, use `active_note::get_storage()` inside the note script itself.
+:::
+
+`InputNoteStorageInfo` contains `commitment: Word` and `num_storage_items: Felt`.
+
+### Note metadata
+
+Returns the same `NoteMetadata` struct as `active_note`:
+
+```rust
+let metadata: NoteMetadata = input_note::get_metadata(note_idx);
+```
+
+## Examples
+
+### Reading storage in a note script
+
+A note script that reads the target account ID from storage and verifies the consumer:
+
+```rust
+use miden::{AccountId, Word, active_note, note};
+
+#[note]
+struct P2idNote {
+ target_account_id: AccountId,
+}
+
+#[note]
+impl P2idNote {
+ #[note_script]
+ pub fn run(self, _arg: Word, account: &mut Account) {
+ assert_eq!(account.get_id(), self.target_account_id);
+
+ let assets = active_note::get_assets();
+ for asset in assets {
+ account.receive_asset(asset);
+ }
+ }
+}
+```
+
+### Reading input notes in a transaction script
+
+A transaction script that reads data from a consumed input note:
+
+```rust
+use miden::*;
+
+#[tx_script]
+pub fn run(arg: Word) {
+ // Query the first input note (index 0)
+ let idx = NoteIdx { inner: felt!(0) };
+ let assets = input_note::get_assets(idx);
+ let sender = input_note::get_sender(idx);
+}
+```
+
+:::info API Reference
+Full API docs on docs.rs: [`miden::active_note`](https://docs.rs/miden/latest/miden/active_note/), [`miden::input_note`](https://docs.rs/miden/latest/miden/input_note/)
+:::
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/overview.md b/versioned_docs/version-0.14/builder/smart-contracts/overview.md
new file mode 100644
index 00000000..69b7b825
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/overview.md
@@ -0,0 +1,151 @@
+---
+title: "What is a Miden Smart Contract"
+sidebar_position: 1
+description: "Miden's execution model, account structure, note system, and transaction lifecycle — how Rust code becomes zero-knowledge proofs."
+---
+
+# 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/miden-bank/).
+
+## What makes Miden different
+
+On Ethereum, smart contracts execute on every node. On Miden, **transactions execute locally on the client** — and only a cryptographic proof is submitted to the network.
+
+This means:
+
+- **Privacy by default** — The network sees the proof, not the inputs
+- **Parallel execution** — Transactions don't compete for block space
+- **Lower fees** — No gas wars; proofs are cheap to verify
+- **Client-side proving** — Your machine generates the ZK proof
+
+## The compilation pipeline
+
+Your Rust code goes through several transformations before it runs:
+
+```
+Rust → Wasm → Miden Assembly (MASM) → ZK Circuit → Proof
+```
+
+1. **Rust → Wasm**: The Miden compiler (`cargo-miden`) compiles your `#![no_std]` Rust to WebAssembly
+2. **Wasm → MASM**: The compiler translates Wasm to Miden Assembly, the VM's native instruction set
+3. **MASM → Proof**: When a transaction executes, the Miden VM runs the MASM code and produces a zero-knowledge proof
+
+The output of `miden build` is a `.masp` file (Miden Assembly Package) containing the compiled MASM and metadata.
+
+## The account model
+
+Every entity on Miden is an **account**. Accounts are smart contracts — even user wallets.
+
+An account has four parts:
+
+| Part | Description |
+|------|-------------|
+| **Code** | One or more components that define the account's behavior |
+| **Storage** | Persistent state — up to 255 slots of `Value` or `StorageMap` |
+| **Vault** | The assets (fungible and non-fungible) the account holds |
+| **Nonce** | A counter that increments with every state change (replay protection) |
+
+### Components
+
+Components are reusable code modules attached to accounts. Think of them as **traits or mixins**, not monolithic contracts. An account can have multiple components.
+
+```rust
+#[component]
+struct MyWallet;
+
+#[component]
+impl MyWallet {
+ pub fn receive_asset(&mut self, asset: Asset) {
+ self.add_asset(asset);
+ }
+}
+```
+
+Each component defines its own storage layout and public methods. The `#[component]` macro generates the necessary WIT (WebAssembly Interface Type) definitions for cross-component interoperability.
+
+See [Components](./accounts/components) for full details.
+
+## Notes as UTXOs
+
+Assets don't transfer directly between accounts. Instead, they move through **notes** — programmable messages that carry assets and logic.
+
+```
+Sender Account → creates Note (with assets + script) → Recipient Account consumes Note
+```
+
+Notes are similar to Bitcoin's UTXOs, but with arbitrary programmable logic. A note contains:
+
+- **Assets**: The tokens or NFTs being transferred
+- **Script**: Code that executes when the note is consumed (e.g., "only account X can claim this")
+- **Inputs**: Custom data the script can read
+
+The most common pattern is **P2ID** (Pay to ID) — a note that can only be consumed by a specific account.
+
+See [Notes](./notes/) for implementation details.
+
+## Transaction flow
+
+A transaction in Miden follows this flow:
+
+```mermaid
+flowchart LR
+ A[Client builds tx locally] --> B[VM executes MASM]
+ B --> C[Proof generated]
+ C --> D[Proof submitted to network]
+ D --> E[Network verifies proof]
+ E --> F[State updated]
+```
+
+1. **Build**: The client assembles the transaction — which notes to consume, which account methods to call
+2. **Execute**: The Miden VM executes the transaction locally
+3. **Prove**: The VM produces a zero-knowledge proof of correct execution
+4. **Submit**: Only the proof (and state commitments) go to the network
+5. **Verify**: The network verifies the proof and updates state
+
+:::info Privacy
+Because execution happens locally, the network never sees your transaction inputs, the account's private state, or the logic that ran. It only sees that the proof is valid and the resulting state commitments.
+:::
+
+## What "proof generation fails" means
+
+When you write assertions in Miden contracts:
+
+```rust
+assert!(amount > felt!(0));
+```
+
+If the assertion fails, the ZK circuit **cannot produce a valid proof**. This means:
+
+- The transaction is rejected **before it ever reaches the network**
+- No state changes occur — it's as if the transaction never happened
+- The client gets an error explaining which assertion failed
+
+This is fundamentally different from Ethereum's `revert` — there's no on-chain transaction that fails. The proof simply doesn't exist if the execution is invalid.
+
+## Account types
+
+Miden supports several account types, configured in `Cargo.toml`:
+
+| Type | Description |
+|------|-------------|
+| `RegularAccountUpdatableCode` | Standard account — code can be updated after deployment |
+| `RegularAccountImmutableCode` | Account with fixed code — cannot be changed |
+| `FungibleFaucet` | Token minting account (fungible assets) |
+| `NonFungibleFaucet` | NFT minting account (non-fungible assets) |
+
+## Building blocks
+
+| Building Block | Description | Details |
+|----------------|-------------|---------|
+| [Components](./accounts/components) | Reusable code modules with storage and WIT interfaces | `#[component]` macro |
+| [Storage](./accounts/storage) | Up to 255 slots of Value or StorageMap | Persistent state |
+| [Custom Types](./accounts/custom-types) | Exported structs/enums for public APIs | `#[export_type]` |
+| [Account Operations](./accounts/account-operations) | Read/write account state and vault | `active_account`, `native_account` |
+| [Notes](./notes/) | Programmable UTXOs for asset transfers | Note scripts |
+| [The tx Module](./transactions/transaction-context) | Block queries and expiration management | `tx` module, `#[tx_script]` |
+| [Authentication](./accounts/authentication) | Falcon512 signatures and replay protection | Nonce management |
+| [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/miden-bank/) for a hands-on walkthrough.
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/patterns.md b/versioned_docs/version-0.14/builder/smart-contracts/patterns.md
new file mode 100644
index 00000000..114ec53a
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/patterns.md
@@ -0,0 +1,76 @@
+---
+title: "Patterns"
+sidebar_position: 6
+description: "Common patterns and security considerations for Miden smart contracts."
+---
+
+# 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/miden-bank/).
+
+## Access control
+
+:::warning[No msg.sender in account components]
+Unlike Solidity, account component procedures cannot check "who is calling me." In Miden:
+- **Note scripts** can check who created the note via `active_note::get_sender()`
+- **Account components** rely on authentication components (Falcon512, ECDSA) which the transaction kernel invokes automatically in the epilogue
+:::
+
+For account-level access control, Miden uses **authentication components** rather than manual sender checks. The transaction kernel calls the account's `auth` procedure automatically during the transaction epilogue — if the signature is invalid, the entire transaction fails. See [Authentication](./accounts/authentication) for the full pattern.
+
+For note-level access control, note scripts can check who created the note using `active_note::get_sender()`. The protocol-level `ownable` standard (`miden-standards/asm/standards/access/ownable.masm`) provides `verify_owner`, `get_owner`, `transfer_ownership`, and `renounce_ownership` procedures.
+
+## Rate limiting {#rate-limiting}
+
+Use `tx::get_block_number()` to enforce cooldown periods between actions. Store the last action block number in a `Value` storage slot, then compare against the current block number before allowing the next action.
+
+See [Transaction Context](./transactions/transaction-context) for the available block and transaction info functions.
+
+## Security
+
+### Assertions and error handling
+
+Miden doesn't support error strings or `Result` types in contract execution. Use assertions:
+
+```rust
+assert!(amount > 0);
+assert_eq!(a, b);
+```
+
+When an assertion fails, proof generation fails and the transaction is rejected before reaching the network.
+
+### Replay protection
+
+Every state-changing transaction must increment the nonce. The auth component handles this automatically — see [Authentication](./accounts/authentication).
+
+### Safe arithmetic
+
+Use `saturating_sub` to prevent underflow:
+
+```rust
+// Good — won't underflow
+let elapsed = current_block.saturating_sub(last_block);
+
+// Dangerous — could underflow
+let elapsed = current_block - last_block;
+```
+
+For Felt arithmetic, values wrap modulo the prime field (no overflow panic), but the result may not be what you expect if you're treating Felts as integers. See [Types — Felt](./types#felt--field-elements) for details.
+
+### Anti-patterns
+
+- **Don't store secrets in contract code** — contract code is visible on-chain
+- **Don't skip nonce management** — prevents replay attacks
+- **Be careful with Felt division** — Felt division computes the multiplicative inverse, not integer division. Convert to `u64` first for integer-style operations
+
+## `#![no_std]` environment
+
+All Miden contracts run without the standard library:
+
+| Not available | Alternative |
+|---------------|-------------|
+| `std::collections::HashMap` | Use `BTreeMap` from `alloc`, or `StorageMap` for persistent account storage |
+| `std::string::String` | Use `alloc::string::String` |
+| `std::vec::Vec` | Use `alloc::vec::Vec` |
+| `println!()` / `eprintln!()` | No direct equivalent — run the transaction under the Mockchain and inspect outputs, or use the external debugger |
+| Error strings in `assert!()` | Use `assert!(condition)` without messages |
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/transactions/_category_.json b/versioned_docs/version-0.14/builder/smart-contracts/transactions/_category_.json
new file mode 100644
index 00000000..5f2ae57d
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/transactions/_category_.json
@@ -0,0 +1,8 @@
+{
+ "label": "Transactions",
+ "position": 5,
+ "link": {
+ "type": "generated-index",
+ "slug": "/builder/smart-contracts/transactions"
+ }
+}
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/transactions/advice-provider.md b/versioned_docs/version-0.14/builder/smart-contracts/transactions/advice-provider.md
new file mode 100644
index 00000000..f5912da8
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/transactions/advice-provider.md
@@ -0,0 +1,140 @@
+---
+title: "Advice Provider"
+sidebar_position: 5
+description: "Reading and writing the advice map, loading preimages, and requesting Falcon signatures in Miden transactions."
+---
+
+# Advice Provider
+
+The advice provider is a mechanism for supplying non-deterministic auxiliary data to the VM during proof generation. It backs an **advice map** (a key-value store of `Word → Vec`) and an **advice stack** that host-provided data can be pushed onto. Common uses include passing structured data into transaction scripts, providing Falcon signatures for authentication, and seeding note scripts with external inputs.
+
+## Trust model and data integrity
+
+The advice provider is supplied by the **host** (the Miden client or node) — not by on-chain consensus. This means the VM cannot blindly trust that the host provided correct data. Two patterns address this:
+
+### Unverified stack push (caller must verify)
+
+`adv_push_mapvaln` pushes data onto the advice stack **without verification**. The caller is responsible for checking integrity if the data is security-sensitive:
+
+```rust
+let num_felts = adv_push_mapvaln(key);
+// Data is now on the stack — but not verified
+// If integrity matters, hash the result and compare to a known commitment
+```
+
+For signatures, `emit_falcon_sig_to_stack` pushes a Falcon512 signature that is subsequently verified by `rpo_falcon512_verify` — the verification step is what makes it safe.
+
+### Commitment-verified loading (safe)
+
+`adv_load_preimage` is integrity-safe by construction. The VM verifies that the loaded data hashes to the provided `commitment` before returning it. If the host tampers with the data, the hash won't match and proof generation fails:
+
+```rust
+// The VM will abort if hash(loaded_data) != commitment
+let data = adv_load_preimage(num_words, commitment);
+// `data` is guaranteed to match `commitment`
+```
+
+Use this pattern when the commitment is known ahead of time (e.g., stored in a note input or passed as a script argument).
+
+:::warning
+Never use `adv_push_mapvaln` for security-sensitive data without a subsequent integrity check. The host can supply any value it wants. Use `adv_load_preimage` (commitment-verified) or verify the loaded data yourself using `hash_words`.
+:::
+
+## Reading from the advice map
+
+### `adv_push_mapvaln`
+
+Pushes the value associated with a key onto the advice stack and returns its length.
+
+```rust
+use miden::intrinsics::advice::adv_push_mapvaln;
+
+// Push the value for `key` onto the advice stack; returns the number of Felts pushed.
+let num_felts: Felt = adv_push_mapvaln(key);
+```
+
+
+### `adv_load_preimage`
+
+Loads a preimage from the advice provider given a commitment and expected word count. This is useful when a note or transaction script needs to retrieve data that was hashed and stored by the sender.
+
+```rust
+use miden::adv_load_preimage;
+
+// Load `num_words` Words whose hash matches `commitment`.
+let felts: Vec = adv_load_preimage(num_words, commitment);
+```
+
+
+### Pattern: passing structured data to a transaction script
+
+The canonical pattern (used in `basic-wallet-tx-script`) combines `adv_push_mapvaln` with `adv_load_preimage` to retrieve structured data encoded as a preimage:
+
+```rust
+use miden::{intrinsics::advice::adv_push_mapvaln, *};
+
+// 1. Look up the key — returns the number of Felts stored there
+let num_felts = adv_push_mapvaln(key);
+
+// 2. Convert to a Felt count of words and load the preimage (length must be word-aligned)
+let num_felts_u64 = num_felts.as_canonical_u64();
+assert_eq(Felt::from_u32((num_felts_u64 % 4) as u32), felt!(0));
+let num_words = Felt::new(num_felts_u64 / 4);
+let data: Vec = adv_load_preimage(num_words, key);
+
+// 3. Index into the data by field position
+let tag = data[0];
+let note_type = data[1];
+// ...
+```
+
+See [Transaction Scripts](./transaction-scripts) for the full `basic-wallet-tx-script` example.
+
+## Writing to the advice map
+
+### `adv_insert`
+
+Inserts a slice of `Word`s into the advice map under the given key.
+
+```rust
+use miden::intrinsics::advice::adv_insert;
+
+let values: &[Word] = &[word_a, word_b];
+adv_insert(key, values);
+```
+
+
+### `adv_insert_mem`
+
+Inserts a range of memory into the advice map. The VM reads `Word`s from addresses `[start_addr, end_addr)` and stores them under the key. Both address arguments are `u32`.
+
+```rust
+use miden::intrinsics::advice::adv_insert_mem;
+
+let start_addr: u32 = 0;
+let end_addr: u32 = 8;
+adv_insert_mem(key, start_addr, end_addr);
+```
+
+
+## Requesting a Falcon signature
+
+`emit_falcon_sig_to_stack` emits an `AUTH_REQUEST_EVENT` that instructs the host to push a Falcon512 signature onto the advice stack. This is typically used in [authentication components](../accounts/authentication) before calling `rpo_falcon512_verify`.
+
+```rust
+use miden::intrinsics::advice::emit_falcon_sig_to_stack;
+
+// Request the host to push a Falcon signature onto the advice stack
+emit_falcon_sig_to_stack(msg, pub_key);
+```
+
+
+:::info API Reference
+Full API docs on docs.rs: [`miden::intrinsics::advice`](https://docs.rs/miden/latest/miden/intrinsics/advice/)
+:::
+
+## Related
+
+- [Authentication](../accounts/authentication) — Falcon512 signature verification (over Poseidon2) and nonce management
+- [Transaction Scripts](./transaction-scripts) — executing logic in the transaction context
+- [Transaction Context](./transaction-context) — overview of transaction execution
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/transactions/introduction.md b/versioned_docs/version-0.14/builder/smart-contracts/transactions/introduction.md
new file mode 100644
index 00000000..9a5e79e7
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/transactions/introduction.md
@@ -0,0 +1,82 @@
+---
+title: "What are Transactions?"
+sidebar_position: 0
+description: "Transactions are Miden's execution unit — they consume input notes and produce output notes, executed locally and proven before submission."
+---
+
+# What are Transactions?
+
+Transactions are the execution unit in Miden. Every state change — transferring assets, updating storage, minting tokens — happens inside a transaction. Each transaction runs against a single account, consumes zero or more input notes, and produces zero or more output notes.
+
+The critical difference from other blockchains: transactions execute locally on the user's machine, not on a shared VM. After execution, the Miden VM generates a zero-knowledge proof that the transaction was valid (see [transaction design](/core-concepts/protocol/transaction)). Only this proof and the resulting state commitments are submitted to the network. The network never sees the transaction inputs, the account's private state, or the logic that ran.
+
+## Anatomy of a transaction
+
+Every transaction has these elements:
+
+| Element | Description |
+|---------|-------------|
+| **Account** | The single account this transaction mutates — its storage, vault, and nonce |
+| **Input notes** | Zero or more notes being consumed — their scripts run and assets transfer to the account |
+| **Output notes** | Zero or more notes being created — carrying assets and scripts for future consumption |
+| **Transaction script** | Optional entry-point logic that runs in addition to note scripts and component code |
+| **Block reference** | The chain state the transaction executes against — provides block number, timestamp, and commitments |
+
+A transaction can only modify one account. Cross-account interactions happen through notes: one account's transaction creates a note, another account's transaction consumes it.
+
+## Transaction lifecycle
+
+```
+┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
+│ Build │────▶│ Execute │────▶│ Prove │────▶│ Submit │────▶│ Verify │
+│ │ │ │ │ │ │ │ │ │
+│ Assemble │ │ VM runs │ │ ZK proof │ │ Proof + │ │ Network │
+│ tx inputs│ │ locally │ │ generated│ │ state │ │ updates │
+│ │ │ │ │ │ │ sent │ │ state │
+└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
+```
+
+1. **Build**: The client assembles the transaction — which account, which notes to consume, what methods to call.
+2. **Execute**: The Miden VM runs the transaction locally. Note scripts execute, component code runs, storage is mutated, output notes are created.
+3. **Prove**: The VM produces a zero-knowledge proof of correct execution. If any assertion fails (e.g., insufficient balance, unauthorized caller), the proof cannot be generated — the transaction is rejected before it ever reaches the network.
+4. **Submit**: The proof and public state updates (new note commitments, updated account commitment, nullifiers for consumed notes) are submitted to the network.
+5. **Verify**: The network verifies the proof, records the state changes, and includes the transaction in a batch and eventually a block.
+
+## The transaction context
+
+During execution, your code runs inside a **transaction context** that provides access to:
+
+- **Block data** — current block number, timestamp, and commitments via the `tx` module
+- **Input notes** — the notes being consumed, their assets, inputs, and metadata
+- **Account state** — the executing account's storage, vault, and nonce
+- **Output notes** — the ability to create new notes and attach assets
+
+The transaction context is what connects your component code to the chain state. For example, you can implement time-based logic by comparing `tx::get_block_number()` against a stored value, or read note inputs to determine what action to take.
+
+## What happens when execution fails
+
+When an assertion fails in your code:
+
+```rust
+assert!(amount > felt!(0));
+```
+
+The ZK circuit **cannot produce a valid proof**. This means:
+
+- The transaction is rejected **before it ever reaches the network**
+- No state changes occur — it's as if the transaction never happened
+- No gas or fees are consumed
+- The client gets an error explaining which assertion failed
+
+This is fundamentally different from Ethereum's `revert`, where the failed transaction still lands on-chain, consumes gas, and is visible to everyone.
+
+## How transactions differ from EVM transactions
+
+| | EVM | Miden |
+|---|---|---|
+| **Execution** | Every validator re-executes the transaction | Client executes locally, submits only the proof |
+| **Scope** | Can call multiple contracts in one tx | One transaction mutates one account; cross-account via notes |
+| **Privacy** | All inputs, state reads, and call traces are public | Network sees only the proof and state commitments |
+| **Failure** | On-chain revert, gas consumed, visible trace | Proof can't be generated — no on-chain trace, no cost |
+| **Parallelism** | Transactions touching same state must serialize | Single-account scope enables parallel execution |
+| **Authentication** | `msg.sender` set by protocol | Falcon512 signatures verified inside the transaction |
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/transactions/transaction-context.md b/versioned_docs/version-0.14/builder/smart-contracts/transactions/transaction-context.md
new file mode 100644
index 00000000..9414f6b2
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/transactions/transaction-context.md
@@ -0,0 +1,66 @@
+---
+title: "The tx Module"
+sidebar_position: 2
+description: "Block queries, note commitments, and expiration management with the tx module."
+---
+
+# The tx Module
+
+A Miden transaction is a local operation that consumes zero or more input notes and produces state changes plus output notes for a single account. The transaction executes entirely on the client — the VM runs the code, generates a ZK proof, and only the proof is submitted to the network. The `tx` module provides access to block information, note commitments, and expiration controls. Transaction scripts (`#[tx_script]`) serve as standalone entry points that orchestrate the transaction.
+
+## The `tx` module
+
+```rust
+use miden::tx;
+```
+
+### Block information
+
+```rust
+// Current block number
+let block_num: Felt = tx::get_block_number();
+
+// Block commitment (hash of block header)
+let commitment: Word = tx::get_block_commitment();
+
+// Block timestamp (seconds since epoch)
+let timestamp: Felt = tx::get_block_timestamp();
+```
+
+### Note commitments
+
+```rust
+// Commitment over all input notes in this transaction
+let input_commit: Word = tx::get_input_notes_commitment();
+
+// Commitment over all output notes in this transaction
+let output_commit: Word = tx::get_output_notes_commitment();
+
+// Number of input/output notes
+let num_inputs: Felt = tx::get_num_input_notes();
+let num_outputs: Felt = tx::get_num_output_notes();
+```
+
+### Transaction expiration
+
+Control how long a transaction remains valid:
+
+```rust
+// Get current expiration delta (in blocks)
+let delta: Felt = tx::get_expiration_block_delta();
+
+// Set a new expiration delta
+tx::update_expiration_block_delta(felt!(100));
+```
+
+The expiration delta determines how many blocks after creation the transaction remains valid. If the transaction isn't included within this window, it expires.
+
+## Transaction scripts
+
+Transaction scripts use the `#[tx_script]` macro to define a top-level entry point for the transaction. See [Transaction Scripts](./transaction-scripts) for the full `#[tx_script]` API and examples.
+
+For signature verification using the transaction context, see [Authentication](../accounts/authentication). For time-based patterns using `tx::get_block_number()`, see [Patterns — Rate limiting](../patterns#rate-limiting).
+
+:::info API Reference
+Full API docs on docs.rs: [`miden::tx`](https://docs.rs/miden/latest/miden/tx/)
+:::
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/transactions/transaction-scripts.md b/versioned_docs/version-0.14/builder/smart-contracts/transactions/transaction-scripts.md
new file mode 100644
index 00000000..f8bda2e1
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/transactions/transaction-scripts.md
@@ -0,0 +1,104 @@
+---
+title: "Transaction Scripts"
+sidebar_position: 3
+description: "Write transaction scripts with #[tx_script] to orchestrate multi-note transactions and build output notes."
+---
+
+# Transaction Scripts
+
+A transaction script is a top-level function that runs once per transaction, after all note scripts have executed. Use it to orchestrate logic that spans multiple consumed notes — moving assets from the account vault into output notes, calling account methods via [cross-component calls](../cross-component-calls), or running anything that must happen after all note scripts finish.
+
+## `#[tx_script]` signature
+
+```rust
+// With account access
+#[tx_script]
+fn run(arg: Word, account: &mut Account) { ... }
+
+// Without account access
+#[tx_script]
+fn run(arg: Word) { ... }
+```
+
+| Constraint | Details |
+|------------|---------|
+| Function name | Must be `run` (enforced by the macro) |
+| Return type | `()` |
+| Required arg | One `Word` (the script argument, passed by the transaction executor) |
+| Optional arg | `&Account` or `&mut Account` |
+| Generics | Not allowed |
+| Async | Not allowed |
+
+## Cargo.toml
+
+```toml
+[package.metadata.miden]
+project-kind = "transaction-script"
+```
+
+## Example: basic-wallet-tx-script
+
+This example reads note parameters from the advice map and creates an output note:
+
+```rust
+// Do not link against libstd (i.e. anything defined in `std::`)
+#![no_std]
+#![feature(alloc_error_handler)]
+
+// However, we could still use some standard library types while
+// remaining no-std compatible, if we uncommented the following lines:
+//
+//
+// extern crate alloc;
+// use alloc::vec::Vec;
+
+use miden::{intrinsics::advice::{adv_load_preimage, adv_push_mapvaln}, *};
+
+use crate::bindings::Account;
+
+// Input layout constants
+const TAG_INDEX: usize = 0;
+const NOTE_TYPE_INDEX: usize = 1;
+const RECIPIENT_START: usize = 2;
+const RECIPIENT_END: usize = 6;
+const ASSET_START: usize = 6;
+const ASSET_END: usize = 10;
+
+#[tx_script]
+fn run(arg: Word, account: &mut Account) {
+ let num_felts = adv_push_mapvaln(arg.clone());
+ let num_felts_u64 = num_felts.as_u64();
+ assert_eq!(Felt::from_u32((num_felts_u64 % 4) as u32), felt!(0));
+ let num_words = Felt::from_u64_unchecked(num_felts_u64 / 4);
+ let commitment = arg;
+ let input = adv_load_preimage(num_words, commitment);
+ let tag = input[TAG_INDEX];
+ let note_type = input[NOTE_TYPE_INDEX];
+ let recipient: [Felt; 4] = input[RECIPIENT_START..RECIPIENT_END].try_into().unwrap();
+ let note_idx = output_note::create(tag.into(), note_type.into(), recipient.into());
+ let asset: [Felt; 4] = input[ASSET_START..ASSET_END].try_into().unwrap();
+ account.move_asset_to_note(asset.into(), note_idx);
+}
+```
+
+### Walkthrough
+
+1. **`arg: Word`** is a map key used to look up the transaction data in the advice map
+2. **`adv_push_mapvaln(arg)`** reads the number of felts stored at that key
+3. **`adv_load_preimage(num_words, commitment)`** retrieves the actual data (tag, note_type, recipient, asset) from the advice map
+4. **`output_note::create(tag, note_type, recipient)`** creates the output note
+5. **`account.move_asset_to_note(asset, note_idx)`** moves the asset from the account vault into the newly created note
+
+:::note
+This script uses the advice map to pass structured input data. The caller encodes the note parameters (tag, note_type, recipient, asset) as a preimage and passes the commitment hash as the `arg` Word.
+:::
+
+:::tip
+`adv_push_mapvaln` and `adv_load_preimage` are part of the advice provider — the mechanism for supplying auxiliary data to a transaction. See [Advice Provider](./advice-provider) for the full function reference.
+:::
+
+## Related
+
+- [Transaction Context](./transaction-context) — `tx` module (block info, note commitments, expiration)
+- [Cross-Component Calls](../cross-component-calls) — how `&mut Account` works in tx scripts
+- [Reading Notes](../notes/reading-notes) — reading input notes by index inside tx scripts
diff --git a/versioned_docs/version-0.14/builder/smart-contracts/types.md b/versioned_docs/version-0.14/builder/smart-contracts/types.md
new file mode 100644
index 00000000..78ece75b
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/smart-contracts/types.md
@@ -0,0 +1,290 @@
+---
+title: "Types"
+sidebar_position: 5.6
+description: "Felt field arithmetic, Word layout, Asset encoding, and type conversions in the Miden Rust SDK."
+---
+
+# Types
+
+Miden's type system is built around field elements rather than standard integers. All computation inside the Miden VM is modular arithmetic over the Goldilocks prime field ($p = 2^{64} - 2^{32} + 1$), so overflow and division behave differently from standard integers. `Felt` is the native numeric type, `Word` is a tuple of four Felts used for [storage](./accounts/storage) and hashing, and `Asset` encodes fungible and non-fungible assets as Words.
+
+## Felt — Field elements
+
+`Felt` is the fundamental numeric type in Miden. It represents an element of the **Goldilocks prime field**:
+
+$$
+p = 2^{64} - 2^{32} + 1 = 18446744069414584321
+$$
+
+:::warning This is not integer arithmetic
+`Felt` uses **modular arithmetic**. Values wrap around the prime modulus, not at `u64::MAX`. Addition, subtraction, and multiplication all happen modulo $p$. Division computes the **multiplicative inverse**, not integer division.
+:::
+
+### Creating Felt values
+
+```rust
+use miden::{felt, Felt};
+
+// Compile-time literal (validated at compile time)
+let zero = felt!(0);
+let one = felt!(1);
+let answer = felt!(42);
+
+// From u32 (always safe)
+let f = Felt::from_u32(255);
+
+// From u64 (infallible — values are reduced into canonical form internally)
+let f = Felt::new(1_000_000_000);
+
+// Built-in zero / one constants
+let z = Felt::ZERO;
+let o = Felt::ONE;
+```
+
+:::info `felt!()` range limitation
+The `felt!()` macro currently only accepts values up to `u32::MAX` (4,294,967,295). For larger values, use `Felt::new()`. This limitation may be lifted in a future release.
+:::
+
+### Arithmetic
+
+```rust
+let a = felt!(10);
+let b = felt!(3);
+
+// Standard arithmetic (modular)
+let sum = a + b; // felt!(13)
+let diff = a - b; // felt!(7)
+let prod = a * b; // felt!(30)
+let neg = -a; // p - 10
+
+// Division computes multiplicative inverse
+// a / b = a * b^(-1) mod p
+let quot = a / b; // NOT integer 3 — it's 10 * inverse(3) mod p
+
+// In-place operators
+let mut x = felt!(5);
+x += felt!(1); // x is now felt!(6)
+x *= felt!(2); // x is now felt!(12)
+```
+
+:::note For business logic, prefer u64
+For computing amounts, balances, counters, or any value where overflow/underflow behavior matters, convert to `u64` first, perform the arithmetic, then convert back with `Felt::new()`.
+:::
+
+### Comparison and conversion
+
+```rust
+let f = felt!(42);
+
+// Convert to u64 (canonical representation)
+let n: u64 = f.as_u64();
+
+// Equality comparison
+if f == felt!(42) { /* ... */ }
+
+// For numeric comparisons, convert to u64 first
+if f.as_u64() > 100 { /* ... */ }
+
+// Check parity
+if f.is_odd() { /* ... */ }
+```
+
+:::tip Integer arithmetic on Felt values
+Converting `Felt` to `u64` with `.as_u64()` gives you standard Rust integer arithmetic — with overflow and underflow protection from Rust's debug-mode checks and `saturating_*` / `checked_*` methods. For business logic involving amounts, limits, or counters, prefer `u64` arithmetic:
+
+```rust
+// Convert, compute in u64, convert back
+let a: u64 = felt_a.as_u64();
+let b: u64 = felt_b.as_u64();
+let sum = a.saturating_add(b); // safe addition
+let diff = a.saturating_sub(b); // no underflow
+let result = Felt::new(sum);
+```
+:::
+
+### Advanced operations
+
+```rust
+let f = felt!(7);
+
+// Multiplicative inverse: f * f.inv() == felt!(1)
+let inv = f.inv(); // Panics if f == felt!(0)
+
+// Exponentiation: base^exponent mod p
+let result = f.exp(felt!(3)); // 7^3 mod p = 343
+
+// Power of 2: computes 2^self
+let power = felt!(10).pow2(); // 2^10 = 1024 (panics if self > 63)
+```
+
+## Word — Four field elements
+
+A `Word` holds four `Felt` values. It's the standard unit for storage, hashing, and data passing in Miden.
+
+```rust
+#[repr(C)]
+pub struct Word {
+ pub a: Felt,
+ pub b: Felt,
+ pub c: Felt,
+ pub d: Felt,
+}
+```
+
+### Creating Words
+
+```rust
+use miden::{felt, Felt, Word};
+
+// From an array of 4 Felts
+let w = Word::new([felt!(1), felt!(2), felt!(3), felt!(4)]);
+
+// Shorthand via `From<[Felt; 4]>`
+let w: Word = [felt!(1), felt!(2), felt!(3), felt!(4)].into();
+
+// Shorthand via `From<[u32; 4]>` (or [u8; 4] / [u16; 4] / [bool; 4])
+let w = Word::from([1u32, 2, 3, 4]);
+
+// All-zero word
+let z = Word::empty(); // same as Word::default()
+```
+
+### Indexing
+
+```rust
+let w = Word::new([felt!(10), felt!(20), felt!(30), felt!(40)]);
+
+// Named fields
+let a: Felt = w.a; // felt!(10)
+let d: Felt = w.d; // felt!(40)
+
+// Convert to array
+let arr: [Felt; 4] = w.into_elements();
+// or via the `From for [Felt; 4]` impl
+let arr2: [Felt; 4] = w.into();
+```
+
+### Packing data into Words
+
+Since each storage slot holds one `Word`, you'll often pack multiple values:
+
+```rust
+// Pack two u64 values into a Word
+let config = Word::new([
+ Felt::new(max_amount), // a: max amount
+ Felt::new(cooldown), // b: cooldown blocks
+ felt!(0), // c: unused
+ felt!(0), // d: unused
+]);
+
+// Unpack via named fields
+let max_amount = config.a.as_u64();
+let cooldown = config.b.as_u64();
+```
+
+## Asset
+
+`Asset` represents either a fungible or non-fungible asset. In v0.14 it is **two words** — a `key` (identifies the asset class) and a `value` (encodes the fungible amount or non-fungible data).
+
+```rust
+pub struct Asset {
+ pub key: Word,
+ pub value: Word,
+}
+```
+
+### Encoding
+
+**Fungible assets** (tokens):
+
+| Word | Field | Content |
+|----------|-------|---------|
+| `key` | `a` | `0` |
+| `key` | `b` | `0` |
+| `key` | `c` | Faucet ID suffix |
+| `key` | `d` | Faucet ID prefix |
+| `value` | `a` | Amount |
+| `value` | `b` | `0` |
+| `value` | `c` | `0` |
+| `value` | `d` | `0` |
+
+**Non-fungible assets** (NFTs):
+
+| Word | Field | Content |
+|----------|-------|---------|
+| `key` | `a` | Data hash element 0 |
+| `key` | `b` | Data hash element 1 |
+| `key` | `c` | Faucet ID suffix |
+| `key` | `d` | Faucet ID prefix |
+| `value` | `a..d`| Data payload (implementation-defined) |
+
+### Working with assets
+
+```rust
+use miden::{Asset, Word, felt};
+
+// Build a fungible asset from key + value words.
+// Fungible key = [0, 0, faucet_suffix, faucet_prefix],
+// fungible value = [amount, 0, 0, 0].
+let asset = Asset::new(
+ Word::from([felt!(0), felt!(0), faucet_suffix, faucet_prefix]),
+ Word::from([felt!(100), felt!(0), felt!(0), felt!(0)]),
+);
+
+// Read the amount (fungible): first limb of `value`.
+let amount: u64 = asset.value.a.as_u64();
+
+// Build a fungible asset from faucet ID + amount via the SDK helper.
+use miden::asset;
+let asset = asset::create_fungible_asset(faucet_id, felt!(1000));
+
+// Build a non-fungible asset.
+let nft = asset::create_non_fungible_asset(faucet_id, data_hash);
+```
+
+:::note Asset on the host side
+On the client / host side, `Asset` is an enum (`Asset::Fungible(_) | Asset::NonFungible(_)`) exposed from `miden-protocol`, with `to_key_word()` / `to_value_word()` / `from_key_value_words()` helpers. Inside a Rust contract the SDK exposes the two-word `Asset` struct shown above.
+:::
+
+## AccountId
+
+Identifies an account with two Felt values:
+
+```rust
+pub struct AccountId {
+ pub prefix: Felt,
+ pub suffix: Felt,
+}
+```
+
+```rust
+use miden::AccountId;
+
+let id = AccountId::new(prefix_felt, suffix_felt);
+
+// Use in comparisons
+let current: AccountId = self.get_id();
+assert_eq!(current.prefix, expected.prefix);
+assert_eq!(current.suffix, expected.suffix);
+```
+
+## Other types
+
+The SDK also provides `NoteIdx`, `Tag`, `NoteType`, `Recipient`, `Digest`, and `StorageSlotId`. See the [full API docs on docs.rs](https://docs.rs/miden/latest/miden/) for their definitions.
+## Type conversion table
+
+| From | To | Method |
+|------|----|--------|
+| `u32` | `Felt` | `Felt::from_u32(n)` |
+| `u64` | `Felt` | `Felt::new(n)` |
+| literal | `Felt` | `felt!(n)` |
+| `Felt` | `u64` | `f.as_u64()` |
+| `[Felt; 4]` | `Word` | `Word::new(arr)` or `Word::from(arr)` |
+| `[u32; 4]` / `[u16; 4]` / `[u8; 4]` / `[bool; 4]` | `Word` | `Word::from(arr)` |
+| `Word` | `[Felt; 4]` | `w.into_elements()` or `let arr: [Felt; 4] = w.into()` |
+
+Use these types in [component definitions](./accounts/components), store and retrieve Words from [persistent storage](./accounts/storage), or define your own types for public APIs with [`#[export_type]`](./accounts/custom-types).
+
+:::info API Reference
+Full API docs on docs.rs: [`Felt`](https://docs.rs/miden/latest/miden/struct.Felt.html), [`Word`](https://docs.rs/miden/latest/miden/struct.Word.html), [`Asset`](https://docs.rs/miden/latest/miden/struct.Asset.html)
+:::
diff --git a/versioned_docs/version-0.14/builder/tools/_category_.json b/versioned_docs/version-0.14/builder/tools/_category_.json
new file mode 100644
index 00000000..6c5100b8
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Tools",
+ "position": 6
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/_category_.yml b/versioned_docs/version-0.14/builder/tools/clients/_category_.yml
new file mode 100644
index 00000000..9c37f780
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/_category_.yml
@@ -0,0 +1,4 @@
+label: Client
+# Determines where this documentation section appears relative to other sections on the main documentation page (which is the parent of this folder in the miden-docs repository)
+position: 5
+collapsed: true
diff --git a/versioned_docs/version-0.14/builder/tools/clients/common-errors.md b/versioned_docs/version-0.14/builder/tools/clients/common-errors.md
new file mode 100644
index 00000000..f041814f
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/common-errors.md
@@ -0,0 +1,37 @@
+## Troubleshooting and transaction lifecycle
+
+This guide helps you troubleshoot common issues and understand the end-to-end lifecycle of transactions and notes in the Miden client.
+
+### Actionable error hints
+
+#### `ClientError::MissingOutputRecipients`
+- Cause: The MASM program emitted an output note whose recipient was not listed in `TransactionRequestBuilder::expected_output_recipients(...)`.
+- Fix: Reconcile the MASM recipient data with the Rust note structs and update the expected recipients so that the expected recipients are part of the transaction outputs.
+
+#### `TransactionRequestError::MissingAuthenticatedInputNote`
+- Cause: A note ID included in `TransactionRequestBuilder::authenticated_input_notes(...)` did not have a corresponding `InputNoteRecord` in the store, or it was not found to contain authentication data.
+- Fix: Import or sync the note, so its record and inclusion proof are present before building and executing the request.
+
+#### `TransactionRequestError::NoInputNotesNorAccountChange`
+- Cause: The transaction neither consumes input notes nor mutates tracked account state.
+- Fix: Add at least one authenticated/unauthenticated input note or include an explicit account state update in the request.
+
+#### `TransactionRequestError::StorageSlotNotFound`
+- Cause: The request referenced an account storage slot that does not exist, often because the ABI layout is incorrectly addressed (auth component is always the first component in the account component list).
+- Fix: Verify the account ABI and component ordering, then adjust the slot index used in the transaction.
+
+#### `TransactionExecutorError::ForeignAccountNotAnchoredInReference`
+- Cause: The foreign account proof was generated against a different block than the request’s reference block.
+- Fix: Re-fetch the foreign account proof anchored at the correct reference block and retry.
+
+#### `TransactionExecutorError::TransactionProgramExecutionFailed`
+- Cause: The MASM kernel failed during execution (e.g., failed assertion or constraint violation).
+- Fix: Re-run with the debug mode, capture VM diagnostics and inspect the source manager output to understand why execution failed.
+
+#### `ClientError::StoreError(AccountCommitmentAlreadyExists(...))`
+- Cause: The final account commitment already exists locally, usually because the transaction was applied previously.
+- Fix: Sync to confirm the transaction status and avoid resubmitting it; if you need a clean slate for development, reset the store.
+
+#### `ClientError::NoteNotFoundOnChain(Note ID)`/`RpcError::NoteNotFound(Note ID)`
+- Cause: The note has not been found on chain, or the input ID is incorrect.
+- Fix: Verify the note ID, ensure it has been committed, and run sync the client before retrying.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/account-a.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/account-a.png
new file mode 100644
index 00000000..4d01f73a
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/account-a.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/account-b.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/account-b.png
new file mode 100644
index 00000000..b85107c3
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/account-b.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/commit-height.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/commit-height.png
new file mode 100644
index 00000000..02693818
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/commit-height.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/consumed-note.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/consumed-note.png
new file mode 100644
index 00000000..391ff114
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/consumed-note.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/miden-account-list.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/miden-account-list.png
new file mode 100644
index 00000000..30915744
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/miden-account-list.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/note-view.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/note-view.png
new file mode 100644
index 00000000..1a3d0964
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/note-view.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/processing-note.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/processing-note.png
new file mode 100644
index 00000000..356215ae
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/processing-note.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/transaction-confirmation.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/transaction-confirmation.png
new file mode 100644
index 00000000..e535a88f
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/transaction-confirmation.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/two-accounts.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/two-accounts.png
new file mode 100644
index 00000000..9c989073
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/two-accounts.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/img/get-started/view-account-vault.png b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/view-account-vault.png
new file mode 100644
index 00000000..e2eeaa9f
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tools/clients/img/get-started/view-account-vault.png differ
diff --git a/versioned_docs/version-0.14/builder/tools/clients/index.md b/versioned_docs/version-0.14/builder/tools/clients/index.md
new file mode 100644
index 00000000..60b678b3
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/index.md
@@ -0,0 +1,52 @@
+---
+title: Clients
+description: "Miden client SDKs — Rust, TypeScript, and React surfaces for accounts, transactions, notes, and client-side proving."
+sidebar_position: 1
+pagination_prev: null
+---
+
+# Clients
+
+The Miden client manages accounts, builds and executes transactions, produces zero-knowledge proofs, and synchronises local state with the node. The same core ships across three consumer surfaces — pick the runtime that matches your application. All three share the same on-chain semantics.
+
+## SDKs
+
+
+
+ Native Rust library and CLI. Best for services, proving infrastructure, tests, scripting, and local exploration.
+
+
+ `@miden-sdk/miden-sdk` — Rust compiled to WebAssembly with a typed TypeScript API. Browser, Node, Electron, service workers.
+
+
+ `@miden-sdk/react` — `MidenProvider` + hooks (`useMiden`, `useAccount`, `useSend`, …) wrapping the Web SDK.
+
+
+
+## Pick a surface
+
+
+
+ Core state machine, transaction executor, prover, keystore abstraction, and note transport. Use it in native services, backend proving infrastructure, and integration tests.
+
+
+ Wraps the library as commands. Shipped in the same `miden-client` crate — good for local exploration and ops workflows.
+
+
+ Rust library compiled to WebAssembly with a typed `MidenClient` JavaScript class. Canonical TS/JS entry point for browser and Node apps.
+
+
+ `MidenProvider` + hooks wrapping the Web SDK. Drop it into a React / Next.js / React Native app for instant Miden integration.
+
+
+
+## Shared topics
+
+
+
+ Errors, diagnostic output, and recovery patterns shared across all surfaces.
+
+
+ End-to-end walkthroughs using each client surface — Miden Bank, recipes, helpers.
+
+
diff --git a/versioned_docs/version-0.14/builder/tools/clients/react-sdk/_category_.yml b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/_category_.yml
new file mode 100644
index 00000000..8859b318
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/_category_.yml
@@ -0,0 +1,4 @@
+label: React
+# Sidebar position within tools/clients/ (Rust is 1, TypeScript is 2, React is 3)
+position: 3
+collapsed: true
diff --git a/versioned_docs/version-0.14/builder/tools/clients/react-sdk/advanced.md b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/advanced.md
new file mode 100644
index 00000000..3562456f
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/advanced.md
@@ -0,0 +1,213 @@
+---
+title: Advanced
+sidebar_position: 5
+---
+
+# Advanced hooks
+
+Hooks beyond the core send / mint / consume trio: custom scripts, MASM compilation, session wallets, store backup, note serialization, and sync control.
+
+## `useTransaction`
+
+General-purpose transaction runner that accepts either a prebuilt `TransactionRequest` or a builder callback. This is the escape hatch when the higher-level hooks don't cover your flow.
+
+```tsx
+import { useTransaction } from "@miden-sdk/react";
+import { TransactionRequestBuilder } from "@miden-sdk/miden-sdk";
+
+const { execute, isLoading, stage } = useTransaction();
+
+// Direct request
+await execute({
+ accountId: contractAccount,
+ request: prebuiltRequest,
+});
+
+// Builder callback — receives the raw WebClient
+await execute({
+ accountId: contractAccount,
+ request: (client) =>
+ new TransactionRequestBuilder()
+ .withCustomScript(txScript)
+ .build(),
+});
+```
+
+`UseTransactionResult` exposes `execute` (not `executeTransaction`), plus `result`, `isLoading`, `stage`, `error`, and `reset`.
+
+`ExecuteTransactionOptions`:
+
+| Field | Description |
+| --- | --- |
+| `accountId` | Account the transaction applies to |
+| `request` | `TransactionRequest` or `(client: WebClient) => TransactionRequest \| Promise` |
+| `skipSync` | Skip pre-send auto-sync (default `false`) |
+| `privateNoteTarget` | Deliver private output notes to this account after commit (any `AccountRef` form) |
+
+The `privateNoteTarget` field is the 4-step pipeline shortcut: execute the tx, commit on-chain, then auto-deliver the private note through the note transport to the target. Useful for "send private note" UIs where the recipient already has the React SDK running.
+
+## `useExecuteProgram`
+
+View call — executes a transaction script locally and returns the stack output. No prove, no submit, no state change. Think of it as Miden's `eth_call`.
+
+```tsx
+import { useExecuteProgram } from "@miden-sdk/react";
+
+const { execute, isLoading, error } = useExecuteProgram();
+
+const result = await execute({
+ accountId: contractAccount,
+ script: compiledTxScript,
+ foreignAccounts: [counterAccount], // optional
+});
+
+// result.stack is a bigint[] — read indices directly
+const count: bigint = result.stack[0];
+console.log("Count:", count);
+```
+
+`UseExecuteProgramResult` exposes `execute` (not `executeProgram`), plus `result`, `isLoading`, `error`, and `reset`. No `stage` — view calls don't prove or submit.
+
+The React hook flattens the 16-element stack into a plain `bigint[]`. `useMidenClient()` exposes the underlying WASM `WebClient` directly — its method is `client.executeProgram(...)` (not namespaced under `client.transactions`). See the [Web SDK transactions guide](../web-client/transactions.md#view-calls-executeprogram) for the imperative `MidenClient.transactions.executeProgram` equivalent and the `FeltArray` shape.
+
+## `useCompile`
+
+Compiles Miden Assembly into `AccountComponent`, `TransactionScript`, or `NoteScript`. Each result method is independently callable — call only what you need for the current operation.
+
+```tsx
+import { useCompile } from "@miden-sdk/react";
+import { StorageSlot } from "@miden-sdk/miden-sdk";
+
+const { component, txScript, noteScript, isReady } = useCompile();
+
+// Account component
+const counterComponent = await component({
+ code: counterContractCode,
+ slots: [StorageSlot.emptyValue("miden::tutorials::counter")],
+});
+
+// Transaction script (with optional libraries)
+const script = await txScript({
+ code: `
+ use external_contract::counter_contract
+ begin
+ call.counter_contract::increment_count
+ end
+ `,
+ libraries: [
+ { namespace: "external_contract::counter_contract", code: counterContractCode },
+ ],
+});
+
+// Note script — v0.14 uses the @note_script attribute on a library proc
+const attachScript = await noteScript({
+ code: `
+ use miden::protocol::active_note
+ use miden::core::sys
+
+ @note_script
+ pub proc on_consume
+ # body runs when the consuming account redeems this note
+ exec.sys::truncate_stack
+ end
+ `,
+});
+```
+
+`UseCompileResult` exposes the three compile methods plus `isReady`. Loading and error state are tracked internally per call — catch errors at the individual `await` site. See the [Web SDK compile guide](../web-client/compile.md) for the full `CompileComponentOptions` / `CompileTxScriptOptions` / `CompileNoteScriptOptions` shapes.
+
+## `useSessionAccount`
+
+Drives the "session wallet" pattern — create a throw-away wallet, wait for a funding note, consume it, then hand control back to your app. Useful for one-off interactions that shouldn't touch a long-lived account.
+
+```tsx
+import { useSessionAccount } from "@miden-sdk/react";
+
+const { initialize, sessionAccountId, isReady, step, error, reset } =
+ useSessionAccount({
+ // Called with the new session wallet's bech32 ID once it's created.
+ // Your code is responsible for actually sending funds to it — e.g. by
+ // triggering a send from a main wallet elsewhere in the app.
+ fund: async (sessionAccountId) => {
+ await sendFromMainWallet(sessionAccountId);
+ },
+ assetId: usdcFaucetId, // optional
+ walletOptions: { storageMode: "private", mutable: true },
+ pollIntervalMs: 3_000, // default 3_000
+ maxWaitMs: 60_000, // default 60_000
+ });
+
+await initialize();
+// step progresses: "idle" → "creating" → "funding" → "consuming" → "ready"
+```
+
+`UseSessionAccountReturn`:
+
+| Field | Description |
+| --- | --- |
+| `initialize()` | Kicks off the create → fund → consume flow |
+| `sessionAccountId` | bech32 ID of the session wallet once created |
+| `isReady` | `true` after the funding note has been consumed |
+| `step` | `SessionAccountStep` — one of the five states above |
+| `error` | Non-null if any step failed |
+| `reset()` | Clears session data (and any persisted state under `storagePrefix`) |
+
+Session state persists under the configurable `storagePrefix` (default `"miden-session"`) so page reloads can resume mid-flow.
+
+## `useExportStore` / `useImportStore`
+
+Back up and restore the entire local store as a JSON dump. Handy for wallet backup/restore UIs.
+
+```tsx
+import { useExportStore, useImportStore, useMidenClient } from "@miden-sdk/react";
+
+// Export — returns a JSON string
+const { exportStore } = useExportStore();
+const dump: string = await exportStore();
+download(new Blob([dump]), "wallet-backup.json");
+
+// Import (destructive — overwrites the target store)
+// Positional: (storeDump, storeName, options?)
+const { importStore } = useImportStore();
+const client = useMidenClient();
+await importStore(uploadedDump, client.storeIdentifier(), { skipSync: false });
+```
+
+`ImportStoreOptions` exposes `skipSync` (default `false`) so you can defer the post-import sync. There's no second "raw bytes" form — `importStore` takes the JSON dump string as its first argument and the target store name as its second.
+
+## `useImportNote` / `useExportNote`
+
+Serialize notes to bytes for QR delivery or import notes handed over out-of-band. These complement the private-note transport layer — use the transport when the recipient is online, and QR/bytes when they aren't.
+
+```tsx
+import { useExportNote, useImportNote } from "@miden-sdk/react";
+
+const { exportNote } = useExportNote();
+const noteBytes = await exportNote(noteId);
+// encode noteBytes into a QR, link, email, etc.
+
+const { importNote } = useImportNote();
+await importNote(uploadedBytes);
+```
+
+## `useSyncControl`
+
+Pause and resume the auto-sync loop without dismounting `MidenProvider`. Useful when a long operation needs consistent local state, or during battery-sensitive background work.
+
+```tsx
+import { useSyncControl } from "@miden-sdk/react";
+
+const { pauseSync, resumeSync, isPaused } = useSyncControl();
+
+// Before a long sequence
+pauseSync();
+// ... operations that need a stable snapshot ...
+resumeSync();
+```
+
+`pauseSync()` stops the timer but doesn't cancel an in-flight sync — wait for `isSyncing` from `useSyncState()` to settle if you need a truly quiescent state.
+
+## Next
+
+- [Signers](./signers.md) — wire external wallets (Para, Turnkey, MidenFi) or build a custom signer.
+- [Recipes](./recipes.md) — end-to-end patterns.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/react-sdk/index.md b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/index.md
new file mode 100644
index 00000000..b47ec301
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/index.md
@@ -0,0 +1,44 @@
+---
+title: Overview
+sidebar_position: 1
+---
+
+# React SDK (@miden-sdk/react)
+
+The React SDK is a thin layer on top of the [Web SDK](../web-client/index.md). It wraps the underlying WASM `WebClient` with a React context (`MidenProvider`), a family of hooks (`useMiden`, `useAccount`, `useSend`, …), automatic sync polling, and a concurrency lock so multiple components never trip over the same WASM instance.
+
+## When to use it
+
+Reach for the React SDK when your application is already a React app (Next.js, Vite + React, React Native, Electron + React, etc.). The hooks:
+
+- own lifecycle management (the WASM worker, signer wiring, auto-sync loop),
+- expose per-hook result interfaces with a domain-named action (`send`, `mint`, `createWallet`, …) plus `isLoading` / `isCreating` / `isImporting`, `error`, and `reset`,
+- serialize mutations so concurrent component interactions don't corrupt the WASM state.
+
+If you are building a non-React app — a service worker, a Node backend, a vanilla-TS dApp — use the imperative [Web SDK](../web-client/index.md) directly.
+
+You can always reach the underlying WASM client from any hook via `useMidenClient()` when a hook doesn't cover what you need — it returns the low-level `WebClient`, not the imperative `MidenClient` wrapper.
+
+## What's in the package
+
+| Surface | Purpose |
+| --- | --- |
+| [`MidenProvider`](./setup.md) | Root React context; loads WASM, wires the client, runs auto-sync |
+| [`useMiden()`](./setup.md#client-lifecycle) | Raw lifecycle hook (`isReady`, `sync`, `runExclusive`) |
+| [`useMidenClient()`](./setup.md#client-lifecycle) | Shortcut for the ready WASM `WebClient` |
+| [Query hooks](./query-hooks.md) | `useAccount(s)`, `useNotes`, `useNoteStream`, `useTransactionHistory`, `useSyncState`, `useAssetMetadata` |
+| [Mutation hooks](./mutation-hooks.md) | `useCreateWallet`, `useCreateFaucet`, `useImportAccount`, `useSend`, `useMultiSend`, `useMint`, `useConsume`, `useSwap` |
+| [Advanced hooks](./advanced.md) | `useTransaction`, `useExecuteProgram`, `useCompile`, `useSessionAccount`, `useExportStore`, `useImportStore`, `useImportNote`, `useExportNote`, `useSyncControl`, `useWaitForCommit`, `useWaitForNotes` |
+| [External signers](./signers.md) | `MultiSignerProvider`, `SignerContext`, `useSigner`, `useMultiSigner` — pluggable wallet integrations (Para, Turnkey, MidenFi, custom) |
+| Utilities | `formatAssetAmount`, `parseAssetAmount`, `getNoteSummary`, `toBech32AccountId`, `createNoteAttachment` / `readNoteAttachment`, … |
+
+Each hook exports its own result interface — not a generic `{ data, isLoading, error }` wrapper. Data lives in named fields (e.g. `accounts`, `wallets`, `records`, `wallet`, `faucet`). Transaction-producing mutations additionally expose a `stage` field that advances through `idle → executing → proving → submitting → complete`. See [setup](./setup.md#hook-result-conventions) for per-family details.
+
+## Where to go next
+
+- [Setup](./setup.md) — install the package, wrap your app in `MidenProvider`, configure the network.
+- [Query hooks](./query-hooks.md) — read accounts, notes, sync state, and asset metadata.
+- [Mutation hooks](./mutation-hooks.md) — create wallets and faucets, send, mint, consume, swap.
+- [Advanced](./advanced.md) — custom scripts, MASM compilation, session accounts, note import/export.
+- [Signers](./signers.md) — external wallets (Para, Turnkey, MidenFi) and custom signer providers.
+- [Recipes](./recipes.md) — end-to-end patterns and a pointer to Philipp's full wallet tutorial.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/react-sdk/mutation-hooks.md b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/mutation-hooks.md
new file mode 100644
index 00000000..7bb02444
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/mutation-hooks.md
@@ -0,0 +1,330 @@
+---
+title: Mutation hooks
+sidebar_position: 4
+---
+
+# Mutation hooks
+
+Mutation hooks own the full transaction lifecycle — execute, prove, submit — and serialize under the Web SDK's concurrency lock so two components can't corrupt the WASM state.
+
+Two result-shape families show up across this page:
+
+**Transaction-producing hooks** (`useSend`, `useMultiSend`, `useMint`, `useConsume`, `useSwap`, `useTransaction`):
+
+```ts
+{
+ [action]: (options) => Promise; // send, sendMany, mint, ...
+ result: Result | null;
+ isLoading: boolean;
+ stage: TransactionStage;
+ error: Error | null;
+ reset: () => void;
+}
+```
+
+**Account-creation hooks** (`useCreateWallet`, `useCreateFaucet`, `useImportAccount`) don't go through the prove/submit pipeline, so they expose `isCreating` / `isImporting` instead of `isLoading` + `stage`:
+
+```ts
+{
+ createWallet: (opts?) => Promise;
+ wallet: Account | null;
+ isCreating: boolean; // or `isImporting` for useImportAccount
+ error: Error | null;
+ reset: () => void;
+}
+```
+
+Polling helpers (`useWaitForCommit`, `useWaitForNotes`) are simpler still — they expose just the action. Per-hook exact shapes are called out below.
+
+See [setup](./setup.md#hook-result-conventions) for the `TransactionStage` progression.
+
+## `useCreateWallet`
+
+Creates a new wallet account. Returns the `Account` object.
+
+```tsx
+import { useCreateWallet, AuthScheme } from "@miden-sdk/react";
+
+function NewWalletButton() {
+ const { createWallet, wallet, isCreating, error } = useCreateWallet();
+
+ const handleCreate = async () => {
+ const account = await createWallet({
+ storageMode: "private", // "private" | "public" | "network" (default private)
+ mutable: true, // default true — updatable code
+ authScheme: AuthScheme.AuthRpoFalcon512, // default
+ });
+ console.log("Created:", account.bech32id());
+ };
+
+ return (
+ Create wallet
+ );
+}
+```
+
+`CreateWalletOptions` (all optional):
+
+| Field | Default | Description |
+| --- | --- | --- |
+| `storageMode` | `"private"` | `"private"` / `"public"` / `"network"` |
+| `mutable` | `true` | Whether code can be updated after deployment |
+| `authScheme` | `AuthScheme.AuthRpoFalcon512` | Signing scheme |
+| `initSeed` | random | 32-byte seed for deterministic account-ID derivation |
+
+## `useCreateFaucet`
+
+Creates a fungible-token faucet.
+
+```tsx
+import { useCreateFaucet } from "@miden-sdk/react";
+
+function NewFaucetButton() {
+ const { createFaucet, faucet, isCreating } = useCreateFaucet();
+
+ const handleCreate = async () => {
+ const created = await createFaucet({
+ tokenSymbol: "TEST",
+ decimals: 8, // default 8
+ maxSupply: 10_000_000n, // number | bigint
+ storageMode: "public", // default "private"; public allows FPI reads
+ });
+ console.log("Faucet:", created.bech32id());
+ };
+
+ return (
+ Create faucet
+ );
+}
+```
+
+`CreateFaucetOptions`:
+
+| Field | Default | Description |
+| --- | --- | --- |
+| `tokenSymbol` | required | Display symbol (e.g. `"USDC"`) |
+| `maxSupply` | required | `bigint \| number` |
+| `decimals` | `8` | Token decimals |
+| `storageMode` | `"private"` | Public faucets are discoverable/readable on-chain |
+| `authScheme` | `AuthScheme.AuthRpoFalcon512` | Signing scheme |
+
+## `useSend`
+
+Sends tokens from one account to another.
+
+```tsx
+import { useSend } from "@miden-sdk/react";
+
+function SendForm({ from, to, usdcFaucetId }: Props) {
+ const { send, isLoading, stage, error } = useSend();
+
+ const handleSend = async () => {
+ const { txId, note } = await send({
+ from,
+ to,
+ assetId: usdcFaucetId,
+ amount: 100n,
+ noteType: "private", // default "private"
+ });
+ console.log("Transaction:", txId);
+ };
+
+ return (
+
+ {isLoading ? `${stage}…` : "Send"}
+
+ );
+}
+```
+
+`SendOptions`:
+
+| Field | Required | Description |
+| --- | --- | --- |
+| `from` | yes | Sender `AccountRef` |
+| `to` | yes | Recipient `AccountRef` |
+| `assetId` | yes | Token faucet `AccountRef` |
+| `amount` | unless `sendAll` | `bigint \| number` |
+| `noteType` | — | `"private"` / `"public"` (default `"private"`) |
+| `recallHeight` | — | Block height after which sender can reclaim the note |
+| `timelockHeight` | — | Block height after which recipient can consume the note |
+| `attachment` | — | `bigint[] \| Uint8Array \| number[]` — payload attached to the note |
+| `skipSync` | — | Skip the pre-send auto-sync (default `false`) |
+| `sendAll` | — | Drain full balance of `assetId` — when `true`, `amount` is ignored |
+| `returnNote` | — | Return the `Note` object in the result (for out-of-band delivery, QR codes, etc.) |
+
+`SendResult`: `{ txId: string; note: Note | null }`. `note` is non-null only when `returnNote: true`.
+
+## `useMultiSend`
+
+Batches multiple recipients into one transaction. All outputs must share the same sender and asset. The action function is named `sendMany`.
+
+```tsx
+import { useMultiSend } from "@miden-sdk/react";
+
+const { sendMany, isLoading } = useMultiSend();
+
+await sendMany({
+ from: senderAccountId,
+ assetId: faucetId,
+ recipients: [
+ { to: recipient1, amount: 500n, noteType: "private" },
+ { to: recipient2, amount: 300n },
+ ],
+ noteType: "private", // default for recipients that don't override
+});
+```
+
+`MultiSendOptions`:
+
+| Field | Description |
+| --- | --- |
+| `from`, `assetId` | Single sender + single faucet for the whole batch |
+| `recipients` | `MultiSendRecipient[]` — each `{ to, amount, noteType?, attachment? }` |
+| `noteType` | Default for recipients that don't specify one (default `"private"`) |
+| `skipSync` | Skip pre-send auto-sync |
+
+## `useMint`
+
+Mints tokens from a faucet you control into a recipient account.
+
+```tsx
+import { useMint } from "@miden-sdk/react";
+
+const { mint, isLoading, stage } = useMint();
+
+const result = await mint({
+ targetAccountId: recipient,
+ faucetId: myFaucet,
+ amount: 10_000n,
+ noteType: "private", // default "private"
+});
+console.log("Mint tx:", result.transactionId);
+```
+
+`MintOptions`:
+
+| Field | Description |
+| --- | --- |
+| `targetAccountId` | Recipient `AccountRef` |
+| `faucetId` | Faucet `AccountRef` (must be owned by the caller) |
+| `amount` | `bigint \| number` |
+| `noteType` | `"private"` / `"public"` (default `"private"`) |
+
+Returns `TransactionResult`: `{ transactionId: string }`.
+
+## `useConsume`
+
+Claims one or more notes into an account.
+
+```tsx
+import { useConsume } from "@miden-sdk/react";
+
+const { consume, isLoading } = useConsume();
+
+await consume({
+ accountId: myAccountId,
+ notes: [noteIdHex1, noteIdHex2], // hex IDs, NoteId objects, InputNoteRecord, or Note
+});
+```
+
+`ConsumeOptions`:
+
+| Field | Description |
+| --- | --- |
+| `accountId` | Account consuming the notes |
+| `notes` | `(string \| NoteId \| InputNoteRecord \| Note)[]` — mix-and-match accepted |
+
+## `useSwap`
+
+Atomic swap between two assets.
+
+```tsx
+import { useSwap } from "@miden-sdk/react";
+
+const { swap, isLoading } = useSwap();
+
+await swap({
+ accountId: myWallet,
+ offeredFaucetId: usdcFaucet,
+ offeredAmount: 100n,
+ requestedFaucetId: dagFaucet,
+ requestedAmount: 200n,
+ noteType: "public", // default "private"
+ paybackNoteType: "private", // default "private"
+});
+```
+
+## `useImportAccount`
+
+Imports an account by ID (fetches from network), by previously-exported file, or by seed.
+
+```tsx
+import { useImportAccount, AuthScheme } from "@miden-sdk/react";
+
+const { importAccount, account, isImporting, error } = useImportAccount();
+
+// By ID — public accounts only
+const imported = await importAccount({
+ type: "id",
+ accountId: "mtst1qy35...",
+});
+
+// By seed — public accounts only
+await importAccount({
+ type: "seed",
+ seed: initSeed, // Uint8Array
+ mutable: true, // default true
+ authScheme: AuthScheme.AuthRpoFalcon512,
+});
+
+// By file — works for both public and private accounts
+await importAccount({
+ type: "file",
+ file: accountFileBytes, // AccountFile | Uint8Array | ArrayBuffer
+});
+```
+
+The `type` discriminant is required. For private accounts, use the `"file"` variant — private account state isn't reconstructible from a seed alone.
+
+## `useWaitForCommit` / `useWaitForNotes`
+
+Polling helpers for transaction confirmation and note inbox arrivals. Both are minimal hooks — they only expose the action function.
+
+### `useWaitForCommit`
+
+Signature: `waitForCommit(txId, options?)` — `txId` is positional (hex string or `TransactionId`), `options` are merged with defaults.
+
+```tsx
+import { useWaitForCommit } from "@miden-sdk/react";
+
+const { waitForCommit } = useWaitForCommit();
+await waitForCommit(result.txId, {
+ timeoutMs: 30_000, // default 10_000
+ intervalMs: 1_000, // default 1_000
+});
+```
+
+### `useWaitForNotes`
+
+Exposes `waitForConsumableNotes(options)` and returns the matching `ConsumableNoteRecord[]` when the threshold is reached.
+
+```tsx
+import { useWaitForNotes } from "@miden-sdk/react";
+
+const { waitForConsumableNotes } = useWaitForNotes();
+
+const notes = await waitForConsumableNotes({
+ accountId: recipient,
+ minCount: 1, // default 1
+ timeoutMs: 30_000,
+});
+```
+
+Both reject on timeout — wrap them in `try/catch` when you want a graceful fallback.
+
+## Next
+
+- [Advanced](./advanced.md) — custom scripts, MASM compilation, session accounts, store import/export.
+- [Signers](./signers.md) — external wallets (Para, Turnkey, MidenFi) and custom signers.
+- [Recipes](./recipes.md) — realistic patterns + link-outs to Philipp's full wallet tutorial.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/react-sdk/query-hooks.md b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/query-hooks.md
new file mode 100644
index 00000000..2474f6b6
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/query-hooks.md
@@ -0,0 +1,282 @@
+---
+title: Query hooks
+sidebar_position: 3
+---
+
+# Query hooks
+
+Query hooks read from the local store (and trigger a fetch when the cache is cold). Every query hook shares `{ isLoading, error, refetch }` alongside hook-specific data fields. Refetching is automatic after successful syncs, so most components don't need to call `refetch()` manually.
+
+## `useAccounts`
+
+Lists every account tracked by the client, pre-categorised into wallets and faucets.
+
+```tsx
+import { useAccounts } from "@miden-sdk/react";
+
+function AccountList() {
+ const { accounts, wallets, faucets, isLoading, error } = useAccounts();
+
+ if (isLoading) return Loading…
;
+ if (error) return {error.message}
;
+
+ return (
+ <>
+ Wallets ({wallets.length})
+ {wallets.map((w) => {w.id().toString()}
)}
+
+ Faucets ({faucets.length})
+ {faucets.map((f) => {f.id().toString()}
)}
+ >
+ );
+}
+```
+
+Return type (`AccountsResult`):
+
+```ts
+{
+ accounts: AccountHeader[]; // every tracked account
+ wallets: AccountHeader[]; // regular accounts
+ faucets: AccountHeader[]; // token faucets
+ isLoading: boolean;
+ error: Error | null;
+ refetch: () => Promise;
+}
+```
+
+## `useAccount(id)`
+
+Full details for a single account, including per-asset balances decorated with symbol + decimals when metadata is available.
+
+```tsx
+import { useAccount } from "@miden-sdk/react";
+
+function AccountDetails({ id }: { id: string }) {
+ const { account, assets, getBalance, isLoading, error } = useAccount(id);
+
+ if (isLoading) return Loading…
;
+ if (error) return {error.message}
;
+ if (!account) return Not found
;
+
+ return (
+ <>
+ Account: {account.bech32id()}
+ Nonce: {account.nonce().toString()}
+ USDC balance: {getBalance(usdcFaucetId).toString()}
+
+
+ {assets.map((a) => (
+
+ {a.amount.toString()} {a.symbol ?? a.assetId}
+
+ ))}
+
+ >
+ );
+}
+```
+
+Return type (`AccountResult`):
+
+```ts
+{
+ account: Account | null;
+ assets: AssetBalance[]; // { assetId, amount, symbol?, decimals? }
+ isLoading: boolean;
+ error: Error | null;
+ refetch: () => Promise;
+ getBalance: (assetId: string) => bigint;
+}
+```
+
+`getBalance(assetId)` is a convenience for single-asset reads — returns `0n` when the account doesn't hold that asset.
+
+## `useNotes(filter?)`
+
+Lists input notes (received) and consumable notes (ready to claim) with optional filtering.
+
+```tsx
+import { useNotes } from "@miden-sdk/react";
+
+function NotesInbox({ account }: { account: string }) {
+ const { notes, consumableNotes, noteSummaries, refetch } = useNotes({
+ status: "committed",
+ accountId: account,
+ });
+
+ return (
+ <>
+ Refresh
+ Received ({notes.length})
+ {noteSummaries.map((s) => (
+
+ {s.assets.map((a) => `${a.amount} ${a.symbol ?? a.assetId}`).join(", ")}
+
+ ))}
+
+ Consumable ({consumableNotes.length})
+ >
+ );
+}
+```
+
+Filter options (`NotesFilter`):
+
+| Field | Values | Description |
+| --- | --- | --- |
+| `status` | `"all" \| "consumed" \| "committed" \| "expected" \| "processing"` | Filter by note lifecycle state |
+| `accountId` | `AccountRef` | Only notes relevant to this account |
+| `sender` | `string` | Account ID in any accepted format (hex or bech32) — normalised internally |
+| `excludeIds` | `string[]` | Skip these note IDs (useful for hiding notes your UI already rendered elsewhere) |
+
+Return type (`NotesResult`):
+
+```ts
+{
+ notes: InputNoteRecord[]; // raw SDK records
+ consumableNotes: ConsumableNoteRecord[];
+ noteSummaries: NoteSummary[]; // pre-computed { id, assets[], sender? }
+ consumableNoteSummaries: NoteSummary[];
+ isLoading: boolean;
+ error: Error | null;
+ refetch: () => Promise;
+}
+```
+
+`noteSummaries` is the pragmatic choice for UIs — it pre-extracts asset info and runs metadata resolution.
+
+## `useNoteStream(options?)`
+
+Temporal note tracking with first-seen timestamps and per-stream filtering. Useful for notification UIs that want to highlight new arrivals.
+
+```tsx
+import { useNoteStream } from "@miden-sdk/react";
+
+function NewNotesToast() {
+ const { notes, latest, markHandled, markAllHandled } = useNoteStream({
+ status: "committed", // default "committed"
+ since: Date.now(), // numeric timestamp; drop notes seen earlier
+ amountFilter: (amount) => amount > 0n,
+ });
+
+ return (
+ <>
+ {latest && New: {latest.id}
}
+ {notes.map((n) => (
+
+ {n.id} at {new Date(n.firstSeenAt).toISOString()}
+ markHandled(n.id)}>dismiss
+
+ ))}
+ Dismiss all
+ >
+ );
+}
+```
+
+`UseNoteStreamOptions` fields: `status`, `sender`, `since` (numeric timestamp), `excludeIds` (`Set` or `string[]`), and `amountFilter` for predicate-based filtering. The stream also exposes `snapshot()` for passing state across unmount / remount boundaries.
+
+## `useTransactionHistory(options?)`
+
+Transaction records, with optional filters for specific IDs or a custom `TransactionFilter`.
+
+```tsx
+import { useTransactionHistory } from "@miden-sdk/react";
+
+function HistoryTable() {
+ const { records, isLoading } = useTransactionHistory();
+ if (isLoading) return Loading…
;
+
+ return (
+
+
+ {records.map((tx) => (
+
+ {tx.id().toHex()}
+ {tx.blockNum().toString()}
+
+ ))}
+
+
+ );
+}
+```
+
+Options:
+
+| Field | Description |
+| --- | --- |
+| `id` | Single transaction ID lookup |
+| `ids` | List of transaction IDs |
+| `filter` | Custom `TransactionFilter` (overrides `id` / `ids`) |
+| `refreshOnSync` | Re-fetch after every auto-sync (default `true`) |
+
+Result (`TransactionHistoryResult`):
+
+```ts
+{
+ records: TransactionRecord[];
+ record: TransactionRecord | null; // convenience when a single id was provided
+ status: TransactionStatus | null; // convenience when a single id was provided
+ isLoading: boolean;
+ error: Error | null;
+ refetch: () => Promise;
+}
+```
+
+For account-scoped history use a `TransactionFilter` that targets the account — see the `@miden-sdk/miden-sdk` `TransactionFilter` API.
+
+## `useSyncState`
+
+Sync heights and manual-trigger controls.
+
+```tsx
+import { useSyncState } from "@miden-sdk/react";
+
+function SyncBadge() {
+ const { syncHeight, isSyncing, lastSyncTime, sync } = useSyncState();
+
+ return (
+ sync()} disabled={isSyncing}>
+ Block {syncHeight ?? "—"} {isSyncing && "(syncing…)"}
+
+ );
+}
+```
+
+Manual `sync()` composes with the auto-sync loop (configured via `autoSyncInterval` on `MidenProvider`) — call it when you want to force an immediate refresh.
+
+## `useAssetMetadata(assetIds?)`
+
+Symbol + decimals lookup for a batch of asset IDs. The argument is an optional `string[]`; pass an empty array (or nothing) to read the global cache without triggering new fetches.
+
+```tsx
+import { useAssetMetadata } from "@miden-sdk/react";
+
+function TokenChip({ assetId }: { assetId: string }) {
+ const { assetMetadata } = useAssetMetadata([assetId]);
+ const meta = assetMetadata.get(assetId);
+ return {meta?.symbol ?? assetId} ;
+}
+
+function TokenLegend({ ids }: { ids: string[] }) {
+ const { assetMetadata } = useAssetMetadata(ids);
+ return (
+ <>
+ {ids.map((id) => {
+ const m = assetMetadata.get(id);
+ return {m?.symbol ?? id} ;
+ })}
+ >
+ );
+}
+```
+
+`assetMetadata` is a `Map` keyed by asset ID. The hook dedupes and caches across components, so siblings that ask for overlapping IDs share cost.
+
+## Next
+
+- [Mutation hooks](./mutation-hooks.md) — create wallets, faucets, send tokens, consume notes.
+- [Advanced](./advanced.md) — custom scripts, note import/export, session accounts.
+- [Recipes](./recipes.md) — end-to-end patterns.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/react-sdk/recipes.md b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/recipes.md
new file mode 100644
index 00000000..c6801a96
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/recipes.md
@@ -0,0 +1,149 @@
+---
+title: Recipes
+sidebar_position: 7
+---
+
+# Recipes
+
+Short patterns covering the common cases. For longer walkthroughs — building a full wallet app from scratch, including UI — see the [React wallet tutorial](https://github.com/0xMiden/tutorials/blob/main/docs/src/web-client/react_wallet_tutorial.md) in the tutorials repo, which uses these hooks end-to-end.
+
+## Show transaction progress
+
+Every mutation hook exposes `isLoading` and `stage`; use them for optimistic UI:
+
+```tsx
+import { useSend } from "@miden-sdk/react";
+
+function SendButton({ from, to, assetId }: Props) {
+ const { send, stage, isLoading, error } = useSend();
+
+ const handleSend = async () => {
+ try {
+ await send({ from, to, assetId, amount: 100n });
+ } catch (err) {
+ console.error("Send failed:", err);
+ }
+ };
+
+ return (
+ <>
+
+ {isLoading ? `${stage}…` : "Send"}
+
+ {error && {error.message}
}
+ >
+ );
+}
+```
+
+## Format token amounts
+
+```tsx
+import { formatAssetAmount, parseAssetAmount } from "@miden-sdk/react";
+
+// Display: 1_000_000n with 8 decimals → "0.01"
+const display = formatAssetAmount(balance, 8);
+
+// User input: "0.01" with 8 decimals → 1_000_000n
+const amount = parseAssetAmount("0.01", 8);
+```
+
+## Display a note summary
+
+```tsx
+import { getNoteSummary, formatNoteSummary } from "@miden-sdk/react";
+
+const summary = getNoteSummary(note);
+const text = formatNoteSummary(summary); // "1.5 USDC"
+```
+
+`noteSummaries` from `useNotes()` already runs `getNoteSummary` for you — these helpers are for ad-hoc formatting elsewhere.
+
+## Wait for confirmation after a send
+
+```tsx
+import { useSend, useWaitForCommit } from "@miden-sdk/react";
+
+const { send } = useSend();
+const { waitForCommit } = useWaitForCommit();
+
+const result = await send({ from, to, assetId, amount: 100n });
+await waitForCommit(result.txId);
+```
+
+## Drop to the raw client
+
+```tsx
+import { useMidenClient } from "@miden-sdk/react";
+
+function BlockHeaderPeek() {
+ const client = useMidenClient();
+ const header = await client.getBlockHeaderByNumber(100);
+ // ... whatever the hooks don't expose
+}
+```
+
+`useMidenClient()` throws if the provider isn't ready — guard with `useMiden().isReady` when you render before init.
+
+## Prevent race conditions
+
+Two user actions can fire in quick succession — a double-click on "Send", or a hook plus a manual button both wanting to sign. The React SDK exposes a lock:
+
+```tsx
+import { useMiden, useMidenClient } from "@miden-sdk/react";
+
+function CompoundFlow() {
+ const { runExclusive } = useMiden();
+ const client = useMidenClient();
+
+ const run = () =>
+ runExclusive(async () => {
+ // Multiple client calls that must not interleave with other hooks'
+ // WASM work run here — the lock serialises them across the whole app.
+ await client.sync();
+ // ...
+ });
+
+ return Run ;
+}
+```
+
+`runExclusive(fn)` takes a zero-argument async function; reach for the client via `useMidenClient()` inside it. Built-in mutations already use this lock internally; `runExclusive` is the escape hatch for your own compound flows.
+
+## Isolated clients for multi-wallet apps
+
+`MidenProvider`'s config does not accept a `storeName` directly. Per-user isolation flows through the active signer: each `SignerContext.Provider` supplies its own `storeName` field, and `MidenProvider` reads that when initialising the underlying client. See the [Signers](./signers.md#custom-signer-providers) guide for a custom signer that picks a unique store name per connected user (typically the wallet address or a hash of it).
+
+If you just need two wallets side-by-side in a dev environment and don't want to wire a signer, mount two separate `MidenProvider`s in isolated subtrees backed by different signer contexts.
+
+## Account IDs — hex and bech32 interchangeably
+
+Every hook accepts either:
+
+```tsx
+// Both are valid
+useAccount("0x1234567890abcdef");
+useAccount("mtst1qy35...");
+
+// Convert for display
+account.bech32id(); // "mtst1qy35..."
+
+import { toBech32AccountId } from "@miden-sdk/react";
+toBech32AccountId(someHexId); // "mtst1qy35..."
+```
+
+## Troubleshooting
+
+| Symptom | Likely cause |
+| --- | --- |
+| `"Client not ready"` thrown by a hook | Component rendered before `MidenProvider` finished initializing. Guard with `useMiden().isReady` or render via `MidenProvider`'s `loadingComponent`. |
+| Transactions stuck in `"proving"` | Remote prover unreachable. Check `prover` config and network; consider `prover: { primary: "testnet", fallback: "local" }`. |
+| Notes not appearing after mint | Call `sync()` from `useSyncState()` or verify `autoSyncInterval` isn't `0`. |
+| Bech32 address has wrong prefix | `rpcUrl` doesn't match the network you intended. `"testnet"` → `mtst1...`, `"devnet"` → `mdev1...`. |
+| WASM init fails in dev | Ensure your bundler serves `.wasm` with the `application/wasm` MIME type. Vite does this automatically; some custom setups don't. |
+| `"A send is already in progress"` | Two `useSend` mutations fired simultaneously. Either `await` the previous call before starting the next, or use `runExclusive` to coordinate. |
+
+## Next
+
+- Longer walkthrough: [React wallet tutorial](https://github.com/0xMiden/tutorials/blob/main/docs/src/web-client/react_wallet_tutorial.md) — builds a complete wallet app on top of these hooks.
+- Reference: [Setup](./setup.md), [Query hooks](./query-hooks.md), [Mutation hooks](./mutation-hooks.md), [Advanced](./advanced.md), [Signers](./signers.md).
diff --git a/versioned_docs/version-0.14/builder/tools/clients/react-sdk/setup.md b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/setup.md
new file mode 100644
index 00000000..df95b16e
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/setup.md
@@ -0,0 +1,211 @@
+---
+title: Setup
+sidebar_position: 2
+---
+
+# Setting up the React SDK
+
+## Install
+
+The React SDK has a hard peer dependency on `@miden-sdk/miden-sdk` — install both:
+
+```bash
+npm install @miden-sdk/react @miden-sdk/miden-sdk
+# or
+yarn add @miden-sdk/react @miden-sdk/miden-sdk
+# or
+pnpm add @miden-sdk/react @miden-sdk/miden-sdk
+```
+
+React 18 or newer is required.
+
+## Wrap your app in `MidenProvider`
+
+`MidenProvider` loads the Web SDK's WebAssembly module, spins up the dedicated worker, wires the keystore, and kicks off the auto-sync loop. Put it at the root of your React tree — typically in `App.tsx` or your Next.js root layout.
+
+```tsx
+import { MidenProvider } from "@miden-sdk/react";
+
+function App() {
+ return (
+
+
+
+ );
+}
+```
+
+Every hook in the rest of this section assumes a `MidenProvider` is mounted somewhere above it.
+
+## Configuration
+
+```tsx
+ } // rendered while WASM boots
+ errorComponent={ } // rendered if init fails
+>
+
+
+```
+
+### `MidenConfig` fields
+
+| Field | Type | Description |
+| --- | --- | --- |
+| `rpcUrl` | `"devnet" \| "testnet" \| "localhost" \| string` | Node RPC endpoint. Shorthands expand to hosted Miden endpoints; any other string is treated as a raw URL. |
+| `prover` | `"local" \| "devnet" \| "testnet" \| string \| ProverConfig` | Default prover. `"local"` runs in-browser. `ProverConfig` supports a `primary` + `fallback` pair if you want automatic fallback. |
+| `autoSyncInterval` | `number` | Milliseconds between automatic sync pulls. `0` disables the loop (you can still call `sync()` manually). Default: 15000. |
+| `noteTransportUrl` | `"devnet" \| "testnet" \| string` | Note transport service. Required for `sendPrivate` / `fetchPrivate`. |
+| `proverTimeoutMs` | `number` | Per-transaction prover timeout. |
+| `seed` | `Uint8Array` | 32-byte RNG seed for deterministic account-ID derivation in tests. |
+
+### Network shorthands
+
+| Shorthand | Meaning |
+| --- | --- |
+| `devnet` | Development / pre-production testing, fake tokens |
+| `testnet` | Pre-production testing against the hosted Miden testnet |
+| `localhost` | Local node at `http://localhost:57291` |
+
+### `loadingComponent` and `errorComponent`
+
+- `loadingComponent` is rendered during the brief WASM load phase (first render only).
+- `errorComponent` is rendered if initialization fails. It accepts either a `ReactNode` or `(error: Error) => ReactNode`.
+
+Both are optional. Leaving them unset uses sensible defaults.
+
+## Client lifecycle
+
+`useMiden()` is the raw context hook. Most apps never need it — the specialized hooks are easier — but it's there when you want to reach into lifecycle state directly.
+
+```tsx
+import { useMiden } from "@miden-sdk/react";
+
+function Status() {
+ const { isReady, isInitializing, error, sync, runExclusive } = useMiden();
+
+ if (isInitializing) return Loading Miden…
;
+ if (error) return Init error: {error.message}
;
+
+ return sync()}>Sync ;
+}
+```
+
+- `isReady` — `true` once the WASM module, keystore, and signer are fully initialised.
+- `isInitializing` — `true` during the first load.
+- `error` — non-null if init failed.
+- `sync()` — trigger a manual sync pass outside the auto-sync loop.
+- `runExclusive(fn: () => Promise): Promise` — serialize a block of async work under the internal lock. `fn` takes no arguments; reach for the client via `useMidenClient()` if you need one inside. See [race conditions](./recipes.md#prevent-race-conditions).
+
+`useMidenClient()` is a shortcut that returns the ready `WebClient` directly, throwing if the provider isn't ready yet:
+
+```tsx
+import { useMidenClient } from "@miden-sdk/react";
+
+function AdvancedCall() {
+ const client = useMidenClient();
+ const header = await client.getBlockHeaderByNumber(100);
+ // ...
+}
+```
+
+Use it for APIs the React SDK hooks don't expose.
+
+## Hook result conventions
+
+Each hook exports its own result interface — `UseSendResult`, `AccountsResult`, `NotesResult`, and so on — rather than a generic `QueryResult` wrapper. Data lives in named fields (e.g. `accounts`, `wallets`, `faucets`) not inside a common `data` key. The shared machinery is narrower than that:
+
+### Query hooks
+
+Every query hook exposes at least:
+
+```ts
+{
+ isLoading: boolean;
+ error: Error | null;
+ refetch: () => Promise;
+}
+```
+
+Plus the hook-specific data fields. For example:
+
+```tsx
+const { wallets, faucets, isLoading, error, refetch } = useAccounts();
+
+if (isLoading) return ;
+if (error) return {error.message}
;
+return ;
+```
+
+### Mutation hooks
+
+Every mutation hook exposes:
+
+```ts
+{
+ // Domain-specific action function — `send` for useSend, `mint` for useMint, etc.
+ [action]: (options) => Promise;
+ result: Result | null;
+ isLoading: boolean;
+ stage: TransactionStage;
+ error: Error | null;
+ reset: () => void;
+}
+```
+
+The action function name mirrors the hook: `useSend` returns `send`, `useMint` returns `mint`, `useConsume` returns `consume`. That keeps call sites readable without destructured renames.
+
+Transaction-producing mutations progress through the `TransactionStage` states:
+
+```ts
+type TransactionStage =
+ | "idle"
+ | "executing"
+ | "proving"
+ | "submitting"
+ | "complete";
+```
+
+Pattern:
+
+```tsx
+const { send, stage, isLoading, error } = useSend();
+
+return (
+ send({ from, to, assetId, amount: 100n })}
+ disabled={isLoading}
+ >
+ {isLoading ? `${stage}…` : "Send"}
+
+);
+```
+
+See [Mutation hooks](./mutation-hooks.md) for the full surface.
+
+## Account ID formats
+
+Every hook accepts either form:
+
+```tsx
+useAccount("0x1234567890abcdef"); // hex
+useAccount("mtst1qy35..."); // bech32 (testnet prefix)
+
+// Convert for display
+account.bech32id(); // "mtst1qy35..." on testnet
+```
+
+The SDK normalises internally — you don't need to convert yourself. Bech32 prefixes encode the network: `mtst1…` on testnet, `mdev1…` on devnet. The prefix is derived from the `rpcUrl` you configured on `MidenProvider`.
+
+## Next
+
+- [Query hooks](./query-hooks.md) — read account, note, sync, and metadata state.
+- [Mutation hooks](./mutation-hooks.md) — create wallets, send, mint, consume, swap.
+- [Advanced](./advanced.md) — custom scripts, session wallets, import/export.
+- [Signers](./signers.md) — integrate Para, Turnkey, MidenFi, or a custom wallet.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/react-sdk/signers.md b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/signers.md
new file mode 100644
index 00000000..64a39269
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/react-sdk/signers.md
@@ -0,0 +1,224 @@
+---
+title: External signers
+sidebar_position: 6
+---
+
+# External signers
+
+The React SDK treats signing as a pluggable contract: `MidenProvider` accepts any `SignerContext` that exposes a `signCb` function, and hooks call into it whenever a transaction needs a signature. Prebuilt providers exist for the major wallet integrations; you can also build your own.
+
+## Built-in signer providers
+
+### Para (EVM wallets)
+
+```tsx
+import { ParaSignerProvider } from "@miden-sdk/para";
+import { MidenProvider } from "@miden-sdk/react";
+
+function App() {
+ return (
+
+
+
+
+
+ );
+}
+```
+
+Expose Para-specific data inside your app:
+
+```tsx
+import { useParaSigner } from "@miden-sdk/para";
+
+const { para, wallet, isConnected } = useParaSigner();
+```
+
+### Turnkey
+
+```tsx
+import { TurnkeySignerProvider } from "@miden-sdk/miden-turnkey-react";
+
+// Config is optional — defaults to https://api.turnkey.com and reads
+// VITE_TURNKEY_ORG_ID from the environment.
+
+
+
+
+
+
+// Or with explicit config:
+
+ ...
+
+```
+
+Connect via passkey authentication. `useSigner()` returns `SignerContextValue | null` (null when no signer provider is above the component), so always null-guard:
+
+```tsx
+import { useSigner } from "@miden-sdk/react";
+import { useTurnkeySigner } from "@miden-sdk/miden-turnkey-react";
+
+function ConnectButton() {
+ const signer = useSigner();
+ const turnkey = useTurnkeySigner(); // call unconditionally — rules of hooks
+
+ if (!signer) return null; // no signer provider mounted
+
+ if (!signer.isConnected) {
+ return Connect ;
+ }
+ return (
+
+ Disconnect ({turnkey.account?.name})
+
+ );
+}
+```
+
+### MidenFi wallet adapter
+
+```tsx
+import { MidenFiSignerProvider } from "@miden-sdk/wallet-adapter-react";
+
+
+
+
+
+
+```
+
+## Unified signer interface
+
+Every prebuilt provider exposes the same `useSigner` surface, so UI code that only cares about connect/disconnect stays signer-agnostic. Note the return is `SignerContextValue | null`:
+
+```tsx
+import { useSigner } from "@miden-sdk/react";
+
+function Header() {
+ const signer = useSigner();
+ if (!signer) return null; // no signer provider above
+
+ if (!signer.isConnected) {
+ return Connect {signer.name} ;
+ }
+ return Disconnect ;
+}
+```
+
+## Custom signer providers
+
+For a signing service that doesn't have a prebuilt provider — internal HSM, hardware wallet, or experimental integration — wire `SignerContext` directly:
+
+```tsx
+import { SignerContext, type SignerContextValue } from "@miden-sdk/react";
+
+const signer: SignerContextValue = {
+ name: "MyWallet",
+ storeName: `mywallet_${userAddress}`, // unique per user for DB isolation
+ isConnected: true,
+ accountConfig: {
+ publicKeyCommitment: userPublicKeyCommitment, // Uint8Array
+ storageMode: "private",
+ accountType: "RegularAccountUpdatableCode",
+ },
+ signCb: async (pubKey, signingInputs) => {
+ // Route to your signing service
+ return signature; // Uint8Array
+ },
+ connect: async () => {
+ /* trigger wallet connection */
+ },
+ disconnect: async () => {
+ /* clear session */
+ },
+};
+
+function App() {
+ return (
+
+
+
+
+
+ );
+}
+```
+
+`storeName` is critical: each user's data lives in its own IndexedDB database, so make the `storeName` unique per signing identity (typically the wallet address or a derived hash).
+
+## Custom `AccountComponent`s
+
+Attach application-specific components — compiled from `.masp` packages, e.g. a DEX module — alongside the default auth and basic wallet components:
+
+```tsx
+import { type SignerAccountConfig } from "@miden-sdk/react";
+import { AccountComponent } from "@miden-sdk/miden-sdk";
+
+const myDexComponent: AccountComponent = await loadCompiledComponent();
+
+const accountConfig: SignerAccountConfig = {
+ publicKeyCommitment: userPublicKeyCommitment,
+ accountType: "RegularAccountUpdatableCode",
+ storageMode: "private",
+ customComponents: [myDexComponent],
+};
+```
+
+Components are appended to the `AccountBuilder` after the default basic wallet component and before `build()` is called, so the account always includes wallet functionality plus any extras you pass. The field is optional; leaving it unset (or passing an empty array) preserves the default behaviour.
+
+## `MultiSignerProvider`
+
+For apps that need to swap between multiple signer providers at runtime (e.g. "connect with Para" or "connect with Turnkey"), use `MultiSignerProvider`. Each registered signer provider renders its own ` ` (the component takes no children — it registers the provider's current `SignerContext` with the multi-signer context) and `MidenProvider` sits as a sibling inside `MultiSignerProvider`:
+
+```tsx
+import { MultiSignerProvider, SignerSlot, MidenProvider } from "@miden-sdk/react";
+
+function App() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+Connect and disconnect by name via `useMultiSigner()`:
+
+```tsx
+import { useMultiSigner } from "@miden-sdk/react";
+
+function SignerPicker() {
+ const multi = useMultiSigner();
+ if (!multi) return null; // no MultiSignerProvider above
+
+ return (
+ <>
+ multi.connectSigner("Para")}>Connect Para
+ multi.connectSigner("Turnkey")}>Connect Turnkey
+ multi.disconnectSigner()}>Disconnect
+ >
+ );
+}
+```
+
+`useMultiSigner()` returns `MultiSignerContextValue | null`; its `connectSigner(name)` / `disconnectSigner()` actions switch and clear the active signer respectively. The name passed to `connectSigner` matches the `name` field on each signer's `SignerContextValue`.
+
+## Next
+
+- [Recipes](./recipes.md) — end-to-end patterns with signer integration examples.
+- [Setup](./setup.md) — client config and lifecycle.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/_category_.yml b/versioned_docs/version-0.14/builder/tools/clients/rust-client/_category_.yml
new file mode 100644
index 00000000..ff26d637
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/_category_.yml
@@ -0,0 +1,4 @@
+label: Rust
+# Determines where this documentation section appears relative to other sections in the parent folder
+position: 1
+collapsed: true
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/api-docs.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/api-docs.md
new file mode 100644
index 00000000..7fe20be7
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/api-docs.md
@@ -0,0 +1,8 @@
+---
+title: API
+sidebar_position: 8
+---
+
+:::note
+The latest and complete reference for the Miden client API can be found at [`Miden client docs.rs`](https://docs.rs/miden-client/latest/miden_client/).
+:::
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/_category_.yml b/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/_category_.yml
new file mode 100644
index 00000000..791dc0ca
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/_category_.yml
@@ -0,0 +1,4 @@
+label: CLI
+# Determines where this documentation section appears relative to other sections in the parent folder
+position: 3
+collapsed: true
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/cli-config.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/cli-config.md
new file mode 100644
index 00000000..129ddef0
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/cli-config.md
@@ -0,0 +1,193 @@
+---
+title: Config
+sidebar-position: 2
+---
+
+After [installation](../install-and-run.md#install-the-client), use the client by running the following and adding the [relevant commands](index.md#commands):
+
+```sh
+miden-client
+```
+
+:::tip
+Run `miden-client --help` for information on `miden` commands.
+:::
+
+## Client Configuration
+
+We configure the client using a [TOML](https://en.wikipedia.org/wiki/TOML) file ([`miden-client.toml`]). The file gets created when running `miden-client init`, which creates a `.miden` directory structure to organize all client-related files. By default, this directory is located in the HOME path, i.e. at `~/.miden`. Running this command is optional, but can be done if you want to have more fine-grained control over the configuration of the `miden-client`. The TOML file can also be edited to use a different configuration for the client.
+
+```sh
+store_filepath = ".miden/store.sqlite3"
+secret_keys_directory = ".miden/keystore"
+default_account_id = "0x012345678"
+token_symbol_map_filepath = ".miden/token_symbol_map.toml"
+remote_prover_endpoint = "http://localhost:8080"
+package_directory = ".miden/packages"
+max_block_number_delta = 256
+
+[rpc]
+endpoint = { protocol = "http", host = "localhost", port = 57291 }
+timeout_ms = 10000
+
+[note-transport] # optional
+endpoint = "http://localhost:57292"
+timeout_ms = 10000
+```
+
+### Configuration Location and Priority
+
+The client supports both **global** and **local** configuration with intelligent priority handling:
+
+1. **Global Configuration** (default): Located at `~/.miden/miden-client.toml` in your home directory. The global directory location can be overridden with the `MIDEN_CLIENT_HOME` environment variable (see [Environment variables](#environment-variables)).
+2. **Local Configuration** (project-specific): Located at `./.miden/miden-client.toml` in your current working directory
+
+**Priority Order**: Local configuration takes precedence over global configuration. If both exist, the client will use the local configuration and ignore the global one.
+
+### Initialization Options
+
+```bash
+# Create global configuration (default behavior)
+miden-client init
+
+# Create local configuration in current directory
+miden-client init --local
+```
+
+The global configuration approach reduces per-project setup overhead while still allowing project-specific customization when needed.
+
+### Configuration Management
+
+#### Clear Command
+
+The `clear` command helps manage configuration by removing existing setups:
+
+```bash
+# Remove local config if present, otherwise remove global config
+miden-client clear
+
+# Force removal of global configuration only
+miden-client clear --global
+```
+
+**Priority Behavior**: The clear command follows the same priority logic as config loading - it will remove the local configuration first if it exists, and only remove the global configuration if no local configuration is found. This ensures you don't accidentally lose both configurations at once.
+
+**Use Cases**:
+- Resetting configuration between releases when changes require clean state
+- Switching from local to global configuration (or vice versa)
+- Troubleshooting configuration-related issues
+
+### RPC
+
+An `rpc` section is used to configure the connection to the Miden node. It contains the following fields:
+
+- `endpoint`: The endpoint of the Miden node. It can be a specific url (like `"https://rpc.devnet.miden.io"`) or a table with the following fields:
+ - `protocol`: The protocol used to connect to the node. It can be either `http` or `https`.
+ - `host`: The host of the node. It can be either an IP address or a domain name.
+ - `port`: The port of the node. It is an integer.
+
+This field can be set with the `--network` flag when running the `miden-client init` command. For example, to set the testnet endpoint, you can run: `miden-client init --network testnet`.
+
+:::note
+
+- Running the node locally for development is encouraged.
+- However, the endpoint can point to any remote node.
+ :::
+
+### Store and keystore
+
+The `store_filepath` field is used to configure the path to the SQLite database file used by the client. The `secret_keys_directory` field is used to configure the path to the directory where the keystore files are stored. The default values are `.miden/store.sqlite3` and `.miden/keystore`, respectively, organizing these files within the `.miden` directory structure.
+
+The store filepath can be set when running the `miden-client init` command with the `--store-path` flag.
+
+### Default account ID
+
+The `default_account_id` field contains the default account ID to be used by the client's command when no `account` is provided. It is a hexadecimal string that represents the account ID. The field is optional, and if not set, the client will set it once the first account is created.
+
+By default none is set, but you can set and unset it with:
+
+```sh
+miden-client account --default #Sets default account
+miden-client account --default none #Unsets default account
+```
+
+:::note
+The account must be tracked by the client in order to be set as the default account.
+:::
+
+You can also see the current default account ID with:
+
+```sh
+miden-client account --default
+```
+
+### Token symbol map
+
+The `token_symbol_map_filepath` field is used to configure the path to the TOML file that contains the token symbol map. The token symbol map stores the faucet details for different token symbols. The default value is `.miden/token_symbol_map.toml`.
+
+This file must be updated manually with known token symbol mappings. A sample token symbol map file looks like this:
+
+```toml
+# This addresses in this file are not real and are only for demonstration purposes.
+ETH = { id = "0xa031cc137adecd54", decimals = 18 }
+BTC = { id = "0x2f3c4b5e6a7b8c9d", decimals = 8 }
+```
+
+The `id` field is the faucet account ID and the `decimals` field is the number of decimals used by the token.
+
+When the client is configured with a token symbol map, any transaction command that specifies an asset can use the token symbol instead of the asset ID. For example, when specifying an asset normally you would use something like:
+`1::0x2f3c4b5e6a7b8c9d`
+
+But if the faucet is included in the token symbol map (using the sample above as the mapping), you would use:
+`0.00000001::BTC`
+
+Notice how the amount specified when using the token symbol takes into account the decimals of the token (`1` base unit of the token is `0.00000001` for BTC as it uses 8 decimals).
+
+### Remote prover endpoint
+
+The `remote_prover_endpoint` field is used to configure the usage of a remote prover. You can set a remote prover when calling the `miden-client prover` command with the `--remote-prover-endpoint` flag. The prover will be used for all transactions that are executed with the `miden` command. By default, no remote prover is used and all transactions are executed locally.
+
+### Package directory
+`Packages` are Miden's native packaging format.
+This structure contains the outputs of a compiled project, with all of its corresponding metadata. Specifically, a `Package` may contain the compiled MAST for an `Account Component` in the form of a `Library`.
+
+The `package_directory` field is used to configure the path to the directory where the account components are stored in package (`.masp`) form. The default value is `.miden/packages`.
+
+In this directory you can place the packages used to create the account components. These define the interface of the account that will be created.
+
+For more information on miden packages, see:
+- [The mast-package crate](https://github.com/0xMiden/miden-vm/blob/next/crates/mast-package/README.md)
+- [The Miden package's status article on the Miden compiler](https://0xmiden.github.io/compiler/appendix/known-limitations.html#packaging)
+
+### Block Delta
+
+The `max_block_number_delta` is an optional field that is used to configure the maximum number of blocks the client can be behind the network.
+
+If not set, the default behavior is to ignore the block difference between the client and the network. If set, the client will check this difference is within the specified maximum when validating a transaction.
+
+```sh
+miden-client init --block-delta 256
+```
+
+### Environment variables
+
+- `MIDEN_CLIENT_HOME`: Overrides the default global `.miden` directory (`~/.miden`). When set, all commands that reference the global directory will use the specified path instead. This is useful for keeping separate environments or storing the client data in a non-default location. For example:
+
+ ```sh
+ export MIDEN_CLIENT_HOME=/path/to/custom/miden
+ miden-client init
+ ```
+
+ Note that this only affects the **global** directory. If a local `./.miden` directory exists, it still takes precedence over the global one (whether default or overridden).
+
+- `MIDEN_DEBUG`: When set to `true`, enables debug mode on the transaction executor and the script compiler. For any script that has been compiled and executed in this mode, debug logs will be output in order to facilitate MASM debugging ([these instructions](https://0xMiden.github.io/miden-vm/user_docs/assembly/debugging.html) can be used to do so). This variable can be overridden by the `--debug` CLI flag.
+
+### Note Transport
+
+A `note-transport` section is used to configure the connection to the Miden Note Transport node used in the exchange of private notes. It contains the following fields:
+- `endpoint`: The endpoint of the Miden Note Transport node;
+- `timeout-ms`: The timeout employed in client requests to the node.
+
+> [!Note]
+> - Running the node locally for development is encouraged.
+> - However, the endpoint can point to any remote node.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/cli-troubleshooting.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/cli-troubleshooting.md
new file mode 100644
index 00000000..9cfd9b0e
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/cli-troubleshooting.md
@@ -0,0 +1,147 @@
+---
+title: Troubleshooting
+sidebar-position: 3
+---
+
+## Troubleshooting and transaction lifecycle (CLI)
+
+This guide helps you troubleshoot common issues and understand the end-to-end lifecycle of transactions and notes in the Miden client.
+
+### TL;DR checklist
+
+> Note: This section applies to the Miden CLI client. Guidance for the Rust and Web clients may differ.
+
+- Ensure you have a proper configuration setup: either a global config at `~/.miden/miden-client.toml` or a local config at `./.miden/miden-client.toml`. Local config takes priority if both exist.
+- If you need a clean local state, delete the SQLite store file referenced by `store_filepath` (default: `.miden/store.sqlite3`). It will be recreated automatically on the next command.
+- Verify your node RPC endpoint is reachable and correct in your configuration file (local `.miden/miden-client.toml` or global `~/.miden/miden-client.toml`).
+- Run with debug output when troubleshooting: add `--debug` or set `MIDEN_DEBUG=true`.
+- Run `miden-client sync` to refresh local state after errors involving missing data or outdated heights.
+
+### Enable debug output
+
+- CLI flag: `miden-client --debug ...` (overrides `MIDEN_DEBUG`)
+- Environment variable: `MIDEN_DEBUG=true`
+
+When enabled, the transaction executor and script compiler emit debug logs that help diagnose MASM-level issues (you can also consult the Miden VM debugging instructions).
+
+### Typical CLI outputs when debugging
+
+```sh
+# Enable debug output for a command
+miden-client --debug send --sender --target --asset 100::
+
+# Force non-interactive submission (e.g., CI)
+miden-client send --force ...
+
+# Refresh local state
+miden-client sync
+```
+
+If you see a gRPC error, it may include a status-derived kind (e.g. `Unavailable`, `InvalidArgument`) which narrows possible causes.
+
+### Common errors and how to resolve
+
+Below are representative errors you may encounter, their likely causes, and suggested fixes.
+
+#### `RpcError.GrpcError: Unavailable` / `DeadlineExceeded`
+- Cause: Node is down, unreachable, or behind a load balancer that blocked the request.
+- Fix: Check `rpc.endpoint` in your configuration file (local `.miden/miden-client.toml` or global `~/.miden/miden-client.toml`), verify the node is running/accessible, and retry.
+
+#### `RpcError.InvalidArgument` / `ExpectedDataMissing` / `InvalidResponse`
+- Cause: Malformed request parameters or unexpected server response.
+- Fix: Re-check command flags/inputs. If using partial IDs, ensure they map to a single entity. Update to the latest client if the server API has changed.
+
+#### Client/network compatibility mismatch
+- Cause: Client and network versions or the genesis header commitment are incompatible.
+- Symptoms: CLI may report messages like:
+
+ ```
+ accept header validation failed: server rejected request - please check your version and network settings
+ ```
+
+ or requests being rejected due to a mismatched genesis header commitment.
+- Details: These are validated by the node by verifying client headers on gRPC requests.
+- Fix: Ensure your client version matches the target network. Switch to the correct network or upgrade/downgrade the client accordingly. Verify the configured genesis header commitment matches the network, then retry.
+
+#### `ClientError.AccountDataNotFound()`
+- Cause: The account is not known to the local store yet.
+- Fix: Create/import the account first, or run `miden-client sync` to fetch it if it exists on-chain.
+
+#### `ClientError.AccountLocked()`
+- Cause: Attempting to modify a locked account.
+- Fix: Unlock or use another account as appropriate.
+
+#### `ClientError.StoreError(AccountCommitmentAlreadyExists(...))`
+- Cause: Trying to apply a transaction whose final account commitment is already present locally.
+- Fix: Ensure you are not re-applying the same transaction. Sync and check transaction status.
+
+#### `ClientError.NoteNotFoundOnChain()` / `RpcError.NoteNotFound()`
+- Cause: The note has not been published/committed yet or the ID is incorrect.
+- Fix: Verify the note ID. If it should exist, run `miden-client sync` and retry.
+
+#### `ClientError.TransactionInputError` / `TransactionScriptError`
+- Cause: Invalid transaction inputs, script logic errors, or failing constraints.
+- Fix: Run with `--debug` to collect execution logs. Validate input notes, foreign accounts, and script assumptions.
+
+#### `ClientError.TransactionProvingError`
+- Cause: Local proving failed or remote prover returned an error.
+- Fix: If using remote proving, verify `remote_prover_endpoint` is reachable and add `--delegate-proving`. Check prover logs.
+
+#### Recency/block delta errors
+- Cause: Client is too far behind the network and validation enforces a max delta.
+- Fix: Run `miden-client sync` or increase `max_block_number_delta` via `miden-client init --block-delta ` and re-run.
+
+### Transaction lifecycle (CLI-oriented overview)
+
+For the full protocol-level lifecycle, see the Miden book: [Transaction lifecycle](https://0xmiden.github.io/miden-docs/imported/miden-base/src/transaction.html#transaction-lifecycle).
+
+```mermaid
+flowchart LR
+ A[Build Request] --> B[Validate Request]
+ A -.->|optional| C[Collect/Insert Input Notes]
+ A -.->|optional| D[Load Foreign Accounts]
+ B -.->|optional| K[Insert Public Note Recipients]
+ B --> E[Execute Transaction]
+ E --> F[Prove Transaction]
+ F --> G[Submit to Node]
+ G --> H[Track Locally]
+
+ subgraph Tracking
+ H --> I[Update Account State]
+ H --> J[Update Notes/Tags]
+ end
+```
+
+Key states the CLI surfaces:
+
+- Transaction status: `Pending` (after execution), `Committed` (after node inclusion), `Discarded` (not included).
+- Input notes: `Expected` → `Processing` → `Consumed` (after sync) or `Committed` if fetched with inclusion.
+
+### Configuration troubleshooting
+
+#### Config priority confusion
+- **Issue**: Unclear which configuration is being used (local vs global)
+- **Check**: Run commands from different directories to see if behavior changes
+- **Local priority**: If `./.miden/miden-client.toml` exists, it overrides `~/.miden/miden-client.toml`
+- **Fix**: Use `miden-client clear-config` to remove unwanted configurations, or `miden-client clear-config --global` to remove only global config. Note: Running `miden-client clear-config` without flags follows priority: if a local .miden folder exists, it removes only that one; if no local folder exists, it removes the global one. Use `--global` to specifically target the global configuration regardless of local config presence.
+
+#### Clean configuration reset
+- **Complete reset**: Use `miden-client clear-config` to remove the active configuration (follows priority: local first, then global)
+- **Selective reset**: Use `miden-client clear-config --global` to remove only global configuration while preserving local
+- **Fresh start**: After clearing, run `miden-client init` (global) or `miden-client init --local` (local) to recreate
+
+### Recovery flow
+
+1. Re-run with `--debug` or `MIDEN_DEBUG=true` for richer logs.
+2. Verify `rpc.endpoint` connectivity and timeouts.
+3. Run `miden-client sync` to refresh local headers/notes.
+4. If local DB is inconsistent for development purposes, delete the store file (`.miden/store.sqlite3` in local config or `~/.miden/store.sqlite3` in global config) and retry.
+5. For configuration issues, use `miden-client clear-config` to reset config and `miden-client init` to recreate.
+6. Adjust `max_block_number_delta` if strict recency checks block validation.
+7. If proving errors persist with a remote prover, confirm `remote_prover_endpoint` and consider running locally to isolate the issue.
+
+### References
+
+- CLI debug flag and environment variable are documented in `CLI` and `Config` docs.
+- Common error enums originate from the client and RPC layers.
+- Protocol lifecycle: [Miden book — Transaction lifecycle](https://0xmiden.github.io/miden-docs/imported/miden-base/src/transaction.html#transaction-lifecycle)
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/index.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/index.md
new file mode 100644
index 00000000..d9a0bbf0
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/cli/index.md
@@ -0,0 +1,410 @@
+---
+title: CLI
+---
+
+The following document lists the commands that the CLI currently supports.
+
+:::tip
+Use `--help` as a flag on any command for more information.
+:::
+
+## Usage
+
+Call a command on the `miden-client` like this:
+
+```sh
+miden-client
+```
+
+Optionally, you can include the `--debug` flag to run the command with debug mode, which enables debug output logs from scripts that were compiled in this mode:
+
+```sh
+miden-client --debug
+```
+
+Note that the debug flag overrides the `MIDEN_DEBUG` environment variable.
+
+## Commands
+
+### `init`
+
+Creates a configuration file for the client in the current directory. Running this command is optional, as the client will self-initialize by default. By default, the command uses the Testnet network.
+
+```sh
+# This will create a config file named `miden-client.toml` using default values
+# This file contains information useful for the CLI like the RPC provider and database path
+miden-client init
+
+# You can set up the CLI for any of the default networks
+miden-client init --network testnet
+miden-client init --network devnet
+miden-client init --network localhost
+
+# You can also specify a custom network
+miden-client init --network 18.203.155.106
+# You can specify the port
+miden-client init --network 18.203.155.106:8080
+# You can also specify the protocol (http/https)
+miden-client init --network https://18.203.155.106
+# You can specify both
+miden-client init --network https://18.203.155.106:1234
+
+# You can use the --store-path flag to override the default store config
+miden-client init --store-path db/store.sqlite3
+
+# You can use the --block-delta flag to set maximum number of blocks the client can be behind
+miden-client init --block-delta 250
+
+# You can provide both flags
+miden-client init --network 18.203.155.106 --store-path db/store.sqlite3
+
+# You can set a remote prover to offload the proving process (along with the `--delegate-proving` flag in transaction commands)
+miden-client init --remote-prover-endpoint
+
+# To enable the transport layer, specify the endpoint
+miden-client init --note-transport-endpoint
+```
+
+More information on the configuration file can be found in the [configuration section](https://github.com/0xMiden/miden-client/docs/typedoc/rust-client/cli-config.md).
+
+### `account`
+
+Inspect account details.
+
+#### Action Flags
+
+| Flags | Description | Short Flag |
+| ---------------- | ------------------------------------------------ | ---------- |
+| `--list` | List all accounts monitored by this client | `-l` |
+| `--show ` | Show details of the account for the specified ID | `-s` |
+| `--default ` | Manage the setting for the default account | `-d` |
+
+The `--show` flag also accepts a partial ID instead of the full ID. For example, instead of:
+
+```sh
+miden-client account --show 0x8fd4b86a6387f8d8
+```
+
+You can call:
+
+```sh
+miden-client account --show 0x8fd4b86
+```
+
+For the `--default` flag, if `` is "none" then the previous default account is cleared. If no `` is specified then the default account is shown.
+
+### `new-wallet`
+
+Creates a new wallet account.
+
+A basic wallet is comprised of a basic authentication component (for RPO Falcon signature verification), alongside a basic wallet component (for sending and receiving assets).
+
+This command has three optional flags:
+
+- `--storage-mode `: Used to select the storage mode of the account (private if not specified). It may receive "private" or "public".
+- `--mutable`: Makes the account code mutable (it's immutable by default).
+- `--extra-packages `: Specifies a list of file paths for packages holding account components to include in the account. If the packages contain placeholders, the CLI will prompt the user to enter the required data for instantiating storage appropriately.
+- `--init-storage-data-path `: Specifies an optional file path to a TOML file containing key/value pairs used for initializing storage. Each key should map to a placeholder within the packages' component metadata. The CLI will prompt for any keys that are not present in the file.
+
+After creating an account with the `new-wallet` command, it is automatically stored and tracked by the client. This means the client can execute transactions that modify the state of accounts and track related changes by synchronizing with the Miden network.
+
+### `new-account`
+
+Creates a new account and saves it locally.
+
+An account may be composed of one or more components, each with its own storage and distinct functionality. This command lets you build a custom account by selecting an account type and optionally adding extra component packages.
+
+This command has four flags:
+
+- `--storage-mode `: Specifies the storage mode of the account. It accepts either "private" or "public", with "private" as the default.
+- `--account-type `: Specifies the type of account to create. Accepted values are:
+ - `fungible-faucet`
+ - `non-fungible-faucet`
+ - `regular-account-immutable-code`
+ - `regular-account-updatable-code`
+- `--packages `: Specifies a list of file paths for packages holding account components to include in the account. If the packages contain placeholders, the CLI will prompt the user to enter the required data for instantiating storage appropriately.
+- `--init-storage-data-path `: Specifies an optional file path to a TOML file containing key/value pairs used for initializing storage. Each key should map to a placeholder within the packages' component metadata. The CLI will prompt for any keys that are not present in the file.
+
+After creating an account with the `new-account` command, the account is stored locally and tracked by the client, enabling it to execute transactions and synchronize state changes with the Miden network.
+
+#### Examples
+
+```bash
+# Create a new wallet with default settings (private storage, immutable, no extra components)
+miden-client new-wallet
+
+# Create a new wallet with public storage and a mutable code
+miden-client new-wallet --storage-mode public --mutable
+
+# Create a new wallet that includes custom packages
+miden-client new-wallet --extra-packages packages/custom-package.masp
+
+# Create a fungible faucet with interactive input
+miden-client new-account --account-type fungible-faucet --packages packages/basic-fungible-faucet.masp
+
+# Create a fungible faucet with preset fields
+miden-client new-account --account-type fungible-faucet --packages packages/basic-fungible-faucet.masp --init-storage-data-path init_data.toml
+```
+
+where `init_data.toml` is a TOML file with the following example content:
+```toml
+token_metadata.max_supply = 1000000000
+token_metadata.decimals = 6
+token_metadata.ticker = "TEST"
+```
+
+### `info`
+
+View a summary of the current client state.
+
+#### Action Flags
+
+| Flag | Description | Short Flag |
+| -------------- | -------------------------------------------- | ---------- |
+| `--rpc-status` | Display detailed RPC node status information | `-r` |
+
+When using the `--rpc-status` flag, the command displays additional information about the RPC node including:
+
+- Node version
+- Genesis commitment
+- Store connection status and chain tip
+- Block producer status and chain tip
+
+### `notes`
+
+View and manage notes. Also, exchange private notes using the note transport network.
+
+#### Action Flags
+
+| Flags | Description | Short Flag |
+| ----------------------- | -------------------------------------------------------- | ---------- |
+| `--list []` | List input notes | `-l` |
+| `--show ` | Show details of the input note for the specified note ID | `-s` |
+| `--send ` | Send a note using the note transport network | |
+| `--fetch` | Fetch notes from the note transport network | |
+
+The `--list` flag receives an optional filter: - expected: Only lists expected notes. - committed: Only lists committed notes. - consumed: Only lists consumed notes. - processing: Only lists processing notes. - consumable: Only lists consumable notes. An additional `--account-id ` flag may be added to only show notes consumable by the specified account.
+If no filter is specified then all notes are listed.
+
+The `--show` flag also accepts a partial ID instead of the full ID. For example, instead of:
+
+```sh
+miden-client notes --show 0x70b7ecba1db44c3aa75e87a3394de95463cc094d7794b706e02a9228342faeb0
+```
+
+You can call:
+
+```sh
+miden-client notes --show 0x70b7ec
+```
+
+To send a private note, the `--send` flag sends a note using the note transport network.
+The note ID (hex, in full or a prefix) and recipient's address (bech32) must be provided.
+The note is assumed to be stored in the store (e.g., imported using [`import`](#import)).
+
+You can call:
+
+```sh
+miden-client notes --send 0xc1234567 mm1qpkdyek2c0ywwvzupakc7zlzty8qn2qnfc
+```
+
+To fetch private notes, the `--fetch` allows to download notes from the note transport network.
+Only notes for tracked tags will be fetched (e.g. `miden-client tags --list`).
+The downloaded notes will be added to the store.
+
+```sh
+miden-client notes --fetch
+```
+
+### `sync`
+
+Sync the client with the latest state of the Miden network. Shows a brief summary at the end.
+
+### `tags`
+
+View and add tags.
+
+#### Action Flags
+
+| Flag | Description | Aliases |
+| ---------------- | ----------------------------------------------------------- | ------- |
+| `--list` | List all tags monitored by this client | `-l` |
+| `--add ` | Add a new tag to the list of tags monitored by this client | `-a` |
+| `--remove ` | Remove a tag from the list of tags monitored by this client | `-r` |
+
+### `tx`
+
+View transactions.
+
+#### Action Flags
+
+| Command | Description | Aliases |
+| -------- | ------------------------- | ------- |
+| `--list` | List tracked transactions | -l |
+
+After a transaction gets executed, two entities start being tracked:
+
+- The transaction itself: It follows a lifecycle from `Pending` (initial state) and `Committed` (after the node receives it). It may also be `Discarded` if the transaction was not included in a block.
+- Output notes that might have been created as part of the transaction (for example, when executing a pay-to-id transaction).
+
+### Transaction creation commands
+
+#### `mint`
+
+Creates a note that contains a specific amount tokens minted by a faucet, that the target Account ID can consume.
+
+Usage: `miden-client mint --target --asset :: --note-type `
+
+#### `consume-notes`
+
+Account ID consumes a list of notes, specified by their Note ID.
+
+Usage: `miden-client consume-notes --account [NOTES]`
+
+For this command, you can also provide a partial ID instead of the full ID for each note. So instead of
+
+```sh
+miden-client consume-notes --account 0x70b7ecba1db44c3aa75e87a3394de95463cc094d7794b706e02a9228342faeb0 0x80b7ecba1db44c3aa75e87a3394de95463cc094d7794b706e02a9228342faeb0
+```
+
+You can do:
+
+```sh
+miden-client consume-notes --account 0x70b7ecb 0x80b7ecb
+```
+
+Additionally, you can optionally not specify note IDs, in which case any note that is known to be consumable by the executor account ID will be consumed.
+
+Either `Expected` or `Committed` notes may be consumed by this command, changing their state to `Processing`. It's state will be updated to `Consumed` after the next sync.
+
+#### `send`
+
+Sends assets to another account. Sender Account creates a note that a target Account ID can consume. The asset is identified by the tuple `(FAUCET ID, AMOUNT)`. The note can be configured to be recallable making the sender able to consume it after a height is reached.
+
+Usage: `miden-client send --sender --target --asset :: --note-type `
+
+#### `swap`
+
+The source account creates a `SWAP` note that offers some asset in exchange for some other asset. When another account consumes that note, it will receive the offered asset amount and the requested asset will removed from its vault (and put into a new note which the first account can then consume). Consuming the note will fail if the account doesn't have enough of the requested asset.
+
+Usage: `miden-client swap --source --offered-asset :: --requested-asset :: --note-type `
+
+### `address`
+
+View and manage addresses.
+
+#### Action Subcommands
+
+| Subcommand | Description |
+| -------------------------------- | ---------------------------------------------------------------------------------------|
+| `list ` | List all addresses or only for the specified account ID (default command) |
+| `add ` | Bind an address for an interface for the specified account ID with optional tag length |
+| `remove ` | Remove an address for the specified account ID |
+
+The `list` subcommand optionally takes an account ID to only show the addresses of that account, if it is not provided, it will show all addresses of all accounts.
+
+```sh
+miden-client address list 0x17f13f4f83a8e8100c19d2961dfda2
+```
+
+`add` and `remove` take the account ID as a mandatory argument, and also the interface of the address, this values can be:
+- `BasicWallet`: The basic wallet interface.
+
+Note: the `Unspecified` denotes an address not bound to any interface, it's the default address for every account created.
+
+```sh
+miden-client address add 0x17f13f4f83a8e8100c19d2961dfda2 BasicWallet 10
+```
+
+```sh
+miden-client address remove 0x17f13f4f83a8e8100c19d2961dfda2 mlcl1qple0ejnutx8zyp0cm0pme9wjfgqz0u9djq
+```
+
+#### Tips
+
+For `send` and `consume-notes`, you can omit the `--sender` and `--account` flags to use the default account defined in the [config](https://github.com/0xMiden/miden-client/docs/typedoc/rust-client/cli-config.md). If you omit the flag but have no default account defined in the config, you'll get an error instead.
+
+For every command which needs an account ID (either wallet or faucet), you can also provide a partial ID instead of the full ID for each account. So instead of
+
+```sh
+miden-client send --sender 0x80519a1c5e3680fc --target 0x8fd4b86a6387f8d8 --asset 100::0xa99c5c8764d4e011
+```
+
+You can do:
+
+```sh
+miden-client send --sender 0x80519 --target 0x8fd4b --asset 100::0xa99c5c8764d4e011
+```
+
+!!! note
+The only exception is for using IDs as part of the asset, those should have the full faucet's account ID.
+
+#### Transaction confirmation
+
+When creating a new transaction, a summary of the transaction updates will be shown and confirmation for those updates will be prompted:
+
+```sh
+miden-client ...
+
+TX Summary:
+
+...
+
+Continue with proving and submission? Changes will be irreversible once the proof is finalized on the network (y/N)
+```
+
+This confirmation can be skipped in non-interactive environments by providing the `--force` flag (`miden-client send --force ...`).
+
+#### Delegated proving
+
+If a remote prover is configured, the CLI can offload the proving process to it. This is done by providing the `--delegate-proving` flag when creating a transaction. The CLI will then send the transaction to the remote prover for processing.
+
+### Importing and exporting
+
+#### `export`
+
+Export input note data to a binary file .
+
+| Flag | Description | Aliases |
+| ----------------------------- | ------------------------------------- | ------- |
+| `--filename ` | Desired filename for the binary file. | `-f` |
+| `--export-type ` | Exported note type. | `-e` |
+
+##### Export type
+
+The user needs to specify how the note should be exported via the `--export-type` flag. The following options are available:
+
+- `id`: Only the note ID is exported. When importing, if the note ID is already tracked by the client, the note will be updated with missing information fetched from the node. This works for both public and private notes. If the note isn't tracked and the note is public, the whole note is fetched from the node and is stored for later use.
+- `full`: The note is exported with all of its information (metadata and inclusion proof). When importing, the note is considered unverified. The note may not be consumed directly after importing as its block header will not be stored in the client. The block header will be fetched and be used to verify the note during the next sync. At this point the note will be committed and may be consumed.
+- `partial`: The note is exported with minimal information and may be imported even if the note is not yet committed on chain. At the moment of importing the note, the client will check the state of the note by doing a note sync, using the note's tag. Depending on the response, the note will be either stored as "Expected" or "Committed".
+
+#### `import`
+
+Import entities managed by the client, such as accounts and notes. The type of entities is inferred.
+
+The `--overwrite` flag can be used when importing accounts. It allows the user to overwrite existing accounts with the same ID. This is useful when you want to update the account's information or replace it with a new version.
+
+### Executing scripts
+
+#### `exec`
+
+Execute the specified program against the specified account.
+
+| Flag | Description | Aliases |
+| ----------------------------- | -------------------------------------------- | ------- |
+| `--account ` | Account ID to use for the program execution. | `-a` |
+| `--script-path ` | Path to script's source code to be executed. | `-s` |
+| `--inputs-path ` | Path to the inputs file. | `-i` |
+| `--hex-words` | Print the output stack grouped into words. | |
+
+The file referenced by `--inputs-path` should contain a TOML array of inline tables, where each table has two fields: - `key`: a 256-bit hexadecimal string representing a word to be used as a key for the input entry. The hexadecimal value must be prefixed with 0x. - `values`: an array of 64-bit unsigned integers representing field elements to be used as values for the input entry. Each integer must be written as a separate string, within double quotes.
+
+The input file should contain a TOML table called `inputs`, as in the following example:
+
+```toml
+inputs = [ { key = "0x0000000000000000000000000000000000000000000000000000001000000000", values = ["13", "9"]}, { key = "0x0000000000000000000000000000000000000000000000000000000000000000" , values = ["1", "2"]}, ]
+```
+
+### `note-transport`
+
+Send and fetch private notes using the transport layer.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/debugging.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/debugging.md
new file mode 100644
index 00000000..d8a1f411
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/debugging.md
@@ -0,0 +1,96 @@
+---
+title: DAP Debugging
+sidebar_position: 7
+---
+
+# DAP Debugging
+
+The Miden client supports interactive debugging via the [Debug Adapter Protocol (DAP)](https://microsoft.github.io/debug-adapter-protocol/). You can debug both raw Miden Assembly scripts and Rust programs compiled to Miden via `midenc`. This lets you step through execution, set breakpoints, and inspect stack/memory state using any DAP-compatible client (e.g. VS Code, the `miden-debug` TUI).
+
+## Feature Flags
+
+Two feature flags control debugging support:
+
+| Feature | Crate | What it enables |
+|---------|-------|-----------------|
+| `dap` | `miden-client`, `miden-client-cli` | Compiles in DAP support (`execute_program_with_dap`, `--start-debug-adapter` CLI flag). **Enabled by default.** |
+| `testing` | `miden-client-cli` | Enables the `--offline` flag on `new-wallet`/`new-account` commands for node-less account creation. Not available in production builds. |
+
+### Building with features
+
+```bash
+# Default build (DAP enabled)
+cargo build -p miden-client-cli
+
+# With offline mode for testing
+cargo build -p miden-client-cli --features testing
+
+# Without DAP (smaller binary)
+cargo build -p miden-client-cli --no-default-features
+```
+
+If you build from source with default features disabled, include the `dap` feature to use
+`--start-debug-adapter`.
+
+## Quick Start
+
+### 1. Create an account
+
+With a running node:
+
+```bash
+miden-client init
+miden-client new-wallet
+miden-client sync
+```
+
+Or without a node (requires `testing` feature):
+
+```bash
+miden-client init
+miden-client new-wallet --offline
+```
+
+### 2. Write a test script
+
+Create a file `test_debug.masm`:
+
+```
+begin
+ push.1.2
+ add
+ push.3
+ mul
+end
+```
+
+### 3. Start the DAP server
+
+```bash
+miden-client exec \
+ --script-path test_debug.masm \
+ --start-debug-adapter 127.0.0.1:4711
+```
+
+The client will compile the script and wait for a DAP client to connect before executing.
+
+### 4. Connect a debugger
+
+In a separate terminal, connect the `miden-debug` TUI:
+
+```bash
+miden-debug --dap-connect 127.0.0.1:4711
+```
+
+You can now step through execution, inspect the stack, and set breakpoints.
+
+
+## How it Works
+
+When `--start-debug-adapter` is passed:
+
+1. The client compiles the transaction script normally.
+2. Instead of using the default `FastProcessor`, it creates a `DapExecutor` (from the `miden-debug` crate) which implements the `ProgramExecutor` trait.
+3. The `DapExecutor` binds a TCP listener on the specified address and waits for a DAP client connection.
+4. Once connected, the DAP client controls execution (continue, step, breakpoints, inspect state).
+5. If the DAP client requests a restart, the client recompiles the script from disk and re-executes — enabling an edit-and-continue workflow.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/design.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/design.md
new file mode 100644
index 00000000..a9c783b9
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/design.md
@@ -0,0 +1,80 @@
+---
+title: Design
+sidebar_position: 4
+---
+
+The Miden client has the following architectural components:
+
+- [Store](#store)
+- [RPC client](#rpc-client)
+- [Transaction executor](#transaction-executor)
+- [Keystore](#keystore)
+- [Note screener](#note-screener)
+- [Note transport](#note-transport)
+
+:::tip
+
+- The RPC client and the store are Rust traits.
+- This allow developers and users to easily customize their implementations.
+
+:::
+
+## Store
+
+The store is central to the client's design.
+
+It manages the persistence of the following entities:
+
+- Accounts; including their state history and related information such as vault assets and account code.
+- Transactions and their scripts.
+- Notes.
+- Note tags.
+- Block headers and chain information that the client needs to execute transactions and consume notes.
+
+Because Miden allows off-chain executing and proving, the client needs to know about the state of the blockchain at the moment of execution. To avoid state bloat, however, the client does not need to see the whole blockchain history, just the chain history intervals that are relevant to the user.
+
+The store can track any number of accounts, and any number of notes that those accounts might have created or may want to consume.
+
+## RPC client
+
+The RPC client communicates with the node through a defined set of gRPC methods. The provided client works both in `std` and `wasm` environments.
+
+The available gRPC methods are documented in the [Node gRPC Reference](https://docs.miden.xyz/miden-node/rpc).
+
+## Transaction executor
+
+The transaction executor uses the [Miden VM](https://0xmiden.github.io/miden-docs/imported/miden-vm/src/intro/main.html) to execute transactions. All transactions run within the [transaction kernel](https://0xmiden.github.io/miden-docs/imported/miden-base/src/transaction.html).
+
+When executing, the executor needs access to relevant blockchain history. The executor uses a `DataStore` interface for accessing this data. This means that there may be some coupling between the executor and the store.
+
+## Keystore
+
+The keystore is responsible for storing and managing the private keys of the accounts tracked by the client.
+
+These private keys are used by the executor to sign and authenticate transactions. Implementations for both rust and web keystores are provided.
+
+## Note Screener
+
+The note screener is used to check the consumability of notes by tracked accounts. It performs fast static checks (e.g. checking the inputs for well known notes) and also dry runs of consumption transactions.
+
+It can find the tracked accounts that can consume a note, and whether the note can be consumed at the moment or in the future.
+
+## State Sync component
+
+The state sync component encapsulates the logic for dealing with synchronization of the client state with the network. It repeatedly queries the node with sync state requests until the chain tip is reached. On every requests it updates the provided tracked elements (accounts, notes, transactions, etc.) and returns an updated state at the end which can be used to update the store (this component does not modify the store directly).
+
+The component also exposes a specific customizable callback which can be used to react to new note arrivals.
+
+## Note transport
+
+Access to the note transport network to exchange private notes is also provided.
+The provided client uses gRPC methods to communicate with the note transport network, working both in `std` and `wasm` environments.
+
+Targeting privacy, notes are primarily exchanged using their tags as identifiers. By default, when notes are created the tag is derived from the recipient account ID, however the tag can also be random.
+
+The system is also prepared for end-to-end encryption (to be implemented).
+
+gRPC methods include:
+
+- `SendNote`: Sends a note to the note transport network. The recipient address is employed to encrypt the outgoing note (to be implemented).
+- `FetchNotes`: Fetch notes from the network by note tag. A pagination mechanism using a monotonic-increasing cursor is also employed. The cursor is created by the network and used by the client to reduce the number of fetched notes (to avoid downloading already fetched notes).
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/examples.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/examples.md
new file mode 100644
index 00000000..7118159a
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/examples.md
@@ -0,0 +1,52 @@
+---
+title: Examples
+sidebar_position: 7
+---
+
+:::note
+For a complete example on how to run the client and submit transactions to the Miden node, refer to the [`Getting started documentation`](https://0xmiden.github.io/miden-docs/imported/miden-client/src/get-started/prerequisites.html#prerequisites).
+:::
+
+## Prover Fallback Pattern
+
+When using a remote prover, network issues or server errors may cause proving to fail. A common pattern is to configure the client with a remote prover by default and fall back to local proving when remote proving fails.
+
+```rust
+use std::sync::Arc;
+use miden_client::{
+ ClientError,
+ RemoteTransactionProver,
+ builder::ClientBuilder,
+ transaction::{LocalTransactionProver, ProvingOptions},
+};
+
+// Create provers
+let remote_prover = Arc::new(RemoteTransactionProver::new("https://prover.example.com"));
+let local_prover = Arc::new(LocalTransactionProver::new(ProvingOptions::default()));
+
+// Build client with remote prover as default
+let mut client = ClientBuilder::new()
+ .prover(remote_prover.clone())
+ .store(store)
+ .rpc(rpc)
+ .authenticator(authenticator)
+ .build()
+ .await?;
+
+// Build transaction request
+let tx_request = /* ... build your transaction request ... */;
+
+// Submit with fallback: try remote prover first, fall back to local on proving error
+let tx_id = match client.submit_new_transaction(account_id, tx_request.clone()).await {
+ Ok(id) => id,
+ Err(ClientError::TransactionProvingError(_)) => {
+ println!("Remote proving failed, falling back to local prover...");
+ client
+ .submit_new_transaction_with_prover(account_id, tx_request, local_prover.clone())
+ .await?
+ }
+ Err(e) => return Err(e.into()),
+};
+
+println!("Transaction submitted: {}", tx_id);
+```
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/features.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/features.md
new file mode 100644
index 00000000..474e0455
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/features.md
@@ -0,0 +1,26 @@
+---
+title: Features
+sidebar_position: 3
+---
+
+The Miden client offers a range of functionality for interacting with the Miden rollup.
+
+### Transaction execution
+
+The Miden client facilitates the execution of transactions on the Miden rollup; allowing users to transfer assets, mint new tokens, and perform various other operations.
+
+### Proof generation
+
+The Miden rollup supports user-generated proofs which are key to ensuring the validity of transactions on the Miden rollup.
+
+To enable such proofs, the client contains the functionality for executing, proving, and submitting transactions.
+
+### Miden network interactivity
+
+The Miden client enables users to interact with the Miden network. This includes syncing with the latest blockchain data and managing account information.
+
+__Note transport__ The client also supports connectivity with the Miden Note Transport network for the exchange of private notes (end-to-end encryption coming soon).
+
+### Account generation and tracking
+
+The Miden client provides features for generating and tracking accounts within the Miden rollup ecosystem. Users can create accounts and track their transaction status.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/_category_.yml b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/_category_.yml
new file mode 100644
index 00000000..b280e681
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/_category_.yml
@@ -0,0 +1,4 @@
+label: Getting Started
+# Determines where this documentation section appears relative to other sections in the parent folder
+position: 3
+collapsed: true
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/create-account-use-faucet.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/create-account-use-faucet.md
new file mode 100644
index 00000000..95e352e1
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/create-account-use-faucet.md
@@ -0,0 +1,202 @@
+---
+title: Create account
+sidebar_position: 1
+---
+
+In this section, we show you how to create a new local Miden account and how to receive funds from the public Miden faucet website.
+
+## Configure the Miden client
+
+The Miden client facilitates interaction with the Miden rollup and provides a way to execute and prove transactions.
+
+:::tip
+Check the [Miden client documentation](https://0xMiden.github.io/miden-docs/miden-client/cli-reference.html) for more information.
+:::
+
+1. If you haven't already done so as part of another tutorial, open your terminal and create a new directory to store the Miden client.
+
+ ```sh
+ mkdir miden-client
+ cd miden-client
+ ```
+
+2. Install the Miden client.
+
+ ```sh
+ cargo install miden-client-cli --locked
+ ```
+
+ You can now use the `miden-client --version` command, and you should see `Miden 0.10.0`.
+
+## Create a new Miden account
+
+1. Create a new account of type `mutable` using the following command:
+
+ ```sh
+ miden-client new-wallet --mutable
+ ```
+
+2. List all created accounts by running the following command:
+
+ ```sh
+ miden-client account -l
+ ```
+
+ You should see something like this:
+
+
+
+Save the account ID for a future step.
+
+## Request tokens from the public faucet
+
+1. To request funds from the faucet navigate to the following website: [Miden faucet website](https://faucet.testnet.miden.io/).
+
+2. Copy the **Account ID** printed by the `miden account -l` command in the previous step. Feel free to change the amount of tokens to issue.
+
+3. Paste this ID into the **Request test tokens** input field on the faucet website and click **Send Private Note**.
+
+:::tip
+You can also click **Send Public Note**. If you do this, the note's details will be public and you will not need to download and import it, so you can skip to [Sync the client](#sync-the-client).
+:::
+
+4. After a few seconds your browser should download - or prompt you to download - a file called `note.mno` (mno = Miden note). It contains the funds the faucet sent to your address.
+
+5. Save this file on your computer, you will need it for the next step.
+
+## Import the note into the Miden client
+
+1. Import the private note that you have received using the following commands:
+
+ ```sh
+ miden-client import /note.mno
+ ```
+
+2. You should see something like this:
+
+ ```sh
+ Successfully imported note 0x0ff340133840d35e95e0dc2e62c88ed75ab2e383dc6673ce0341bd486fed8cb6
+ ```
+
+3. Now that the note has been successfully imported, you can view the note's information using the following command:
+
+ ```sh
+ miden-client notes
+ ```
+
+4. You should see something like this:
+
+
+
+:::tip The importance of syncing
+
+- As you can see, the note is listed as `Expected`.
+- This is because you have received a private note but have not yet synced your view of the rollup to check that the note is the result of a valid transaction.
+- Hence, before consuming the note we will need to update our view of the rollup by syncing.
+- Many users could have received the same private note, but only one user can consume the note in a transaction that gets verified by the Miden operator.
+ :::
+
+### Sync the client
+
+Do this periodically to keep informed about any updates on the node by running the `sync` command:
+
+```sh
+miden-client sync
+```
+
+You will see something like this as output:
+
+```sh
+State synced to block 179672
+New public notes: 0
+Committed notes: 1
+Tracked notes consumed: 0
+Tracked accounts updated: 0
+Locked accounts: 0
+Committed transactions: 0
+```
+
+## Consume the note & receive the funds
+
+1. Now that we have synced the client, the input-note imported from the faucet should have a `Committed` status, confirming it exists at the rollup level:
+
+ ```sh
+ miden-client notes
+ ```
+
+2. You should see something like this:
+
+
+
+3. Find your account and note id by listing both `accounts` and `notes`:
+
+ ```sh
+ miden-client account
+ miden-client notes
+ ```
+
+4. Consume the note and add the funds from its vault to our account using the following command:
+
+ ```sh
+ miden-client consume-notes --account
+ ```
+
+5. You should see a confirmation message like this:
+
+
+
+6. After confirming you can view the new note status by running the following command:
+
+ ```sh
+ miden-client notes
+ ```
+
+7. You should see something like this:
+
+
+
+8. The note is `Processing`. This means that the proof of the transaction was sent, but there is no network confirmation yet. You can update your view of the rollup by syncing again:
+
+ ```sh
+ miden-client sync
+ ```
+
+9. After syncing, you should have received confirmation of the consumed note. You should see the note as `Consumed` after listing the notes:
+
+ ```sh
+ miden-client notes
+ ```
+
+
+
+Amazing! You just have created a client-side zero-knowledge proof locally on your machine and submitted it to the Miden rollup.
+
+:::tip
+You only need to copy the top line of characters of the Note ID.
+:::
+
+## View confirmations
+
+5. View your updated account's vault containing the tokens sent by the faucet by running the following command:
+
+ ```sh
+ miden-client account --show
+ ```
+
+6. You should now see your accounts vault containing the funds sent by the faucet.
+
+
+
+## Congratulations!
+
+You have successfully configured and used the Miden client to interact with a Miden rollup and faucet.
+
+You have performed basic Miden rollup operations like submitting proofs of transactions, generating and consuming notes.
+
+For more information on the Miden client, refer to the [Miden client documentation](https://0xMiden.github.io/miden-docs/miden-client/index.html).
+
+## Debugging tips (clear state and folder)
+
+- Need a fresh start? All state is maintained in `store.sqlite3`, located in the directory defined in the `miden-client.toml` file. If you want to clear all state, delete this file. It recreates on any command execution.
+
+- Getting an error? Only execute the `miden-client` command in the folder where your `miden-client.toml` is located.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/index.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/index.md
new file mode 100644
index 00000000..0574c186
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/index.md
@@ -0,0 +1,19 @@
+---
+title: Getting started
+sidebar_position: 2
+---
+
+This section shows you how to get started with Miden by generating a new Miden account, requesting funds from a public faucet, consuming private notes, and creating public pay-to-id-notes.
+
+By the end of this tutorial, you will have:
+
+- Configured the Miden client.
+- Connected to a Miden node.
+- Created an account and requested funds from the faucet.
+- Transferred assets between accounts by creating and consuming notes.
+
+## Prerequisites
+
+### Rust
+
+Download from [the Rust website](https://www.rust-lang.org/learn/get-started).
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/p2p-private.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/p2p-private.md
new file mode 100644
index 00000000..3eab6ec9
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/p2p-private.md
@@ -0,0 +1,128 @@
+---
+title: Private peer-to-peer transfer
+sidebar_position: 3
+---
+
+In this section, we show you how to make private transactions and send funds to another account using the Miden client.
+
+:::info Important: Prerequisite steps
+
+- You should have already followed the [prerequisite steps](index.md#prerequisites) and [create account](create-account-use-faucet) documents.
+- You should _not_ have reset the state of your local client.
+ :::
+
+## Create a second account
+
+:::tip
+Remember to use the [Miden client documentation](https://0xMiden.github.io/miden-docs/miden-client/cli-reference.html) for clarifications.
+:::
+
+1. Create a second account to send funds with. Previously, we created a type `mutable` account (`Account A`). Now, create another `mutable` (`Account B`) using the following command:
+
+ ```sh
+ miden-client new-wallet --mutable
+ ```
+
+2. List and view the newly created accounts with the following command:
+
+ ```sh
+ miden-client account -l
+ ```
+
+3. You should see two accounts:
+
+
+
+## Transfer assets between accounts
+
+1. Now we can transfer some of the tokens we received from the faucet to our second `Account B`.
+
+ To do this, run:
+
+ ```sh
+ miden-client send --sender --target --asset 50:: --note-type private
+ ```
+
+ :::note
+ The faucet account ID can be found on the [Miden faucet website](https://testnet.miden.io/) under the title **Miden faucet**.
+ :::
+
+ This generates a private Pay-to-ID (`P2ID`) note containing `50` assets, transferred from one account to the other.
+
+2. First, sync the accounts.
+
+ ```sh
+ miden-client sync
+ ```
+
+3. Get the second note id.
+
+ ```sh
+ miden-client notes
+ ```
+
+4. Have the second account consume the note.
+
+ ```sh
+ miden-client consume-notes --account
+ ```
+
+ :::tip
+ It's possible to use a short version of the note id: 7 characters after the `0x` is sufficient, e.g. `0x6ae613a`.
+ :::
+
+ You should now see both accounts containing faucet assets with amounts transferred from `Account A` to `Account B`.
+
+5. Check the second account:
+
+ ```sh
+ miden-client account --show
+ ```
+
+
+
+6. Check the original account:
+
+ ```sh
+ miden-client account --show
+ ```
+
+
+
+Wanna do more? [Sending public notes](p2p-public)
+
+## Using the note transport network
+
+The steps above assume that the client owns both accounts. To exchange notes with other users, the note transport network can be used.
+For this the sender (`Account A`) will need the address (bech32 string) of the recipient (`Account B`).
+After creating the note (step 1 above), get the created note ID with `miden-client notes --list`. Then send that note through the note transport network,
+```sh
+miden-client notes --send
+```
+Then the recipient can fetch that note using `miden-client sync`, or more specifically,
+```sh
+miden-client notes --fetch
+```
+The note will then be available to be consumed.
+
+:::note
+
+The client will fetch notes for tracked note tags.
+By default, note tags are derived from the recipient's account ID. However these can also be random to increase privacy.
+In this case, to track a specific tag, run `miden-client tags --add `.
+
+:::
+
+## Congratulations!
+
+You have successfully configured and used the Miden client to interact with a Miden rollup and faucet.
+
+You have performed basic Miden rollup operations like submitting proofs of transactions, generating and consuming notes.
+
+For more information on the Miden client, refer to the [Miden client documentation](https://0xMiden.github.io/miden-docs/miden-client/index.html).
+
+## Clear data
+
+All state is maintained in `store.sqlite3`, located in the directory defined in the `miden-client.toml` file.
+
+To clear all state, delete this file. It recreates on any command execution.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/p2p-public.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/p2p-public.md
new file mode 100644
index 00000000..17f118e8
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/get-started/p2p-public.md
@@ -0,0 +1,97 @@
+---
+title: Peer-to-peer transfer
+sidebar_position: 2
+---
+
+In this section, we show you how to execute transactions and send funds to another account using the Miden client and [public notes](https://0xMiden.github.io/miden-docs/miden-base/architecture/notes.html#note-storage-mode).
+
+:::info Important: Prerequisite steps
+- You should have already followed the [prerequisite steps](index.md#prerequisites) and [create account](create-account-use-faucet) documents.
+- You should have *not* reset the state of your local client.
+:::
+
+## Create a second client
+
+:::tip
+Remember to use the [Miden client documentation](https://0xMiden.github.io/miden-docs/miden-client/cli-reference.html) for clarifications.
+:::
+
+This is an alternative to the [private P2P transactions](p2p-private) process.
+
+In this tutorial, we use two different clients to simulate two different remote users who don't share local state.
+
+To do this, we use two terminals with their own state (using their own `miden-client.toml`).
+
+1. Create a new directory to store the new client.
+
+ ```sh
+ mkdir miden-client-2
+ cd miden-client-2
+ ```
+2. On the new client, create a new [basic account](https://0xMiden.github.io/miden-docs/miden-base/architecture/accounts.html):
+
+ ```sh
+ miden-client new-wallet --mutable -s public
+ ```
+
+ We refer to this account as _Account C_. Note that we set the account's storage mode to `public`, which means that the account details are public and its latest state can be retrieved from the node.
+
+3. List and view the account with the following command:
+
+ ```sh
+ miden-client account -l
+ ```
+
+## Transfer assets between accounts
+
+1. Now we can transfer some of the tokens we received from the faucet to our new account C. Remember to switch back to `miden-client` directory, since you'll be making the txn from Account ID A.
+
+ To do this, from the first client run:
+
+ ```sh
+ miden-client send --sender --target --asset 50:: --note-type public
+ ```
+
+ :::note
+ The faucet account ID can be found on the [Miden faucet website](https://testnet.miden.io/) under the title **Miden faucet**.
+ :::
+
+ This generates a Pay-to-ID (`P2ID`) note containing `50` tokens, transferred from one account to the other. As the note is public, the second account can receive the necessary details by syncing with the node.
+
+2. First, sync the account on the new client.
+
+ ```sh
+ miden-client sync
+ ```
+
+3. At this point, we should have received the public note details.
+
+ ```sh
+ miden-client notes --list
+ ```
+
+ Because the note was retrieved from the node, the commit height will be included and displayed.
+
+4. Have account C consume the note.
+
+ ```sh
+ miden-client consume-notes --account
+ ```
+
+ :::tip
+ It's possible to use a short version of the note id: 7 characters after the `0x` is sufficient, e.g. `0x6ae613a`.
+ :::
+
+That's it!
+
+Account C has now consumed the note and there should be new assets in the account:
+
+```sh
+miden-client account --show
+```
+
+## Clear state
+
+All state is maintained in `store.sqlite3`, located in the directory defined in the `miden-client.toml` file.
+
+To clear all state, delete this file. It recreates on any command execution.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/index.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/index.md
new file mode 100644
index 00000000..10eb06f7
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/index.md
@@ -0,0 +1,22 @@
+---
+title: Rust
+sidebar_position: 1
+---
+
+# Overview
+
+The Miden client in Rust contains, the Miden client library and the Miden client cli.
+
+### Miden client library
+
+The Miden client library is a Rust library that can be integrated into projects, allowing developers to interact with the Miden rollup.
+
+The library provides a set of APIs and functions for executing transactions, generating proofs, and managing activity on the Miden network.
+
+### Miden client CLI
+
+The Miden client also includes a command-line interface (CLI) that serves as a wrapper around the library, exposing its basic functionality in a user-friendly manner.
+
+The CLI provides commands for interacting with the Miden rollup, such as submitting transactions, syncing with the network, and managing account data.
+
+More information about the CLI can be found in the [CLI section](./cli/index.md).
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/install-and-run.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/install-and-run.md
new file mode 100644
index 00000000..1915b161
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/install-and-run.md
@@ -0,0 +1,35 @@
+---
+title: Installation
+sidebar_position: 1
+---
+
+## Software prerequisites
+
+- [Rust installation](https://www.rust-lang.org/tools/install) minimum version 1.88.
+
+## Install the client
+
+Run the following command to install the miden-client:
+
+```sh
+cargo install miden-client-cli --locked
+```
+
+This installs the `miden-client` binary (at `~/.cargo/bin/miden-client`).
+
+## Run the client
+
+If the install worked correctly, you should be able to check the version by running:
+
+```sh
+miden-client --version
+```
+
+Once installed, you may run:
+```sh
+miden-client --help
+```
+
+This will show you the available commands and options for the client.
+
+An more in depth tutorial can be fund in the [Getting started section](./get-started).
diff --git a/versioned_docs/version-0.14/builder/tools/clients/rust-client/library.md b/versioned_docs/version-0.14/builder/tools/clients/rust-client/library.md
new file mode 100644
index 00000000..0863b23e
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/rust-client/library.md
@@ -0,0 +1,139 @@
+---
+title: Library
+sidebar_position: 5
+---
+
+To use the Miden client library in a Rust project, include it as a dependency.
+
+In your project's `Cargo.toml`, add:
+
+```toml
+miden-client = { version = "0.11" }
+```
+
+## Client instantiation
+
+The recommended way to create a client is using the `ClientBuilder`. For standard networks, use the pre-configured constructors:
+
+```rust
+use std::sync::Arc;
+use miden_client::builder::ClientBuilder;
+use miden_client_sqlite_store::SqliteStore;
+
+// Create store
+let sqlite_store = SqliteStore::new("path/to/store".try_into()?).await?;
+let store = Arc::new(sqlite_store);
+
+// Build client for testnet (pre-configured RPC, prover, and note transport)
+let client = ClientBuilder::for_testnet()
+ .store(store)
+ .filesystem_keystore("path/to/keys")?
+ .build()
+ .await?;
+```
+
+Other network constructors are available:
+- `ClientBuilder::for_testnet()` - Pre-configured for Miden testnet
+- `ClientBuilder::for_devnet()` - Pre-configured for Miden devnet
+- `ClientBuilder::for_localhost()` - Pre-configured for local development
+
+For custom configurations, use `ClientBuilder::new()` and configure each component:
+
+```rust
+use std::sync::Arc;
+use miden_client::builder::ClientBuilder;
+use miden_client::rpc::{Endpoint, GrpcClient};
+use miden_client_sqlite_store::SqliteStore;
+
+// Create store
+let sqlite_store = SqliteStore::new("path/to/store".try_into()?).await?;
+let store = Arc::new(sqlite_store);
+
+// Setup the gRPC endpoint
+let endpoint = Endpoint::new("https".into(), "localhost".into(), Some(57291));
+
+let client = ClientBuilder::new()
+ .grpc_client(&endpoint, None)
+ .store(store)
+ .filesystem_keystore("path/to/keys")?
+ // Optional: custom prover via .prover(Arc::new(prover))
+ // Optional: note transport via .note_transport(Arc::new(nt_client))
+ // Optional: debug mode via .in_debug_mode(DebugMode::Enabled)
+ .build()
+ .await?;
+```
+
+## Create local account
+
+With the Miden client, you can create and track any number of public and local accounts. For local accounts, the state is tracked locally, and the rollup only keeps commitments to the data, which in turn guarantees privacy.
+
+The `AccountBuilder` can be used to create a new account with the specified parameters and components. The following code creates a new local account:
+
+```rust
+let key_pair = SecretKey::with_rng(client.rng());
+
+let new_account = AccountBuilder::new(init_seed) // Seed should be random for each account
+ .account_type(AccountType::RegularAccountImmutableCode)
+ .storage_mode(AccountStorageMode::Private)
+ .with_auth_component(AuthRpoFalcon512::new(key_pair.public_key()))
+ .with_component(BasicWallet)
+ .build()?;
+keystore.add_key(&AuthSecretKey::RpoFalcon512(key_pair), new_account.id()).await?;
+client.add_account(&new_account, false).await?;
+```
+Once an account is created, it is kept locally and its state is automatically tracked by the client.
+
+To create an public account, you can specify `AccountStorageMode::Public` like so:
+
+```Rust
+let key_pair = SecretKey::with_rng(client.rng());
+let anchor_block = client.get_latest_epoch_block().await.unwrap();
+
+let new_account = AccountBuilder::new(init_seed) // Seed should be random for each account
+ .anchor((&anchor_block).try_into().unwrap())
+ .account_type(AccountType::RegularAccountImmutableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthRpoFalcon512::new(key_pair.public_key()))
+ .with_component(BasicWallet)
+ .build()?;
+keystore.add_key(&AuthSecretKey::RpoFalcon512(key_pair), new_account.id()).await?;
+client.add_account(&new_account, false).await?;
+```
+
+The account's state is also tracked locally, but during sync the client updates the account state by querying the node for the most recent account data.
+
+## Execute transaction
+
+In order to execute a transaction, you first need to define which type of transaction is to be executed. This may be done with the `TransactionRequest` which represents a general definition of a transaction. Some standardized constructors are available for common transaction types.
+
+Here is an example for a `pay-to-id` transaction type:
+
+```rust
+// Define asset
+let faucet_id = AccountId::from_hex(faucet_id)?;
+let fungible_asset = FungibleAsset::new(faucet_id, *amount)?.into();
+
+let sender_account_id = AccountId::from_hex(bob_account_id)?;
+let target_account_id = AccountId::from_hex(alice_account_id)?;
+let payment_description = PaymentNoteDescription::new(
+ vec![fungible_asset.into()],
+ sender_account_id,
+ target_account_id,
+);
+
+let transaction_request = TransactionRequestBuilder::new().build_pay_to_id(
+ payment_description,
+ None,
+ NoteType::Private,
+ client.rng(),
+)?;
+
+// Execute transaction. No information is tracked after this.
+let transaction_execution_result = client.new_transaction(sender_account_id, transaction_request.clone()).await?;
+
+// Prove and submit the transaction, which is stored alongside created notes (if any)
+client.submit_transaction(transaction_execution_result).await?
+```
+
+You can decide whether you want the note details to be public or private through the `note_type` parameter.
+You may also customize the transaction request with the other `TransactionRequestBuilder` methods. This allows you to run custom code, with custom note arguments and additional output/input notes as well.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Danger.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Danger.tsx
new file mode 100644
index 00000000..ab14d7de
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Danger.tsx
@@ -0,0 +1,19 @@
+import React, { type ReactNode } from "react";
+import type { Props } from "@theme/Admonition/Icon/Danger";
+
+export default function AdmonitionIconDanger(props: Props): ReactNode {
+ return (
+
+
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Info.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Info.tsx
new file mode 100644
index 00000000..59e48a52
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Info.tsx
@@ -0,0 +1,20 @@
+import React, { type ReactNode } from "react";
+import type { Props } from "@theme/Admonition/Icon/Info";
+
+export default function AdmonitionIconInfo(props: Props): ReactNode {
+ return (
+
+
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Note.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Note.tsx
new file mode 100644
index 00000000..d7c524b3
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Note.tsx
@@ -0,0 +1,20 @@
+import React, { type ReactNode } from "react";
+import type { Props } from "@theme/Admonition/Icon/Note";
+
+export default function AdmonitionIconNote(props: Props): ReactNode {
+ return (
+
+
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Tip.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Tip.tsx
new file mode 100644
index 00000000..219bb8d0
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Tip.tsx
@@ -0,0 +1,20 @@
+import React, { type ReactNode } from "react";
+import type { Props } from "@theme/Admonition/Icon/Tip";
+
+export default function AdmonitionIconTip(props: Props): ReactNode {
+ return (
+
+
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Warning.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Warning.tsx
new file mode 100644
index 00000000..f96398d1
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Icon/Warning.tsx
@@ -0,0 +1,20 @@
+import React, { type ReactNode } from "react";
+import type { Props } from "@theme/Admonition/Icon/Warning";
+
+export default function AdmonitionIconCaution(props: Props): ReactNode {
+ return (
+
+
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Layout/index.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Layout/index.tsx
new file mode 100644
index 00000000..7b2c170d
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Layout/index.tsx
@@ -0,0 +1,51 @@
+import React, { type ReactNode } from "react";
+import clsx from "clsx";
+import { ThemeClassNames } from "@docusaurus/theme-common";
+
+import type { Props } from "@theme/Admonition/Layout";
+
+import styles from "./styles.module.css";
+
+function AdmonitionContainer({
+ type,
+ className,
+ children,
+}: Pick & { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+function AdmonitionHeading({ icon, title }: Pick) {
+ return (
+
+ {icon}
+ {/* {title} */}
+
+ );
+}
+
+function AdmonitionContent({ children }: Pick) {
+ return children ? (
+ {children}
+ ) : null;
+}
+
+export default function AdmonitionLayout(props: Props): ReactNode {
+ const { type, icon, title, children, className } = props;
+ return (
+
+ {title || icon ? : null}
+ {children}
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Layout/styles.module.css b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Layout/styles.module.css
new file mode 100644
index 00000000..88df7e63
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Layout/styles.module.css
@@ -0,0 +1,35 @@
+.admonition {
+ margin-bottom: 1em;
+}
+
+.admonitionHeading {
+ font: var(--ifm-heading-font-weight) var(--ifm-h5-font-size) /
+ var(--ifm-heading-line-height) var(--ifm-heading-font-family);
+ text-transform: uppercase;
+}
+
+/* Heading alone without content (does not handle fragment content) */
+.admonitionHeading:not(:last-child) {
+ margin-bottom: 0.3rem;
+}
+
+.admonitionHeading code {
+ text-transform: none;
+}
+
+.admonitionIcon {
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: 0.4em;
+}
+
+.admonitionIcon svg {
+ display: inline-block;
+ height: 1.6em;
+ width: 1.6em;
+ fill: var(--ifm-alert-foreground-color);
+}
+
+.admonitionContent > :last-child {
+ margin-bottom: 0;
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Caution.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Caution.tsx
new file mode 100644
index 00000000..b570a37a
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Caution.tsx
@@ -0,0 +1,32 @@
+import React, {type ReactNode} from 'react';
+import clsx from 'clsx';
+import Translate from '@docusaurus/Translate';
+import type {Props} from '@theme/Admonition/Type/Caution';
+import AdmonitionLayout from '@theme/Admonition/Layout';
+import IconWarning from '@theme/Admonition/Icon/Warning';
+
+const infimaClassName = 'alert alert--warning';
+
+const defaultProps = {
+ icon: ,
+ title: (
+
+ caution
+
+ ),
+};
+
+// TODO remove before v4: Caution replaced by Warning
+// see https://github.com/facebook/docusaurus/issues/7558
+export default function AdmonitionTypeCaution(props: Props): ReactNode {
+ return (
+
+ {props.children}
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Danger.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Danger.tsx
new file mode 100644
index 00000000..49901fa9
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Danger.tsx
@@ -0,0 +1,30 @@
+import React, {type ReactNode} from 'react';
+import clsx from 'clsx';
+import Translate from '@docusaurus/Translate';
+import type {Props} from '@theme/Admonition/Type/Danger';
+import AdmonitionLayout from '@theme/Admonition/Layout';
+import IconDanger from '@theme/Admonition/Icon/Danger';
+
+const infimaClassName = 'alert alert--danger';
+
+const defaultProps = {
+ icon: ,
+ title: (
+
+ danger
+
+ ),
+};
+
+export default function AdmonitionTypeDanger(props: Props): ReactNode {
+ return (
+
+ {props.children}
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Info.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Info.tsx
new file mode 100644
index 00000000..018e0a16
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Info.tsx
@@ -0,0 +1,30 @@
+import React, {type ReactNode} from 'react';
+import clsx from 'clsx';
+import Translate from '@docusaurus/Translate';
+import type {Props} from '@theme/Admonition/Type/Info';
+import AdmonitionLayout from '@theme/Admonition/Layout';
+import IconInfo from '@theme/Admonition/Icon/Info';
+
+const infimaClassName = 'alert alert--info';
+
+const defaultProps = {
+ icon: ,
+ title: (
+
+ info
+
+ ),
+};
+
+export default function AdmonitionTypeInfo(props: Props): ReactNode {
+ return (
+
+ {props.children}
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Note.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Note.tsx
new file mode 100644
index 00000000..c99e0385
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Note.tsx
@@ -0,0 +1,30 @@
+import React, {type ReactNode} from 'react';
+import clsx from 'clsx';
+import Translate from '@docusaurus/Translate';
+import type {Props} from '@theme/Admonition/Type/Note';
+import AdmonitionLayout from '@theme/Admonition/Layout';
+import IconNote from '@theme/Admonition/Icon/Note';
+
+const infimaClassName = 'alert alert--secondary';
+
+const defaultProps = {
+ icon: ,
+ title: (
+
+ note
+
+ ),
+};
+
+export default function AdmonitionTypeNote(props: Props): ReactNode {
+ return (
+
+ {props.children}
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Tip.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Tip.tsx
new file mode 100644
index 00000000..18604a5e
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Tip.tsx
@@ -0,0 +1,30 @@
+import React, {type ReactNode} from 'react';
+import clsx from 'clsx';
+import Translate from '@docusaurus/Translate';
+import type {Props} from '@theme/Admonition/Type/Tip';
+import AdmonitionLayout from '@theme/Admonition/Layout';
+import IconTip from '@theme/Admonition/Icon/Tip';
+
+const infimaClassName = 'alert alert--success';
+
+const defaultProps = {
+ icon: ,
+ title: (
+
+ tip
+
+ ),
+};
+
+export default function AdmonitionTypeTip(props: Props): ReactNode {
+ return (
+
+ {props.children}
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Warning.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Warning.tsx
new file mode 100644
index 00000000..61d9597b
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Type/Warning.tsx
@@ -0,0 +1,30 @@
+import React, {type ReactNode} from 'react';
+import clsx from 'clsx';
+import Translate from '@docusaurus/Translate';
+import type {Props} from '@theme/Admonition/Type/Warning';
+import AdmonitionLayout from '@theme/Admonition/Layout';
+import IconWarning from '@theme/Admonition/Icon/Warning';
+
+const infimaClassName = 'alert alert--warning';
+
+const defaultProps = {
+ icon: ,
+ title: (
+
+ warning
+
+ ),
+};
+
+export default function AdmonitionTypeWarning(props: Props): ReactNode {
+ return (
+
+ {props.children}
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Types.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Types.tsx
new file mode 100644
index 00000000..2a100190
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/Types.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import AdmonitionTypeNote from '@theme/Admonition/Type/Note';
+import AdmonitionTypeTip from '@theme/Admonition/Type/Tip';
+import AdmonitionTypeInfo from '@theme/Admonition/Type/Info';
+import AdmonitionTypeWarning from '@theme/Admonition/Type/Warning';
+import AdmonitionTypeDanger from '@theme/Admonition/Type/Danger';
+import AdmonitionTypeCaution from '@theme/Admonition/Type/Caution';
+import type AdmonitionTypes from '@theme/Admonition/Types';
+
+const admonitionTypes: typeof AdmonitionTypes = {
+ note: AdmonitionTypeNote,
+ tip: AdmonitionTypeTip,
+ info: AdmonitionTypeInfo,
+ warning: AdmonitionTypeWarning,
+ danger: AdmonitionTypeDanger,
+};
+
+// Undocumented legacy admonition type aliases
+// Provide hardcoded/untranslated retrocompatible label
+// See also https://github.com/facebook/docusaurus/issues/7767
+const admonitionAliases: typeof AdmonitionTypes = {
+ secondary: (props) => ,
+ important: (props) => ,
+ success: (props) => ,
+ caution: AdmonitionTypeCaution,
+};
+
+export default {
+ ...admonitionTypes,
+ ...admonitionAliases,
+};
diff --git a/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/index.tsx b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/index.tsx
new file mode 100644
index 00000000..8f4225da
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/theme/Admonition/index.tsx
@@ -0,0 +1,21 @@
+import React, {type ComponentType, type ReactNode} from 'react';
+import {processAdmonitionProps} from '@docusaurus/theme-common';
+import type {Props} from '@theme/Admonition';
+import AdmonitionTypes from '@theme/Admonition/Types';
+
+function getAdmonitionTypeComponent(type: string): ComponentType {
+ const component = AdmonitionTypes[type];
+ if (component) {
+ return component;
+ }
+ console.warn(
+ `No admonition component found for admonition type "${type}". Using Info as fallback.`,
+ );
+ return AdmonitionTypes.info!;
+}
+
+export default function Admonition(unprocessedProps: Props): ReactNode {
+ const props = processAdmonitionProps(unprocessedProps);
+ const AdmonitionTypeComponent = getAdmonitionTypeComponent(props.type);
+ return ;
+}
diff --git a/versioned_docs/version-0.14/builder/tools/clients/web-client/_category_.yml b/versioned_docs/version-0.14/builder/tools/clients/web-client/_category_.yml
new file mode 100644
index 00000000..9d57d1b6
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/web-client/_category_.yml
@@ -0,0 +1,4 @@
+label: TypeScript
+# Sidebar position within tools/clients/ (Rust is 1, TypeScript is 2, React is 3)
+position: 2
+collapsed: true
diff --git a/versioned_docs/version-0.14/builder/tools/clients/web-client/accounts.md b/versioned_docs/version-0.14/builder/tools/clients/web-client/accounts.md
new file mode 100644
index 00000000..db231a4c
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/web-client/accounts.md
@@ -0,0 +1,292 @@
+---
+title: Accounts
+sidebar_position: 3
+---
+
+# Accounts
+
+`client.accounts` is the resource namespace for everything account-related: creation, lookup, listing, import / export, and address management. All ID fields accept an `AccountRef` — a hex string, bech32 string, `AccountId`, `Account`, or `AccountHeader` — so you rarely need to convert between them.
+
+## Account type and auth scheme values
+
+Account creation uses two small enums:
+
+| Kind | Accepted values | Meaning |
+| --- | --- | --- |
+| `AccountType.FungibleFaucet` | `0` | Mintable token source |
+| `AccountType.NonFungibleFaucet` | `1` | Non-fungible token source |
+| `AccountType.RegularAccountImmutableCode` | `2` | Immutable wallet or contract |
+| `AccountType.RegularAccountUpdatableCode` | `3` | Mutable wallet or contract (default) |
+| `auth` field | `"falcon"` \| `"ecdsa"` | Signing scheme — Falcon is the default |
+| `storage` field | `"public"` \| `"private"` \| `"network"` | Visibility and persistence mode |
+
+All snippets below use these names. The SDK also ships friendly aliases (`AccountType.MutableWallet`, `AccountType.ImmutableContract`, etc.) but prefer the canonical names above when writing TypeScript — the aliases currently fail strict type-checking in some builds.
+
+## Create
+
+### Wallet
+
+```typescript
+import { MidenClient, AccountType } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+// Default: mutable wallet, private storage, Falcon auth
+const wallet = await client.accounts.create();
+
+// With explicit options
+const wallet2 = await client.accounts.create({
+ type: AccountType.RegularAccountImmutableCode, // immutable wallet
+ storage: "public",
+ auth: "ecdsa",
+ seed: "my-seed", // hashed to 32 bytes via SHA-256
+});
+
+console.log(wallet.id().toString()); // hex
+console.log(wallet.nonce().toString());
+console.log(wallet.isPublic());
+console.log(wallet.isPrivate());
+console.log(wallet.isFaucet());
+console.log(wallet.isRegularAccount());
+```
+
+### Faucet
+
+```typescript
+import { MidenClient, AccountType } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+const faucet = await client.accounts.create({
+ type: AccountType.FungibleFaucet,
+ symbol: "TEST",
+ decimals: 8,
+ maxSupply: 10_000_000n, // number | bigint
+});
+
+const faucet2 = await client.accounts.create({
+ type: AccountType.FungibleFaucet,
+ symbol: "DAG",
+ decimals: 8,
+ maxSupply: 10_000_000n,
+ storage: "public",
+ auth: "falcon",
+});
+```
+
+### Contract
+
+Contract accounts hold custom MASM code. Compile the code into an `AccountComponent` with [`client.compile.component()`](./compile.md), then create the account:
+
+```typescript
+import {
+ MidenClient,
+ AccountType,
+ AuthSecretKey,
+ StorageSlot,
+} from "@miden-sdk/miden-sdk";
+
+const counterCode = `
+ use miden::protocol::active_account
+ use miden::protocol::native_account
+ use miden::core::word
+ use miden::core::sys
+
+ const COUNTER_SLOT = word("miden::tutorials::counter")
+
+ pub proc get_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ exec.sys::truncate_stack
+ end
+
+ pub proc increment_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ add.1
+ push.COUNTER_SLOT[0..2] exec.native_account::set_item
+ exec.sys::truncate_stack
+ end
+`;
+
+const client = await MidenClient.createTestnet();
+
+const component = await client.compile.component({
+ code: counterCode,
+ slots: [StorageSlot.emptyValue("miden::tutorials::counter")],
+});
+
+const seed = crypto.getRandomValues(new Uint8Array(32));
+const auth = AuthSecretKey.rpoFalconWithRNG(seed);
+
+const contract = await client.accounts.create({
+ type: AccountType.RegularAccountImmutableCode, // immutable contract
+ seed,
+ auth,
+ components: [component],
+});
+
+console.log("Contract:", contract.id().toString());
+console.log("Is public:", contract.isPublic()); // contracts default to public
+```
+
+Contract defaults:
+
+- **Storage** — defaults to `"public"` (so other accounts can read state for FPI). Pass `storage: "private"` to override.
+- **Auth** — must be a concrete `AuthSecretKey` instance, not a scheme string. The caller retains the key; the client uses it to sign transactions that touch the contract.
+- **Seed** — required, 32 bytes. Determines the account ID.
+
+### Pre-built via `AccountBuilder`
+
+When you need full control — e.g. an external signer that provides an auth commitment instead of a secret key — build the account manually and hand it to `insert()`:
+
+```typescript
+import {
+ MidenClient,
+ AccountBuilder,
+ AccountComponent,
+ AccountStorageMode,
+ AccountType,
+} from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+const commitment = /* externalSigner.getPublicKeyCommitment() */ (undefined as any);
+const seed = new Uint8Array(32); // deterministic seed from your derivation path
+
+const account = new AccountBuilder(seed)
+ .withAuthComponent(
+ AccountComponent.createAuthComponentFromCommitment(commitment, 1),
+ )
+ .accountType(AccountType.RegularAccountImmutableCode)
+ .storageMode(AccountStorageMode.public())
+ .withBasicWalletComponent()
+ .build().account;
+
+await client.accounts.insert({ account });
+```
+
+`accounts.insert()` is local-only: it persists the account to the store without any network call. Use it when you already have a valid `Account` object and just need to track it.
+
+## Retrieve
+
+### `get`
+
+```typescript
+const account = await client.accounts.get("0x1234...");
+if (!account) {
+ console.log("Not found locally");
+ return;
+}
+console.log(account.id().toString());
+console.log(account.nonce().toString());
+console.log(account.isPublic());
+console.log(account.isFaucet());
+```
+
+`get` only consults the local store. It returns `null` if the account isn't tracked.
+
+### `getOrImport`
+
+```typescript
+// Returns the local copy if present; otherwise fetches from the network and stores it.
+const account = await client.accounts.getOrImport(
+ "mtst1arjemrxne8lj5qz4mg9c8mtyxg954483",
+);
+console.log("Nonce:", account.nonce().toString());
+```
+
+Use `getOrImport` for accounts you didn't create — a faucet or contract deployed by another party, for example. Once imported, subsequent calls return the local copy without hitting the network.
+
+`get` vs `getOrImport`:
+
+- `get` for local-only reads. Cheap, no network.
+- `getOrImport` when the account may need to be pulled from chain.
+
+### `list`
+
+```typescript
+const accounts = await client.accounts.list();
+for (const header of accounts) {
+ console.log(header.id().toString(), header.nonce().toString());
+}
+```
+
+Returns `AccountHeader[]` — a lightweight summary suitable for listing UIs. Call `get()` if you need the full `Account`.
+
+### `getDetails`
+
+```typescript
+const details = await client.accounts.getDetails("0x1234...");
+console.log(details.account.id().toString());
+console.log(details.vault); // AssetVault
+console.log(details.storage); // AccountStorage
+console.log(details.code); // AccountCode | null
+console.log(details.keys); // Word[] (public-key commitments)
+```
+
+One round-trip returns the full account plus its vault, storage, code, and key commitments.
+
+### `getBalance`
+
+```typescript
+const balance: bigint = await client.accounts.getBalance(
+ "0xACCOUNT...",
+ "0xFAUCET...",
+);
+console.log(`Balance: ${balance}`);
+```
+
+## Address management
+
+```typescript
+await client.accounts.addAddress("0xACCOUNT...", "mtst1address...");
+await client.accounts.removeAddress("0xACCOUNT...", "mtst1address...");
+```
+
+Associates one or more bech32 addresses with an account. Useful when your UI lets users alias accounts by a human-readable string.
+
+## Import
+
+`client.accounts.import()` accepts a discriminated union:
+
+```typescript
+// 1. By reference — fetches a public account from the network.
+await client.accounts.import("0x1234..."); // hex
+await client.accounts.import("mtst1arjemrxne8lj5qz4mg9c8mtyxg954483"); // bech32
+
+// 2. From a previously exported file.
+await client.accounts.import({ file: accountFile });
+
+// 3. From a seed — PUBLIC ACCOUNTS ONLY.
+await client.accounts.import({
+ seed: initSeed, // Uint8Array
+ type: AccountType.RegularAccountUpdatableCode, // mutable wallet
+ auth: "falcon",
+});
+```
+
+:::warning[Seed imports are public-only]
+Import-by-seed works only for accounts originally created with `storage: "public"`. Private account state is never published on-chain, so it can't be reconstructed from a seed alone. For private accounts, use the file export / import workflow below.
+:::
+
+If you aren't sure whether the account is already in your local store, prefer [`getOrImport()`](#getorimport) — it skips the network call when the account is already known.
+
+## Export
+
+```typescript
+const accountFile = await client.accounts.export("0x1234...");
+// Persist accountFile to disk, send it to another device, etc.
+```
+
+`AccountFile` includes the full account state, code, seed (if new), and tracked secret keys. Treat it as sensitive.
+
+## Error behaviour
+
+- `get()` returns `null` when the account is not in the local store.
+- `getDetails()`, `getBalance()`, and `export()` throw `"Account not found: 0x..."` when the account is missing.
+- `import({ seed, ... })` on a private account throws at resolve time — private state isn't recoverable from a seed.
+
+## Next
+
+- [Transactions](./transactions.md) — spend, mint, consume, swap.
+- [Compile](./compile.md) — turn MASM into `AccountComponent`s for contract accounts.
+- [Sync and store](./sync.md) — refresh account state from the network.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/web-client/compile.md b/versioned_docs/version-0.14/builder/tools/clients/web-client/compile.md
new file mode 100644
index 00000000..64749ebd
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/web-client/compile.md
@@ -0,0 +1,225 @@
+---
+title: Compile
+sidebar_position: 6
+---
+
+# Compile
+
+`client.compile` turns Miden Assembly (MASM) source into the three runtime artifacts the rest of the SDK consumes:
+
+| Method | Produces | Used by |
+| --- | --- | --- |
+| `client.compile.component({ code, slots?, supportAllTypes? })` | `AccountComponent` | [`accounts.create({ components: [...] })`](./accounts.md#contract) |
+| `client.compile.txScript({ code, libraries? })` | `TransactionScript` | [`transactions.execute({ script })`](./transactions.md#custom-transaction-scripts-execute) |
+| `client.compile.noteScript({ code, libraries? })` | `NoteScript` | `Note` construction utilities |
+
+Each call spins up a fresh `CodeBuilder`, so libraries linked in one call never leak into another.
+
+## Account components
+
+```typescript
+import { MidenClient, StorageSlot } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+const contractCode = `
+ use miden::protocol::active_account
+ use miden::protocol::native_account
+ use miden::core::word
+ use miden::core::sys
+
+ const COUNTER_SLOT = word("miden::tutorials::counter")
+
+ pub proc get_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ exec.sys::truncate_stack
+ end
+
+ pub proc increment_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ add.1
+ push.COUNTER_SLOT[0..2] exec.native_account::set_item
+ exec.sys::truncate_stack
+ end
+`;
+
+const component = await client.compile.component({
+ code: contractCode,
+ slots: [StorageSlot.emptyValue("miden::tutorials::counter")],
+});
+
+// Use the procedure hash when calling this contract via FPI
+const getCountHash = component.getProcedureHash("get_count");
+console.log("get_count hash:", getCountHash);
+```
+
+Options:
+
+- `code` — the MASM source for the component.
+- `slots` — initial storage slots. Use the `StorageSlot` helpers (`emptyValue`, etc.).
+- `supportAllTypes` — defaults to `true`. When `true`, the compiler auto-injects an auth-kernel invocation so the component accepts the standard set of input types for authenticated transactions. Set to `false` if your component already invokes an auth kernel procedure itself, or intentionally omits one.
+
+## Transaction scripts
+
+### Without libraries
+
+A script with no `libraries` entry can only reference procedures that exist in the transaction kernel and the standard library — no custom external contracts:
+
+```typescript
+const script = await client.compile.txScript({
+ code: `
+ use miden::core::sys
+ begin
+ push.0
+ exec.sys::truncate_stack
+ end
+ `,
+});
+```
+
+If your script needs to call into an external contract (as in the FPI section below), you must pass that contract's code through `libraries` — the compiler only links what you explicitly provide.
+
+### With inline libraries
+
+```typescript
+import { Linking } from "@miden-sdk/miden-sdk";
+
+const script = await client.compile.txScript({
+ code: `
+ use external_contract::my_contract
+ use miden::core::sys
+ begin
+ call.my_contract::do_something
+ exec.sys::truncate_stack
+ end
+ `,
+ libraries: [
+ {
+ namespace: "external_contract::my_contract",
+ code: myContractCode,
+ linking: Linking.Dynamic, // default
+ },
+ ],
+});
+```
+
+Each library takes:
+
+| Field | Required | Description |
+| --- | --- | --- |
+| `namespace` | yes | MASM namespace, e.g. `"counter::module"`. |
+| `code` | yes | MASM source. |
+| `linking` | no | `Linking.Dynamic` (default) or `Linking.Static`. `"dynamic"` / `"static"` string literals are also accepted. |
+
+### Linking modes
+
+| Value | Behaviour | When to use |
+| --- | --- | --- |
+| `Linking.Dynamic` (default) | Links via DYNCALL at prove time. The foreign contract's on-chain code is fetched by the prover. | FPI — foreign contract lives on-chain. |
+| `Linking.Static` | Inlines library code into the script at compile time. | Off-chain libraries that must be self-contained. |
+
+## Note scripts
+
+Note scripts run when an account consumes the note. The shape mirrors `txScript`; use it when you need custom logic on consumption.
+
+```typescript
+const noteScript = await client.compile.noteScript({
+ code: `
+ use miden::protocol::active_note
+ use miden::core::sys
+
+ begin
+ # Runs when the consuming account redeems this note.
+ # Real note scripts inspect note storage, assets, and account state
+ # using procedures from miden::protocol::active_note.
+ exec.sys::truncate_stack
+ end
+ `,
+});
+```
+
+Libraries follow the same `{ namespace, code, linking? }` shape as transaction scripts.
+
+## Procedure hashes (for FPI)
+
+Foreign procedure invocation requires the **hash** of the target procedure. Extract it from a compiled component:
+
+```typescript
+const component = await client.compile.component({
+ code: counterContractCode,
+ slots: [StorageSlot.emptyValue("miden::tutorials::counter")],
+});
+
+const getCountHash = component.getProcedureHash("get_count");
+
+const script = await client.compile.txScript({
+ code: `
+ use external_contract::count_reader_contract
+ use miden::core::sys
+ begin
+ push.${getCountHash}
+ push.${counterAccountId.suffix()}
+ push.${counterAccountId.prefix()}
+ call.count_reader_contract::copy_count
+ exec.sys::truncate_stack
+ end
+ `,
+ libraries: [
+ { namespace: "external_contract::count_reader_contract", code: countReaderCode },
+ ],
+});
+```
+
+## End-to-end: compile → create contract → execute script
+
+```typescript
+import {
+ MidenClient,
+ AccountType,
+ AuthSecretKey,
+ StorageSlot,
+} from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+await client.sync();
+
+// 1. Compile the contract component
+const component = await client.compile.component({
+ code: counterCode,
+ slots: [StorageSlot.emptyValue("miden::tutorials::counter")],
+});
+
+// 2. Create the contract account
+const seed = crypto.getRandomValues(new Uint8Array(32));
+const auth = AuthSecretKey.rpoFalconWithRNG(seed);
+
+const contract = await client.accounts.create({
+ type: AccountType.RegularAccountImmutableCode,
+ seed,
+ auth,
+ components: [component],
+});
+
+await client.sync();
+
+// 3. Compile the transaction script
+const script = await client.compile.txScript({
+ code: `
+ use external_contract::counter_contract
+ begin
+ call.counter_contract::increment_count
+ end
+ `,
+ libraries: [
+ { namespace: "external_contract::counter_contract", code: counterCode },
+ ],
+});
+
+// 4. Execute
+const { txId } = await client.transactions.execute({
+ account: contract.id(),
+ script,
+});
+
+console.log("Tx:", txId.toHex());
+```
diff --git a/versioned_docs/version-0.14/builder/tools/clients/web-client/index.md b/versioned_docs/version-0.14/builder/tools/clients/web-client/index.md
new file mode 100644
index 00000000..fe38017d
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/web-client/index.md
@@ -0,0 +1,78 @@
+---
+title: Overview
+sidebar_position: 1
+---
+
+# Web SDK (@miden-sdk/miden-sdk)
+
+The Web SDK is the browser-focused toolkit for the Miden network. It wraps the Rust client, compiles to WebAssembly, and exposes a typed JavaScript API through the `MidenClient` class. Use it from web apps, wallets, dApps, service workers, Node servers — any JavaScript runtime that supports Web Workers and WebAssembly.
+
+## Capabilities
+
+- Read and write on-chain state: accounts, notes, transactions, tags.
+- Build and execute Miden transactions, including custom MASM scripts.
+- Compile Miden Assembly into account components, transaction scripts, and note scripts directly in the browser.
+- Generate zero-knowledge proofs locally via the in-browser prover, or offload proving to a remote or delegated prover.
+- Manage keys through built-in Falcon/ECDSA keystores or external signer integrations.
+- Exchange private notes through the Miden note transport network.
+- Import / export account files, note files, and full store snapshots for backup and migration.
+
+## Architecture
+
+```text
+┌────────────────────────────────────────────────┐
+│ @miden-sdk/miden-sdk (npm) │
+│ │
+│ MidenClient (typed TS API) │
+│ │ │
+│ ├─ accounts / transactions / notes / │
+│ │ tags / compile / keystore namespaces │
+│ │ │
+│ └─ wraps WasmWebClient (Rust → WASM) │
+│ │
+│ Runs prove / execute on a dedicated │
+│ Web Worker to keep the main thread responsive │
+└────────────────────────────────────────────────┘
+```
+
+The SDK is built from the `web-client` Rust crate in [0xMiden/miden-client](https://github.com/0xMiden/miden-client), compiled with `wasm-bindgen`, and bundled with the WASM module, JavaScript bindings, and a dedicated Web Worker script.
+
+## Resource management
+
+Each `MidenClient` instance holds a dedicated Web Worker thread. When you no longer need a client — for example in a multi-wallet app that creates one client per active network — call `client.terminate()` to release the worker.
+
+```typescript
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+// ... use the client ...
+
+// Free the Web Worker when you are done
+client.terminate();
+```
+
+In environments that support the TC39 [explicit resource management](https://github.com/tc39/proposal-explicit-resource-management) proposal, you can use `using` to let the runtime handle cleanup automatically:
+
+```typescript
+{
+ using client = await MidenClient.createTestnet();
+ // ... client.terminate() is called automatically when the block exits
+}
+```
+
+After `terminate()`, every subsequent method call throws `Error("Client terminated")`.
+
+## Where to go next
+
+- [Setup](./setup.md) — install the SDK and create your first client.
+- [Accounts](./accounts.md) — create wallets, faucets, and contract accounts; look up existing ones.
+- [Transactions](./transactions.md) — mint, send, consume, swap, and run custom scripts.
+- [Notes](./notes.md) — list, import, export, and transport private notes.
+- [Compile](./compile.md) — turn Miden Assembly into account components and scripts.
+- [Sync and store](./sync.md) — pull network state and manage the local database.
+- [Testing](./testing.md) — drive a fully in-memory mock chain for fast, deterministic tests.
+
+## Migrating from `WebClient`
+
+The v0.13 flat `WebClient` class is deprecated. v0.14 introduces `MidenClient` with resource-based namespaces (`client.accounts`, `client.transactions`, …). See the [v0.13 → v0.14 Web SDK migration guide](../../../migration/07-client-changes.md) for the full delta.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/web-client/notes.md b/versioned_docs/version-0.14/builder/tools/clients/web-client/notes.md
new file mode 100644
index 00000000..fc44c3e4
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/web-client/notes.md
@@ -0,0 +1,145 @@
+---
+title: Notes
+sidebar_position: 5
+---
+
+# Notes
+
+Notes are the primary mechanism for transferring assets and data between accounts on Miden. This guide covers the `client.notes.*` surface: listing, lookup, import / export, private-note transport, and tags.
+
+## List received notes
+
+```typescript
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+// All input notes
+const all = await client.notes.list();
+
+// Filter by status
+const committed = await client.notes.list({ status: "committed" });
+const consumed = await client.notes.list({ status: "consumed" });
+const expected = await client.notes.list({ status: "expected" });
+const processing = await client.notes.list({ status: "processing" });
+const unverified = await client.notes.list({ status: "unverified" });
+
+// Filter by specific IDs
+const specific = await client.notes.list({ ids: [noteId1, noteId2] });
+
+for (const note of all) {
+ console.log(note.id().toString());
+}
+```
+
+Statuses:
+
+- `"committed"` — on-chain, consumable.
+- `"consumed"` — already spent.
+- `"expected"` — the client expects this note to arrive.
+- `"processing"` — mid-consume.
+- `"unverified"` — on-chain, awaiting local verification.
+
+## Retrieve a single note
+
+```typescript
+const note = await client.notes.get("0xnote...");
+if (note) {
+ console.log(note.id().toString());
+}
+```
+
+Returns `null` when the note isn't tracked locally.
+
+## List sent notes (output notes)
+
+```typescript
+const sent = await client.notes.listSent();
+
+// With status filter
+const committedSent = await client.notes.listSent({ status: "committed" });
+```
+
+## List consumable notes for an account
+
+```typescript
+const records = await client.notes.listAvailable({ account: wallet });
+
+for (const record of records) {
+ console.log("Note:", record.id().toString());
+}
+```
+
+Returns the input notes available for the specified account. Use this to drive "inbox" UIs.
+
+## Import and export
+
+```typescript
+import { MidenClient, NoteExportFormat } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+// Import from a previously exported NoteFile
+const noteId = await client.notes.import(noteFile);
+console.log("Imported:", noteId.toString());
+
+// Export — formats differ in completeness
+const idOnly = await client.notes.export("0xnote...", { format: NoteExportFormat.Id });
+const full = await client.notes.export("0xnote...", { format: NoteExportFormat.Full });
+const details = await client.notes.export("0xnote...", { format: NoteExportFormat.Details });
+```
+
+`NoteExportFormat`:
+
+- **`Id`** — just the note ID. Only works for public notes.
+- **`Full`** — complete note data plus inclusion proof. Requires the note to have an on-chain inclusion proof.
+- **`Details`** — note ID, metadata, and creation block.
+
+## Note transport (private notes)
+
+Private notes are delivered through the Miden note transport service. The sender emits a note with `type: "private"`; the recipient fetches it from the transport network.
+
+```typescript
+// Send a private note
+await client.notes.sendPrivate({
+ note: "0xnote...", // NoteInput
+ to: "mtst1recipient...", // recipient AccountRef
+});
+
+// Fetch — default is incremental (paginated)
+await client.notes.fetchPrivate();
+
+// Or fetch everything at once (initial-setup scenarios)
+await client.notes.fetchPrivate({ mode: "all" });
+
+// Now inspect the inbox
+const notes = await client.notes.list();
+console.log(`Fetched ${notes.length} notes`);
+```
+
+You need a note transport endpoint configured on the client — set `noteTransportUrl` in `ClientOptions`, or use a network factory (`createTestnet`, `createDevnet`) that preconfigures it.
+
+## Tags
+
+Tags are `u32` values that the sync process uses as a fuzzy filter to decide which notes to pull for your client. They come from three sources:
+
+1. **Account tags** — auto-registered for every account the client tracks.
+2. **Note tags** — auto-registered for notes the client expects.
+3. **User tags** — manually added via `client.tags.add()`.
+
+```typescript
+await client.tags.add(12345);
+
+const tags = await client.tags.list();
+console.log("Tracked tags:", tags);
+
+await client.tags.remove(12345);
+```
+
+Auto-generated tags (accounts, expected notes) cannot be removed — `remove()` only unregisters user-added tags. Use `NoteTag` helpers (exposed from the WASM module) to compute tag values from faucet IDs and account IDs.
+
+## Next
+
+- [Transactions](./transactions.md) — consume notes, send tokens, create output notes.
+- [Compile](./compile.md) — author note scripts in MASM.
+- [Sync and store](./sync.md) — the pipeline that feeds note state into your client.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/web-client/setup.md b/versioned_docs/version-0.14/builder/tools/clients/web-client/setup.md
new file mode 100644
index 00000000..4f1f3364
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/web-client/setup.md
@@ -0,0 +1,158 @@
+---
+title: Setup
+sidebar_position: 2
+---
+
+# Setting up the Web SDK
+
+## Install
+
+Add `@miden-sdk/miden-sdk` to your project.
+
+```bash
+npm install @miden-sdk/miden-sdk
+# or
+yarn add @miden-sdk/miden-sdk
+# or
+pnpm add @miden-sdk/miden-sdk
+```
+
+The SDK targets modern browsers (Chrome, Firefox, Safari, Edge) with WebAssembly and Web Worker support. It also runs under Node 20+ when the host provides those primitives.
+
+## Create a client
+
+Every operation goes through a `MidenClient` instance. Four factories cover the common cases:
+
+| Factory | Use for |
+| --- | --- |
+| `MidenClient.createTestnet()` | Miden testnet — RPC, prover, and note transport preconfigured |
+| `MidenClient.createDevnet()` | Miden devnet — same shape, devnet endpoints |
+| `MidenClient.createMock()` | Deterministic in-memory chain for tests — no network |
+| `MidenClient.create({ ... })` | Custom endpoints (localhost, self-hosted node, or any shorthand) |
+
+```typescript
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+// Miden testnet (most common for dApps under development)
+const client = await MidenClient.createTestnet();
+
+// Local node — "localhost" / "local" shorthand resolves to http://localhost:57291
+const local = await MidenClient.create({ rpcUrl: "localhost" });
+
+// Any custom URL
+const custom = await MidenClient.create({
+ rpcUrl: "https://my-node.example.com",
+});
+
+// Mock chain (see the Testing guide)
+const mock = await MidenClient.createMock();
+```
+
+All factories are async — the SDK has to load its WebAssembly module and spin up a Web Worker before the client is usable.
+
+## `ClientOptions` reference
+
+All four factories accept the same `ClientOptions` shape. The differences are in what each factory pre-fills before the options are applied.
+
+### Field reference
+
+| Option | Type | Description |
+| --- | --- | --- |
+| `rpcUrl` | `"testnet" \| "devnet" \| "localhost" \| "local" \| string` | Node RPC endpoint. Shorthands expand to the hosted Miden endpoints; any other string is treated as a raw URL. |
+| `noteTransportUrl` | `"testnet" \| "devnet" \| string` | Note transport service endpoint. Required for private-note `sendPrivate` / `fetchPrivate`. |
+| `proverUrl` | `"local" \| "devnet" \| "testnet" \| string` | Default prover for transactions. `"local"` runs in the browser; remote shorthands and URLs route to a remote / delegated prover. |
+| `autoSync` | `boolean` | When `true`, the client runs one sync pass before the promise resolves. |
+| `seed` | `string \| Uint8Array` | Seed for deterministic RNG. Strings are hashed to 32 bytes via SHA-256. |
+| `storeName` | `string` | Store isolation key (IndexedDB database name in browsers). Set this to keep multiple clients' data separate in the same origin. |
+| `keystore` | `{ getKey, insertKey, sign }` | External keystore callbacks. Leave unset to use the built-in keystore. |
+
+### Factory defaults
+
+Any option not passed falls back to the factory default, then to an SDK default.
+
+| Factory | `rpcUrl` | `proverUrl` | `noteTransportUrl` | `autoSync` |
+| --- | --- | --- | --- | --- |
+| `createTestnet(opts?)` | `"testnet"` | `"testnet"` | `"testnet"` | `true` |
+| `createDevnet(opts?)` | `"devnet"` | `"devnet"` | `"devnet"` | `true` |
+| `create(opts?)` **with** `rpcUrl` | your value | `"local"` | _none_ | `false` |
+| `create(opts?)` **without** `rpcUrl` | _delegates to `createTestnet(opts)`_ | ← | ← | ← |
+| `createMock(opts?)` | _(no network)_ | _(dummy proving)_ | _(in-memory)_ | _(manual)_ |
+
+`create()` without an `rpcUrl` is not a separate "custom" client — it forwards its options to `createTestnet()`. If you want a no-prover, no-autosync client against localhost, pass `rpcUrl: "localhost"` explicitly.
+
+### Testnet with an in-browser prover
+
+```typescript
+// Testnet — but prove locally in the browser instead of offloading
+const client = await MidenClient.createTestnet({ proverUrl: "local" });
+```
+
+### Keeping two isolated clients in the same origin
+
+```typescript
+const a = await MidenClient.createTestnet({ storeName: "wallet-a" });
+const b = await MidenClient.createTestnet({ storeName: "wallet-b" });
+```
+
+Each call creates its own IndexedDB database. The two wallets' state never crosses over.
+
+## Keystores and authentication
+
+The built-in keystore handles signing for the common flows: `client.accounts.create()` generates a Falcon key, persists it, and the client uses it automatically during `client.transactions.*` calls.
+
+When you need explicit control — for example when creating a contract account with a pre-derived seed, or wiring an external signer — build an `AuthSecretKey` directly:
+
+```typescript
+import { AuthSecretKey } from "@miden-sdk/miden-sdk";
+
+// Falcon key (the default auth scheme for regular wallets)
+const seed = crypto.getRandomValues(new Uint8Array(32));
+const auth = AuthSecretKey.rpoFalconWithRNG(seed);
+```
+
+The caller is responsible for retaining `auth` as long as the account is in use: the client holds a reference for signing, but the secret material only exists on the caller side until it is handed to the keystore.
+
+See [Accounts](./accounts.md) for full examples covering wallets, contracts, and faucets. For advanced setups — external signers, hardware wallets — the `keystore` option on `ClientOptions` wires the SDK to your own `sign`/`getKey`/`insertKey` callbacks.
+
+## Remote provers and per-transaction overrides
+
+Local proving in the browser is CPU-intensive for larger transactions. Override globally via `ClientOptions.proverUrl`, or per transaction via the `prover` field:
+
+```typescript
+// Globally: every transaction uses the remote prover by default
+const client = await MidenClient.create({
+ rpcUrl: "testnet",
+ proverUrl: "https://prover.example.com",
+});
+
+// Per-transaction: pass a TransactionProver instance
+await client.transactions.send({
+ account: wallet,
+ to: recipient,
+ token: faucet,
+ amount: 100n,
+ prover: customProver,
+});
+```
+
+See [Transactions](./transactions.md) for the full lifecycle.
+
+## Minimal example
+
+```typescript
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+async function demo() {
+ const client = await MidenClient.createTestnet();
+ await client.sync();
+
+ const wallet = await client.accounts.create();
+ console.log("Wallet:", wallet.id().toString());
+
+ client.terminate();
+}
+
+demo().catch(console.error);
+```
+
+For a testable, offline-friendly version of this pattern, see [Testing](./testing.md).
diff --git a/versioned_docs/version-0.14/builder/tools/clients/web-client/sync.md b/versioned_docs/version-0.14/builder/tools/clients/web-client/sync.md
new file mode 100644
index 00000000..2f32a89a
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/web-client/sync.md
@@ -0,0 +1,88 @@
+---
+title: Sync and store
+sidebar_position: 7
+---
+
+# Sync and store
+
+Every operation the Web SDK performs reads from — or writes to — a local store. This page covers how to keep that store in sync with the Miden network, and how to back up / restore the store itself.
+
+## `client.sync()`
+
+Pulls updates from the Miden node and applies them to the local store. Returns a `SyncSummary` describing what changed.
+
+```typescript
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+const summary = await client.sync();
+
+console.log("Block:", summary.blockNum());
+console.log("Committed notes:", summary.committedNotes().length);
+console.log("Consumed notes:", summary.consumedNotes().length);
+console.log("Committed txs:", summary.committedTransactions().length);
+console.log("Updated accounts:", summary.updatedAccounts().length);
+```
+
+`SyncSummary` accessors:
+
+- `blockNum(): number` — tip block the summary is based on.
+- `committedNotes(): NoteId[]` — notes committed in this sync window.
+- `consumedNotes(): NoteId[]` — notes consumed in this sync window.
+- `committedTransactions(): TransactionId[]` — transactions that were committed.
+- `updatedAccounts(): AccountId[]` — accounts whose on-chain state advanced.
+
+## Sync with timeout
+
+```typescript
+// 30-second timeout
+const summary = await client.sync({ timeout: 30_000 });
+```
+
+## Auto-sync on creation
+
+The network factories (`createTestnet`, `createDevnet`) default `autoSync: true`, so one sync runs before the client is returned. `create()` defaults to `false`; pass `autoSync: true` explicitly if you want the same behaviour for a custom endpoint:
+
+```typescript
+const client = await MidenClient.create({
+ rpcUrl: "localhost",
+ autoSync: true,
+});
+```
+
+## Current sync height
+
+Cheap check of the locally-known tip, no network call:
+
+```typescript
+const height: number = await client.getSyncHeight();
+console.log("Local tip:", height);
+```
+
+## Store backup and restore
+
+The SDK exposes two **standalone** functions (not methods on `MidenClient`) for snapshotting the entire store:
+
+```typescript
+import { exportStore, importStore } from "@miden-sdk/miden-sdk";
+
+// Dump the store identified by storeName into a JSON string.
+const storeName = client.storeIdentifier(); // or pass your own storeName
+const dump = await exportStore(storeName);
+
+// Later — on another device, or after a page refresh — restore it.
+await importStore(storeName, dump);
+```
+
+- `exportStore(storeName)` → `Promise` — returns a JSON dump of the IndexedDB store.
+- `importStore(storeName, dump)` → `Promise` — replaces all existing data in the target store with the dump.
+
+`importStore` is destructive. It overwrites the target store entirely — keep a backup if you need the previous state.
+
+These helpers are package-level exports, not methods on `MidenClient`, so they can run without an active client — for example in a worker that performs scheduled backups.
+
+## Next
+
+- [Testing](./testing.md) — running a deterministic, in-memory mock chain for tests.
+- [Transactions](./transactions.md) — how network state feeds back into transaction flows.
diff --git a/versioned_docs/version-0.14/builder/tools/clients/web-client/testing.md b/versioned_docs/version-0.14/builder/tools/clients/web-client/testing.md
new file mode 100644
index 00000000..0401fa15
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/web-client/testing.md
@@ -0,0 +1,132 @@
+---
+title: Testing
+sidebar_position: 8
+---
+
+# Testing with the mock client
+
+`MidenClient.createMock()` returns a client backed by a fully in-memory mock chain. It exposes the same resource API as the real client — `accounts`, `transactions`, `notes`, `tags`, `compile`, `keystore` — so tests for your application code don't need parallel mocking logic. It also adds a handful of mock-specific helpers for driving the chain manually.
+
+Use it for unit tests, CI, and offline development. It is orders of magnitude faster than hitting testnet, and it works on flaky networks.
+
+## Create
+
+```typescript
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createMock();
+```
+
+`createMock()` accepts `MockOptions`:
+
+| Option | Type | Description |
+| --- | --- | --- |
+| `seed` | `string \| Uint8Array` | RNG seed. Strings are hashed to 32 bytes via SHA-256. |
+| `serializedMockChain` | `Uint8Array` | Restore a previously serialized chain state. |
+| `serializedNoteTransport` | `Uint8Array` | Restore a previously serialized note-transport state. |
+
+## Basic flow
+
+The mock chain does not create blocks automatically — advance it with `proveBlock()` after each transaction batch. Between `proveBlock()` and subsequent reads, call `client.sync()` to hydrate the store.
+
+```typescript
+import { MidenClient, AccountType } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createMock();
+
+const wallet = await client.accounts.create();
+const faucet = await client.accounts.create({
+ type: AccountType.FungibleFaucet,
+ symbol: "TEST",
+ decimals: 8,
+ maxSupply: 10_000_000n,
+});
+
+client.proveBlock();
+await client.sync();
+
+await client.transactions.mint({
+ account: faucet,
+ to: wallet,
+ amount: 1000n,
+});
+
+client.proveBlock();
+await client.sync();
+
+const result = await client.transactions.consumeAll({ account: wallet });
+console.log(`Consumed ${result.consumed} notes`);
+
+client.proveBlock();
+await client.sync();
+
+const balance = await client.accounts.getBalance(wallet, faucet);
+console.log(`Balance: ${balance}`); // 1000n
+```
+
+## Dummy proving
+
+Transaction proving is automatically replaced with `LocalTransactionProver.prove_dummy()` on mock clients. You don't configure anything — any transaction method that would normally prove is instead given a dummy proof that the mock chain accepts. Key consequences:
+
+- Near-instant transactions, regardless of script complexity.
+- No prover configuration required — `proverUrl` is ignored on mock clients.
+- Dummy proofs are **not** valid on real chains.
+
+## Mock-only helpers
+
+The `MidenClient` class exposes a few methods that only make sense on mock clients. Guard them behind `usesMockChain()` if your code needs to work against both mock and real clients.
+
+```typescript
+if (client.usesMockChain()) {
+ client.proveBlock();
+}
+
+const chainBytes = client.serializeMockChain();
+const transportBytes = client.serializeMockNoteTransportNode();
+```
+
+| Method | Purpose |
+| --- | --- |
+| `client.proveBlock()` | Advance the mock chain by one block. |
+| `client.usesMockChain()` | `true` on mock clients; `false` on real clients. |
+| `client.serializeMockChain()` | Snapshot mock chain state as bytes. |
+| `client.serializeMockNoteTransportNode()` | Snapshot mock note transport state as bytes. |
+
+## Snapshot and restore
+
+Serialize the state of a running mock client, then restore it in a new client. Useful for long-running test scaffolds that want a shared starting state:
+
+```typescript
+// Setup: create client, advance chain, fund accounts, etc.
+const setup = await MidenClient.createMock();
+// ...
+const chainState = setup.serializeMockChain();
+const transportState = setup.serializeMockNoteTransportNode();
+setup.terminate();
+
+// In a test, restore from the snapshot
+const client = await MidenClient.createMock({
+ serializedMockChain: chainState,
+ serializedNoteTransport: transportState,
+});
+```
+
+## Private-note transport
+
+The mock client ships its own in-memory note transport. The same `sendPrivate` / `fetchPrivate` flow works:
+
+```typescript
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createMock();
+
+await client.notes.sendPrivate({
+ note: "0xnote...",
+ to: "mtst1recipient...",
+});
+
+await client.notes.fetchPrivate();
+
+const notes = await client.notes.list();
+console.log(`Received ${notes.length} notes`);
+```
diff --git a/versioned_docs/version-0.14/builder/tools/clients/web-client/transactions.md b/versioned_docs/version-0.14/builder/tools/clients/web-client/transactions.md
new file mode 100644
index 00000000..bde5520a
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/clients/web-client/transactions.md
@@ -0,0 +1,379 @@
+---
+title: Transactions
+sidebar_position: 4
+---
+
+# Transactions
+
+`client.transactions` is the resource namespace for everything that mutates on-chain state: sending, minting, consuming, swapping, running custom scripts, and inspecting history. Every mutation method handles the full lifecycle — execute, prove, submit — in one call.
+
+## Simplified operations
+
+### `send`
+
+Creates a pay-to-ID note that transfers tokens from one account to another.
+
+```typescript
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+const { txId } = await client.transactions.send({
+ account: senderWallet,
+ to: recipientWallet,
+ token: faucet,
+ amount: 100n,
+ type: "private", // "public" or "private" — default "public"
+ reclaimAfter: 100, // optional: block number after which sender can reclaim
+ timelockUntil: 90, // optional: block number until which note is timelocked
+});
+console.log("Send tx:", txId.toHex());
+```
+
+Set `returnNote: true` to receive the created `Note` in the result (useful when you need the note body — for QR delivery, for example):
+
+```typescript
+const { txId, note } = await client.transactions.send({
+ account: senderWallet,
+ to: recipientWallet,
+ token: faucet,
+ amount: 100n,
+ returnNote: true,
+});
+// `note` is guaranteed non-null when returnNote is true
+```
+
+`reclaimAfter` and `timelockUntil` are **block numbers**, not wall-clock times.
+
+:::note
+The `returnNote: true` branch is a separate overload: it does not accept `reclaimAfter` or `timelockUntil`. If you need either of those, use the default branch (without `returnNote`) and retrieve the note through `client.notes.listSent()` instead.
+:::
+
+### `mint`
+
+```typescript
+const { txId } = await client.transactions.mint({
+ account: faucet, // the faucet account
+ to: wallet, // recipient
+ amount: 1000n,
+ type: "private", // optional, default "public"
+});
+console.log("Mint tx:", txId.toHex());
+```
+
+### `consume`
+
+```typescript
+// Consume one or more input notes
+const { txId } = await client.transactions.consume({
+ account: wallet,
+ notes: [noteId1, noteId2], // note references — hex strings, NoteIds, records, or Notes
+});
+
+// Single note also accepted directly, no array needed
+await client.transactions.consume({
+ account: wallet,
+ notes: noteId1,
+});
+```
+
+### `consumeAll`
+
+Consumes every available note for an account, up to an optional limit. Useful for quickly draining an inbox.
+
+```typescript
+const result = await client.transactions.consumeAll({ account: wallet });
+console.log(`Consumed ${result.consumed}, ${result.remaining} remaining`);
+if (result.txId) {
+ console.log("Tx:", result.txId.toHex());
+}
+
+// Cap the number of notes consumed in one transaction
+await client.transactions.consumeAll({ account: wallet, maxNotes: 5 });
+```
+
+`result.remaining > 0` signals pagination — call `consumeAll` again to drain the rest.
+
+### `swap`
+
+Atomic swap between two assets.
+
+```typescript
+const { txId } = await client.transactions.swap({
+ account: wallet,
+ offer: { token: faucetA, amount: 100n },
+ request: { token: faucetB, amount: 200n },
+ type: "public", // optional — visibility of the offer note
+ paybackType: "private", // optional — visibility of the payback note
+});
+```
+
+## Waiting for confirmation
+
+All simplified operations return as soon as the transaction is submitted to the mempool. To block until the network commits the transaction, use `waitFor`:
+
+```typescript
+const { txId } = await client.transactions.mint({
+ account: faucet,
+ to: wallet,
+ amount: 1000n,
+});
+
+// Default: 60s timeout, 5s polling interval
+await client.transactions.waitFor(txId.toHex());
+
+// Custom polling
+await client.transactions.waitFor(txId.toHex(), {
+ timeout: 120_000, // 2 minutes — set to 0 to poll indefinitely
+ interval: 3_000,
+ onProgress: (status) => {
+ console.log(`Status: ${status}`); // "pending" | "submitted" | "committed"
+ },
+});
+```
+
+`waitFor` throws on rejection or timeout.
+
+## Preview (dry run)
+
+Run any of the simplified operations as a dry-run to inspect its effects without submitting to the network. The return type is a `TransactionSummary`.
+
+```typescript
+const summary = await client.transactions.preview({
+ operation: "send",
+ account: wallet,
+ to: recipient,
+ token: faucet,
+ amount: 100n,
+});
+```
+
+`operation` accepts `"send"`, `"mint"`, `"consume"`, and `"swap"`.
+
+## Custom transaction scripts (`execute`)
+
+When the simplified operations aren't enough — for example to call a procedure on a contract account — compile a transaction script with [`client.compile.txScript()`](./compile.md) and run it through `execute`:
+
+```typescript
+import { MidenClient } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+const script = await client.compile.txScript({
+ code: `
+ use external_contract::counter_contract
+ begin
+ call.counter_contract::increment_count
+ end
+ `,
+ libraries: [
+ {
+ namespace: "external_contract::counter_contract",
+ code: counterContractCode,
+ },
+ ],
+});
+
+const { txId } = await client.transactions.execute({
+ account: contractAccount.id(),
+ script,
+});
+
+console.log("Tx:", txId.toHex());
+```
+
+### Foreign procedure invocation (FPI)
+
+Pass `foreignAccounts` to read state from other contracts during execution:
+
+```typescript
+import { MidenClient, StorageSlot } from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+// Compile the foreign contract to get a procedure hash
+const counterComponent = await client.compile.component({
+ code: counterContractCode,
+ slots: [StorageSlot.emptyValue("miden::tutorials::counter")],
+});
+const getCountHash = counterComponent.getProcedureHash("get_count");
+
+const script = await client.compile.txScript({
+ code: `
+ use external_contract::count_reader_contract
+ use miden::core::sys
+ begin
+ push.${getCountHash}
+ push.${counterAccount.id().suffix()}
+ push.${counterAccount.id().prefix()}
+ call.count_reader_contract::copy_count
+ exec.sys::truncate_stack
+ end
+ `,
+ libraries: [
+ { namespace: "external_contract::count_reader_contract", code: countReaderCode },
+ ],
+});
+
+const { txId } = await client.transactions.execute({
+ account: countReaderAccount.id(),
+ script,
+ foreignAccounts: [
+ // Bare reference — client fetches storage requirements automatically
+ counterAccount.id(),
+ // Or with explicit storage requirements:
+ // { id: counterAccount.id(), storage: requirements },
+ ],
+});
+```
+
+## View calls (`executeProgram`)
+
+`executeProgram` runs a transaction script locally, returning the resulting stack without submitting or proving anything. Use it to read on-chain state — similar to `eth_call` in Ethereum.
+
+```typescript
+const script = await client.compile.txScript({
+ code: `
+ use external_contract::counter_contract
+ begin
+ call.counter_contract::get_count
+ end
+ `,
+ libraries: [
+ { namespace: "external_contract::counter_contract", code: counterContractCode },
+ ],
+});
+
+const stack = await client.transactions.executeProgram({
+ account: contractAccount.id(),
+ script,
+ foreignAccounts: [counterAccount.id()],
+});
+
+// stack is a FeltArray — 16 elements representing the final stack
+const count = stack.get(0).asInt();
+console.log("Count:", count);
+```
+
+Options:
+
+| Option | Type | Description |
+| --- | --- | --- |
+| `account` | `AccountRef` | Account to execute the script against. |
+| `script` | `TransactionScript` | Compiled script. |
+| `adviceInputs` | `AdviceInputs?` | Advice inputs for the VM. Defaults to empty. |
+| `foreignAccounts` | `(AccountRef \| { id, storage? })[]?` | Foreign accounts for FPI reads. |
+
+## Manual `TransactionRequest`
+
+For full control over note inputs and outputs — e.g. emitting multiple custom output notes from one transaction — build a `TransactionRequest` yourself and pass it to `submit`.
+
+The builder accepts WASM array classes (`NoteArray`, `NoteDetailsAndTagArray`, `NoteRecipientArray`) rather than plain TS arrays. This is unusual but required by the underlying wasm-bindgen interface: the array types take ownership of their elements and are explicitly disposable.
+
+```typescript
+import {
+ MidenClient,
+ TransactionRequestBuilder,
+ NoteArray,
+} from "@miden-sdk/miden-sdk";
+
+const client = await MidenClient.createTestnet();
+
+// Populate a NoteArray with your output notes
+const ownOutputs = new NoteArray();
+for (const note of outputNotes) {
+ ownOutputs.push(note);
+}
+
+const request = new TransactionRequestBuilder()
+ .withCustomScript(transactionScript)
+ .withOwnOutputNotes(ownOutputs)
+ .build();
+
+const { txId } = await client.transactions.submit(wallet, request);
+console.log("Tx:", txId.toHex());
+```
+
+Expected-note hints are also available:
+
+- `withExpectedFutureNotes(NoteDetailsAndTagArray)` — notes the transaction should produce but won't emit directly (e.g. for later pickup).
+- `withExpectedOutputRecipients(NoteRecipientArray)` — recipients for expected output notes.
+
+### Setting an expiration
+
+```typescript
+const request = new TransactionRequestBuilder()
+ .withOwnOutputNotes(ownOutputs)
+ .withExpirationDelta(10) // expires 10 blocks after submission
+ .build();
+
+await client.transactions.submit(wallet, request);
+```
+
+`withExpirationDelta()` composes with `withCustomScript()` — the builder applies the expiration at the request level regardless of how the script was provided. You can still set expiration inside the script itself when you need a different rule; the two paths don't interact.
+
+## Remote proving
+
+Local proving is CPU-intensive. Offload globally via `ClientOptions.proverUrl`, or per-transaction via the `prover` field:
+
+```typescript
+// Global: every transaction uses the remote prover
+const client = await MidenClient.create({
+ rpcUrl: "testnet",
+ proverUrl: "https://prover.example.com",
+});
+
+// Per-transaction: pass a TransactionProver instance
+await client.transactions.send({
+ account: wallet,
+ to: recipient,
+ token: faucet,
+ amount: 100n,
+ prover: customProver,
+});
+```
+
+## History
+
+Query past transactions through `client.transactions.list()`.
+
+```typescript
+// All transactions
+const all = await client.transactions.list();
+
+// Only uncommitted
+const uncommitted = await client.transactions.list({ status: "uncommitted" });
+
+// By ID
+const specific = await client.transactions.list({ ids: [txId1, txId2] });
+
+// By expiration
+const expired = await client.transactions.list({ expiredBefore: 1000 });
+```
+
+Each record exposes:
+
+```typescript
+for (const tx of all) {
+ tx.id().toHex();
+ tx.accountId().toString(); // AccountId.toString() returns canonical hex
+ tx.blockNum().toString();
+
+ const status = tx.transactionStatus();
+ if (status.isPending()) console.log("Pending");
+ if (status.isCommitted()) console.log("Committed at", status.getBlockNum(), status.getCommitTimestamp());
+ if (status.isDiscarded()) console.log("Discarded");
+
+ tx.initAccountState().toHex();
+ tx.finalAccountState().toHex();
+
+ tx.inputNoteNullifiers().map((n) => n.toHex());
+ tx.outputNotes().toString();
+}
+```
+
+## Next
+
+- [Notes](./notes.md) — list, import, export, private-note transport.
+- [Compile](./compile.md) — MASM for account components, transaction scripts, note scripts.
+- [Testing](./testing.md) — drive a fully in-memory mock chain for fast, deterministic tests.
diff --git a/versioned_docs/version-0.14/builder/tools/index.md b/versioned_docs/version-0.14/builder/tools/index.md
new file mode 100644
index 00000000..5e0d4a94
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/index.md
@@ -0,0 +1,34 @@
+---
+title: Tools
+description: "Developer tools for building on and interacting with the Miden network — clients, playground, and the live network surface."
+pagination_prev: null
+---
+
+# Tools
+
+Developer tools for building on and interacting with the Miden network. Use the client SDKs inside your app, the Playground to prototype contracts in-browser, and the Network page to find the live testnet endpoints (status, explorer, RPC, faucet, remote prover).
+
+## Clients
+
+
+
+ Full-featured Rust library for Miden rollup integration — accounts, transactions, notes, proving.
+
+
+ Browser-based client for managing accounts and transactions from a web app.
+
+
+ Hooks and components for Miden dApps.
+
+
+
+## Environments
+
+
+
+ Interactive environment for writing and testing Miden Assembly programs.
+
+
+ Live Miden testnet endpoints — status, block explorer (MidenScan), RPC, faucet, remote prover.
+
+
diff --git a/versioned_docs/version-0.14/builder/tools/network.md b/versioned_docs/version-0.14/builder/tools/network.md
new file mode 100644
index 00000000..e0acb99f
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/network.md
@@ -0,0 +1,70 @@
+---
+title: Network
+sidebar_position: 4
+description: "Miden testnet endpoints — status, explorer, RPC, faucet, remote prover, and other public services."
+---
+
+# Network
+
+Public Miden testnet services — explorer, RPC, faucet, remote prover, and status. The canonical live inventory lives at [status.testnet.miden.io](https://status.testnet.miden.io/); the page below is an editorial overview that won't go stale as new endpoints ship.
+
+
+The same services exist on devnet under the `devnet` subdomain — e.g., `status.devnet.miden.io`, `devnet.midenscan.com`. Swap `testnet` → `devnet` in any URL on this page to point at devnet instead.
+
+
+
+
+ Dashboard showing every public Miden testnet endpoint with live health checks. Bookmark this when something looks off — it's the source of truth for "is the network up."
+
+
+ Search by account ID, transaction ID, note ID, or block height. The verification surface referenced throughout the tutorials.
+
+
+
+## Developer endpoints
+
+
+
+ gRPC endpoint the node exposes for submitting proven transactions and querying account / note / block state. Clients connect here by default; see [status.testnet.miden.io](https://status.testnet.miden.io/) for the current URL.
+
+
+ Dispenses test assets (testnet MID and fungible test tokens) to the account ID you specify. Check status for the current faucet endpoint.
+
+
+ Off-loads proof generation to a hosted prover when the client doesn't have the compute for client-side proving. Opt in via the client's `proverUrl` option.
+
+
+ Same as the top-right card — included here so the developer-endpoint view is complete.
+
+
+
+
+Hard-coding testnet URLs in client configs is fine for demos, but the Miden ops team moves endpoints as new nodes come online. For anything production-ish, keep your configuration loading the current URLs from [status.testnet.miden.io](https://status.testnet.miden.io/) or the client's default (which tracks the canonical testnet host).
+
+
+## Typical flow
+
+
+
+**Install the toolchain** — `midenup` pulls the client, CLI, and compiler in one step. See [Installation](../get-started/setup/installation).
+
+**Point the client at testnet** — default config targets the testnet RPC, so `miden new` projects work out of the box.
+
+**Top up with the faucet** — mint testnet assets to your account ID before attempting transactions.
+
+**Submit + verify** — send a proven transaction via the RPC; watch the account and transaction IDs land on [MidenScan](https://testnet.midenscan.com).
+
+**Offload proving if needed** — the remote prover URL is configurable if client-side proving isn't feasible (mobile, low-power clients).
+
+
+
+## Related
+
+
+
+ End-to-end walkthrough — client → RPC → MidenScan verification.
+
+
+ How the node receives transactions, aggregates batches, and produces blocks.
+
+
diff --git a/versioned_docs/version-0.14/builder/tools/playground.md b/versioned_docs/version-0.14/builder/tools/playground.md
new file mode 100644
index 00000000..2d53bfa6
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tools/playground.md
@@ -0,0 +1,50 @@
+---
+title: Playground
+sidebar_position: 3
+description: "Browser-based environment to write, compile, and execute Miden Assembly programs — no local tooling required."
+---
+
+# Miden Playground
+
+An interactive browser environment for writing, compiling, and executing Miden Assembly (MASM) programs. No installation required — prototype account code, test note scripts, and experiment with VM instructions straight from a URL.
+
+
+
+ Launch the browser IDE and start writing MASM immediately.
+
+
+ Instruction set, stack semantics, chiplets, and assembler behaviour.
+
+
+
+## What you can do
+
+
+
+ Syntax-highlighted editor with inline error reporting for the Miden assembler.
+
+
+ Run programs against the Miden VM in-browser and inspect the resulting stack and memory.
+
+
+ Shareable URLs with embedded code for reproducing bugs or teaching examples.
+
+
+
+
+The Playground shines for learning MASM and for quick prototyping. For anything bigger than a snippet — components, storage, note dispatch, transaction flows — move to a `miden new` Rust project locally. See [your first smart contract](../get-started/your-first-smart-contract) for the handoff.
+
+
+## Related
+
+
+
+ Install the toolchain and build + deploy a counter contract in Rust.
+
+
+ New `word(...)` / `event(...)` constants, `std::math::u128`, and other MASM-level deltas.
+
+
+ Accounts, notes, transactions, and the Rust SDK surface.
+
+
diff --git a/versioned_docs/version-0.14/builder/tutorials/_category_.json b/versioned_docs/version-0.14/builder/tutorials/_category_.json
new file mode 100644
index 00000000..e3af22eb
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Tutorials",
+ "position": 1
+}
diff --git a/versioned_docs/version-0.14/builder/tutorials/components/CodeSdkTabs.module.css b/versioned_docs/version-0.14/builder/tutorials/components/CodeSdkTabs.module.css
new file mode 100644
index 00000000..391d6d32
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/components/CodeSdkTabs.module.css
@@ -0,0 +1,82 @@
+/* CodeSdkTabs Component Styles */
+
+.codeContainer {
+ margin: 1rem 0;
+ border: 1px solid var(--ifm-color-emphasis-300);
+ border-radius: var(--ifm-global-radius);
+ overflow: hidden;
+ background: var(--ifm-background-color);
+}
+
+.tabContainer {
+ background: var(--ifm-color-emphasis-100);
+ border-bottom: 1px solid var(--ifm-color-emphasis-300);
+}
+
+.tabButtons {
+ display: flex;
+ gap: 0;
+}
+
+.tabButton {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.75rem 1rem;
+ border: none;
+ background: transparent;
+ color: var(--ifm-color-content-secondary);
+ cursor: pointer;
+ font-size: 0.875rem;
+ font-weight: 500;
+ transition: all 0.2s ease;
+ border-bottom: 2px solid transparent;
+}
+
+.tabButton:hover {
+ color: var(--ifm-color-primary);
+ background: var(--ifm-color-emphasis-200);
+}
+
+.tabButton.active {
+ color: var(--ifm-color-primary);
+ background: var(--ifm-color-emphasis-200);
+ border-bottom: 2px solid var(--ifm-color-primary);
+}
+
+.codeSection {
+ position: relative;
+ margin: 0;
+}
+
+.codeSection pre {
+ margin: 0;
+ border-radius: 0;
+ border: none;
+}
+
+/* Remove any extra bottom spacing from the theme code block */
+.codeSection :global(.theme-code-block) {
+ margin-bottom: 0 !important;
+}
+
+.outputSection {
+ border-top: 1px solid var(--ifm-color-emphasis-300);
+ background: var(--ifm-color-emphasis-50);
+}
+
+.outputHeader {
+ padding: 0.5rem 1rem;
+ font-size: 0.875rem;
+ font-weight: 600;
+ color: var(--ifm-color-content-secondary);
+ background: var(--ifm-color-emphasis-100);
+ border-bottom: 1px solid var(--ifm-color-emphasis-200);
+}
+
+.outputSection pre {
+ margin: 0;
+ border-radius: 0;
+ border: none;
+ background: var(--ifm-color-emphasis-50) !important;
+}
diff --git a/versioned_docs/version-0.14/builder/tutorials/components/CodeSdkTabs.tsx b/versioned_docs/version-0.14/builder/tutorials/components/CodeSdkTabs.tsx
new file mode 100644
index 00000000..48b36ba2
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/components/CodeSdkTabs.tsx
@@ -0,0 +1,138 @@
+import React, { useState } from "react";
+import CodeBlock from "@theme/CodeBlock";
+import styles from "./CodeSdkTabs.module.css";
+
+interface CodeExample {
+ react?: {
+ code: string;
+ output?: string;
+ };
+ typescript?: {
+ code: string;
+ output?: string;
+ };
+}
+
+interface CodeSdkTabsProps {
+ example: CodeExample;
+ reactFilename?: string;
+ tsFilename?: string;
+}
+
+// Dot-indentation convention for CodeSdkTabs
+// ─────────────────────────────────────────────
+// MDX/webpack strips leading whitespace from template literals inside JSX props.
+// To preserve indentation in code snippets, use leading dots in the markdown
+// source. Each dot represents one indent level (2 spaces).
+//
+// Example in a .md file:
+// typescript: { code: `export function foo() {
+// .const x = 1;
+// .if (x) {
+// ..console.log(x);
+// .}
+// }` }
+//
+// Renders as:
+// export function foo() {
+// const x = 1;
+// if (x) {
+// console.log(x);
+// }
+// }
+//
+// Rules:
+// 0 dots → top-level declarations (export, import, closing braces)
+// 1 dot → first level inside a function/block body
+// 2 dots → second level (nested blocks, function call arguments)
+// 3+ dots → deeper nesting
+function preserveIndent(code: string): string {
+ return code.replace(/^(\.+)/gm, (match) => ' '.repeat(match.length));
+}
+
+export default function CodeSdkTabs({
+ example,
+ reactFilename = "index.tsx",
+ tsFilename = "index.ts",
+}: CodeSdkTabsProps): JSX.Element {
+ const [activeTab, setActiveTab] = useState<"react" | "typescript">(
+ example.react ? "react" : "typescript"
+ );
+
+ const hasReact = !!example.react;
+ const hasTypeScript = !!example.typescript;
+
+ // Infer syntax language from filename extension (.tsx → tsx, .ts → ts)
+ const langFor = (filename: string, fallback: string) =>
+ filename.endsWith(".tsx") ? "tsx" : filename.endsWith(".ts") ? "ts" : fallback;
+
+ // Don't show tabs if there's only one language
+ if (!hasReact || !hasTypeScript) {
+ const singleLang = hasReact ? "react" : "typescript";
+ const singleExample = example[singleLang];
+ const filename = singleLang === "react" ? reactFilename : tsFilename;
+
+ return (
+
+
+
+ {preserveIndent(singleExample!.code)}
+
+
+ {singleExample!.output && (
+
+
Output
+
{singleExample.output}
+
+ )}
+
+ );
+ }
+
+ const currentExample = example[activeTab];
+ const activeFilename = activeTab === "react" ? reactFilename : tsFilename;
+
+ return (
+
+
+
+ setActiveTab("react")}
+ >
+ React
+
+ setActiveTab("typescript")}
+ >
+ TypeScript
+
+
+
+
+
+
+ {preserveIndent(currentExample!.code)}
+
+
+
+ {currentExample!.output && (
+
+
Output
+
{currentExample.output}
+
+ )}
+
+ );
+}
diff --git a/versioned_docs/version-0.14/builder/tutorials/components/index.ts b/versioned_docs/version-0.14/builder/tutorials/components/index.ts
new file mode 100644
index 00000000..33f91fd9
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/components/index.ts
@@ -0,0 +1 @@
+export { default as CodeSdkTabs } from './CodeSdkTabs';
diff --git a/versioned_docs/version-0.14/builder/tutorials/helpers/_category_.json b/versioned_docs/version-0.14/builder/tutorials/helpers/_category_.json
new file mode 100644
index 00000000..b6d47724
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/helpers/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Guides",
+ "position": 4
+}
diff --git a/versioned_docs/version-0.14/builder/tutorials/helpers/debugging.md b/versioned_docs/version-0.14/builder/tutorials/helpers/debugging.md
new file mode 100644
index 00000000..ed50001d
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/helpers/debugging.md
@@ -0,0 +1,59 @@
+---
+sidebar_position: 2
+title: "Debugging Guide"
+description: "Learn how to debug Miden Rust contracts using assert_eq and cycle counts."
+---
+
+# Debugging Guide
+
+Miden contracts don't support traditional debugging tools like console.log or print statements. Instead, you can use `assert_eq` statements to check values during execution.
+
+## Using assert_eq
+
+The `assert_eq` function compares two `Felt` values and fails if they differ:
+
+```rust
+use miden::*;
+
+// Check if a value equals an expected value
+assert_eq(actual_value, expected_value);
+```
+
+:::note
+`assert_eq` is a **function**, not a macro. Use `assert_eq(a, b)` without the exclamation mark.
+:::
+
+## Debugging with Cycle Counts
+
+When your code fails, the error output includes a **cycle count** indicating where execution stopped. You can use this to narrow down problems:
+
+1. **Note the cycle count** when your code fails
+2. **Place an `assert_eq`** before the code you suspect is failing
+3. **Run again** and check the result:
+ - If the assertion fails at an **earlier cycle count**: the value you're checking is wrong
+ - If the assertion passes and fails at the **same cycle count**: the value is correct, the problem is elsewhere
+
+### Example
+
+```rust
+pub fn withdraw(&mut self, depositor: AccountId, amount: Felt) {
+ let balance = self.get_balance(depositor);
+
+ // Debug: Check if balance is what you expect
+ assert_eq(balance, felt!(1000));
+
+ // If the above passes, the problem is below this line
+ // If it fails, the balance isn't what you expected
+
+ let new_balance = balance - amount;
+ self.balances.set(key, new_balance);
+}
+```
+
+By moving the `assert_eq` statement around, you can isolate which value is incorrect.
+
+## Limitations
+
+- No console.log or print debugging in contract code
+- `assert_eq` only works with `Felt` values
+- This is currently the primary debugging technique available
diff --git a/versioned_docs/version-0.14/builder/tutorials/helpers/pitfalls.md b/versioned_docs/version-0.14/builder/tutorials/helpers/pitfalls.md
new file mode 100644
index 00000000..23ae65c7
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/helpers/pitfalls.md
@@ -0,0 +1,505 @@
+---
+sidebar_position: 3
+title: "Common Pitfalls"
+description: "Reference guide for known issues, limitations, and workarounds when developing with the Miden Rust compiler."
+---
+
+# Common Pitfalls
+
+This reference documents known issues and limitations when developing with the Miden Rust compiler, along with recommended workarounds.
+
+## Felt Comparison Operators
+
+### Problem
+
+Direct comparison operators (`<`, `>`, `<=`, `>=`) on `Felt` values produce incorrect results.
+
+```rust
+// WRONG: This does NOT work correctly
+let a = Felt::new(100);
+let b = Felt::new(200);
+if a < b { // May produce unexpected results!
+ // ...
+}
+```
+
+### Solution
+
+Always convert Felt values to `u64` before comparing:
+
+```rust
+// CORRECT: Convert to u64 first
+let a = Felt::new(100);
+let b = Felt::new(200);
+if a.as_u64() < b.as_u64() {
+ // Works correctly
+}
+```
+
+### Example from Bank Contract
+
+```rust title="contracts/bank-account/src/lib.rs"
+// Validating deposit amount
+let amount = asset.unwrap_fungible().amount().as_u64();
+
+// Use u64 comparison
+assert!(
+ amount <= MAX_DEPOSIT_AMOUNT, // MAX_DEPOSIT_AMOUNT is u64
+ "Deposit exceeds maximum"
+);
+```
+
+:::warning Always Use .as_u64()
+Any time you compare Felt values, convert them first. This applies to:
+- Amount comparisons
+- Balance checks
+- Index comparisons
+- Any numeric ordering
+:::
+
+---
+
+## Stack Limit (16 Elements)
+
+### Problem
+
+The Miden VM stack only allows direct access to the first 16 elements. Complex functions with many local variables trigger this error:
+
+```
+invalid stack index: only the first 16 elements on the stack are directly accessible
+```
+
+This may also appear as:
+```
+values not found in advice provider
+```
+
+### Solution
+
+**1. Reduce local variables:**
+
+```rust
+// WRONG: Too many local variables
+fn complex_operation(&mut self) {
+ let a = self.get_a();
+ let b = self.get_b();
+ let c = self.get_c();
+ let d = self.get_d();
+ let e = self.get_e();
+ let f = self.get_f();
+ // ... more variables cause stack overflow
+}
+
+// CORRECT: Use values directly or minimize locals
+fn complex_operation(&mut self) {
+ // Process in smaller batches
+ let result_ab = self.process(self.get_a(), self.get_b());
+ let result_cd = self.process(self.get_c(), self.get_d());
+ self.finalize(result_ab, result_cd);
+}
+```
+
+**2. Break into smaller functions:**
+
+```rust
+// WRONG: One large function
+fn do_everything(&mut self, a: Word, b: Word, c: Word) {
+ // Many operations touching all parameters...
+}
+
+// CORRECT: Split into stages
+fn stage_one(&mut self, a: Word) -> Felt {
+ // Process a
+}
+
+fn stage_two(&mut self, b: Word, result: Felt) -> Felt {
+ // Process b with result from stage one
+}
+
+fn stage_three(&mut self, c: Word, result: Felt) {
+ // Final processing
+}
+```
+
+**3. Process iteratively:**
+
+```rust
+// CORRECT: Process one at a time
+for asset in assets {
+ self.process_single_asset(asset);
+}
+```
+
+---
+
+## Function Argument Limit (4 Words)
+
+### Problem
+
+Miden functions can receive at most 4 Words (16 Felts) as arguments:
+
+```
+error: expected at most 4 words of arguments
+```
+
+```rust
+// WRONG: Too many arguments
+fn process(
+ &mut self,
+ depositor: AccountId, // ~1 Word
+ asset: Asset, // 1 Word
+ serial_num: Word, // 1 Word
+ tag: Felt, // 1 Felt
+ note_type: Felt, // 1 Felt
+ extra_data: Word, // 1 Word - EXCEEDS LIMIT!
+) {
+ // ...
+}
+```
+
+### Solution
+
+**1. Make sure to only pass 4 Words to functions:**
+
+```rust
+// CORRECT: Only pass 4 Words
+fn process(
+ &mut self,
+ depositor: AccountId, // ~1 Word
+ asset: Asset, // 1 Word
+ serial_num: Word, // 1 Word
+ params: Word, // [tag, note_type, 0, 0] - 1 Word
+) {
+ let tag = params[0];
+ let note_type = params[1];
+ // ...
+}
+```
+
+**2. Use note storage for passing data:**
+
+For note scripts, pass complex data via `active_note::get_storage()`:
+
+```rust
+#[note]
+struct MyNote;
+
+#[note]
+impl MyNote {
+ #[note_script]
+ fn run(self, _arg: Word) {
+ let storage = active_note::get_storage();
+ // Storage can hold many Felts without function argument limits
+ let param1 = storage[0];
+ let param2 = storage[1];
+ // ... access up to the full storage capacity
+ }
+}
+```
+
+**3. Store data first, reference by key:**
+
+```rust
+// Store complex data in storage
+fn store_config(&mut self, key: Word, config_data: Word) {
+ self.configs.set(key, config_data);
+}
+
+// Reference by key in other operations
+fn process_with_config(&mut self, key: Word) {
+ let config = self.configs.get(&key);
+ // Use config...
+}
+```
+
+---
+
+## Array Ordering (Rust/MASM Reversal)
+
+### Problem
+
+Arrays passed from Rust to the Miden VM are received in **reversed order**.
+
+```rust
+// In Rust, you define:
+let word = Word::from([a, b, c, d]);
+
+// In MASM, this becomes: [d, c, b, a]
+```
+
+### Solution
+
+Be aware of this when:
+- Constructing storage keys
+- Parsing note inputs
+- Working with asset data
+
+**Example: Storage Key Construction**
+
+```rust
+// Balance key format in Rust
+let key = Word::from([
+ depositor.prefix().as_felt(), // Position 0 in Rust
+ depositor.suffix(), // Position 1
+ faucet.prefix().as_felt(), // Position 2
+ faucet.suffix(), // Position 3
+]);
+
+// When the VM processes this, it sees:
+// [faucet.suffix, faucet.prefix, depositor.suffix, depositor.prefix]
+```
+
+**Example: Asset Structure**
+
+```rust
+// Asset Word layout (Rust perspective)
+// [amount, 0, faucet_suffix, faucet_prefix]
+
+let asset_word = Word::from([
+ Felt::new(amount), // [0] amount
+ Felt::new(0), // [1] padding
+ faucet.id().suffix(), // [2] faucet suffix
+ faucet.id().prefix().as_felt(), // [3] faucet prefix
+]);
+```
+
+:::tip Consistency is Key
+The reversal doesn't matter as long as you're **consistent**. Always construct and parse arrays the same way throughout your codebase.
+:::
+
+---
+
+## Felt Arithmetic Underflow/Overflow
+
+### Problem
+
+Miden uses field element (Felt) arithmetic, which operates in a prime field with modulus `p = 2^64 - 2^32 + 1`. This means arithmetic is **modular** and will silently wrap around instead of causing an error.
+
+```rust
+// DANGEROUS: This does NOT error on underflow!
+let balance = Felt::new(100);
+let withdrawal = Felt::new(500);
+let new_balance = balance - withdrawal; // Silently wraps to a huge positive number!
+```
+
+When you subtract a larger value from a smaller one, the result wraps around to a large positive number (approximately `2^64`). This is NOT an error in the Miden VM - the transaction will succeed with an incorrect balance.
+
+### Why This Happens
+
+The Miden VM performs all Felt arithmetic as modular operations within the prime field. There is no automatic overflow or underflow detection at the VM level. The Rust compiler's default overflow mode is `Unchecked`, meaning it compiles directly to raw VM arithmetic operations.
+
+### Solution
+
+**Always validate before subtraction:**
+
+```rust
+// CORRECT: Check balance before subtracting
+let current_balance: Felt = self.balances.get(&key);
+let withdraw_amount = withdraw_asset.inner[0];
+
+// Validate that balance is sufficient
+assert!(
+ current_balance.as_u64() >= withdraw_amount.as_u64(),
+ "Withdrawal amount exceeds available balance"
+);
+
+// Only subtract after validation
+let new_balance = current_balance - withdraw_amount;
+```
+
+### Example from Bank Contract
+
+```rust title="contracts/bank-account/src/lib.rs"
+pub fn withdraw(&mut self, depositor: AccountId, withdraw_asset: Asset, /* ... */) {
+ let withdraw_amount = withdraw_asset.inner[0];
+
+ // 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"
+ );
+
+ let new_balance = current_balance - withdraw_amount;
+ self.balances.set(key, new_balance);
+}
+```
+
+:::danger Critical Security Issue
+Failure to validate before subtraction can lead to:
+- Users withdrawing more than their balance
+- Balance values becoming astronomically large
+- Complete loss of funds in the contract
+
+**Always check bounds before Felt subtraction operations.**
+:::
+
+---
+
+## Wallet Component Requirement
+
+### Problem
+
+The `active_note::add_assets_to_account()` function fails if the consuming account doesn't have the basic wallet component.
+
+```
+Error: Account does not support asset operations
+```
+
+### Solution
+
+Ensure accounts that receive assets via this function have wallet capability:
+
+```rust
+use miden_client::account::component::BasicWallet;
+
+// When creating an account that needs to receive assets
+let account = AccountBuilder::new(seed)
+ .with_component(BasicWallet) // Add wallet capability
+ .with_component(YourCustomComponent)
+ .build()?;
+```
+
+**Alternative: Use `native_account::add_asset()`**
+
+For account components, use the native account API instead:
+
+```rust
+#[component]
+impl Bank {
+ pub fn deposit(&mut self, depositor: AccountId, asset: Asset) {
+ // This works for any account - no wallet required
+ native_account::add_asset(asset);
+
+ // Track balance in storage
+ self.update_balance(depositor, asset);
+ }
+}
+```
+
+---
+
+## Storage Map Key Consistency
+
+### Problem
+
+Storage map lookups return unexpected results or zeros when keys are constructed inconsistently.
+
+### Solution
+
+Define a single key construction pattern and use it everywhere:
+
+```rust title="contracts/bank-account/src/lib.rs"
+#[component]
+impl Bank {
+ /// Construct a balance key for a depositor and asset.
+ /// Key format: [depositor_prefix, depositor_suffix, faucet_prefix, faucet_suffix]
+ fn balance_key(&self, depositor: AccountId, faucet_id: AccountId) -> Word {
+ Word::from([
+ depositor.prefix().as_felt(),
+ depositor.suffix(),
+ faucet_id.prefix().as_felt(),
+ faucet_id.suffix(),
+ ])
+ }
+
+ pub fn get_balance(&self, depositor: AccountId, faucet_id: AccountId) -> Felt {
+ let key = self.balance_key(depositor, faucet_id);
+ self.balances.get(&key)
+ }
+
+ fn update_balance(&mut self, depositor: AccountId, faucet_id: AccountId, amount: Felt) {
+ let key = self.balance_key(depositor, faucet_id);
+ self.balances.set(key, amount);
+ }
+}
+```
+
+---
+
+## Note Type Values
+
+### Problem
+
+When creating output notes, the `note_type` parameter uses specific integer values that aren't obvious.
+
+### Solution
+
+Use the correct values for note types:
+
+| Value | Type | Description |
+|-------|------|-------------|
+| 1 | Public | Note data is visible on-chain |
+| 2 | Private | Note data is hidden (only hash on-chain) |
+
+```rust
+// In note inputs or when creating output notes
+let note_type = Felt::new(1); // Public note
+// or
+let note_type = Felt::new(2); // Private note
+```
+
+---
+
+## P2ID Script Root
+
+### Problem
+
+When creating P2ID (Pay-to-ID) output notes, you need the script's MAST root. The v0.13 pattern of hardcoding the digest is fragile — it hashes under RPO, which v0.14 replaced with Poseidon2, and any future change to the P2ID script invalidates the constant silently.
+
+### Solution (v0.14)
+
+Carry the P2ID script root on the initiating note's storage and read it at runtime instead of hardcoding a value. This is the pattern used in the v0.14 `miden-bank` example:
+
+```rust title="contracts/bank-account/src/lib.rs"
+// The withdraw-request note encodes the P2ID script root at storage slots
+// 10..14 (4 felts = 1 Word). The Poseidon2-hashed digest of the P2ID note
+// script is injected by the caller when the note is created.
+let storage = active_note::get_storage();
+let script_root = Word::from([
+ storage[10], storage[11], storage[12], storage[13],
+]);
+
+// Pass the script root through to the P2ID-note constructor
+self.create_p2id_note(serial_num, &asset, depositor, tag, note_type, script_root);
+```
+
+On the client side, compute the script root dynamically from the standard P2ID note script instead of hardcoding it:
+
+```rust
+use miden_client::note::P2idNote;
+
+// v0.14: ask miden-standards for the current P2ID script, take its MAST root.
+let p2id_script_root: Word = P2idNote::script().root();
+```
+
+:::info Why Not Hardcode
+The native hash function changed from RPO to Poseidon2 in v0.14, so every MAST root — including the P2ID script's — is different from v0.13. Any hardcoded digest from v0.13 will fail a script-root check against v0.14. Reading the root from `P2idNote::script().root()` (or the active note's storage for on-chain code) keeps the contract resilient to future script changes.
+:::
+
+---
+
+## Quick Reference Table
+
+| Pitfall | Symptom | Solution |
+|---------|---------|----------|
+| Felt comparison | Wrong comparison results | Use `.as_u64()` |
+| Stack overflow | "16 elements" error | Reduce locals, split functions |
+| Too many args | "4 words" error | Group into Words, use inputs |
+| Array reversal | Wrong data order | Be consistent with construction |
+| Felt underflow | Balance wraps to huge number | Validate before subtraction |
+| Missing wallet | Asset operation fails | Add `BasicWallet` component |
+| Key mismatch | Zero balances | Use helper function for keys |
+| Note type | Wrong note visibility | Use 1 (Public) or 2 (Private) |
+
+:::tip View Complete Source
+See these patterns in context in the [miden-bank repository](https://github.com/keinberger/miden-bank).
+:::
+
+## Next Steps
+
+- **[Debugging Guide](./debugging)** - Troubleshoot errors
+- **[Testing Guide](./testing)** - MockChain patterns
+- **[Miden Bank Tutorial](../miden-bank/)** - See these patterns in context
diff --git a/versioned_docs/version-0.14/builder/tutorials/helpers/testing.md b/versioned_docs/version-0.14/builder/tutorials/helpers/testing.md
new file mode 100644
index 00000000..ff2bdc58
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/helpers/testing.md
@@ -0,0 +1,610 @@
+---
+sidebar_position: 1
+title: "Testing with MockChain"
+description: "Learn how to test Miden Rust compiler contracts using MockChain for simulating blockchain behavior locally."
+---
+
+# Testing with MockChain
+
+MockChain provides a local simulation of the Miden blockchain for testing your contracts without connecting to a network. This guide covers testing patterns for account components, note scripts, and transaction scripts.
+
+## Overview
+
+MockChain simulates:
+- Block production and proving
+- Account state management
+- Note creation and consumption
+- Transaction execution
+
+This enables fast, deterministic testing of your Miden contracts.
+
+## Test Project Setup
+
+Create an integration test crate alongside your contracts:
+
+```text
+your-project/
+├── contracts/
+│ ├── my-account/
+│ └── my-note/
+└── integration/
+ ├── Cargo.toml
+ ├── src/
+ │ └── helpers.rs # Test utilities
+ └── tests/
+ └── my_test.rs # Test files
+```
+
+### Cargo.toml for Tests
+
+```toml title="integration/Cargo.toml"
+[package]
+name = "integration"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+path = "src/helpers.rs"
+
+[[test]]
+name = "my_test"
+path = "tests/my_test.rs"
+
+[dependencies]
+anyhow = "1.0"
+tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
+
+# Miden dependencies
+cargo-miden = { version = "0.7" }
+miden-client = { version = "0.13", features = ["tonic", "testing"] }
+miden-client-sqlite-store = { version = "0.13", package = "miden-client-sqlite-store" }
+miden-core = { version = "0.20" }
+miden-standards = { version = "0.13", default-features = false, features = ["testing"] }
+miden-testing = "0.13"
+miden-mast-package = { version = "0.20", default-features = false }
+rand = { version = "0.9" }
+```
+
+## Building Contracts for Tests
+
+Use `cargo-miden` to build your contracts programmatically:
+
+```rust title="integration/src/helpers.rs"
+use std::path::Path;
+use anyhow::{bail, Context, Result};
+use cargo_miden::{run, OutputType};
+use miden_mast_package::Package;
+
+pub fn build_project_in_dir(dir: &Path, release: bool) -> Result {
+ let profile = if release { "--release" } else { "--debug" };
+ let manifest_path = dir.join("Cargo.toml");
+ let manifest_arg = manifest_path.to_string_lossy();
+
+ let args = vec![
+ "cargo", "miden", "build",
+ profile,
+ "--manifest-path", &manifest_arg,
+ ];
+
+ let output = run(args.into_iter().map(String::from), OutputType::Masm)
+ .context("Failed to compile project")?
+ .context("Cargo miden build returned None")?;
+
+ let artifact_path = match output {
+ cargo_miden::CommandOutput::BuildCommandOutput { output } => match output {
+ cargo_miden::BuildOutput::Masm { artifact_path } => artifact_path,
+ other => bail!("Expected Masm output, got {:?}", other),
+ },
+ other => bail!("Expected BuildCommandOutput, got {:?}", other),
+ };
+
+ let package_bytes = std::fs::read(&artifact_path)?;
+ Package::read_from_bytes(&package_bytes)
+ .context("Failed to deserialize package")
+}
+```
+
+## MockChain Basics
+
+### Creating a MockChain
+
+Use the builder pattern to set up your test environment:
+
+```rust
+use miden_testing::{Auth, MockChain};
+
+#[tokio::test]
+async fn my_test() -> anyhow::Result<()> {
+ // Create builder
+ let mut builder = MockChain::builder();
+
+ // Add accounts, faucets, notes...
+
+ // Build the chain
+ let mut mock_chain = builder.build()?;
+
+ Ok(())
+}
+```
+
+### Adding a Faucet
+
+Faucets mint assets for testing:
+
+```rust
+// Create a faucet with 1,000,000 total supply, decimals = 10
+let faucet = builder.add_existing_basic_faucet(
+ Auth::BasicAuth,
+ "TEST", // Token symbol
+ 1_000_000, // Total supply
+ Some(10), // Decimals
+)?;
+```
+
+### Adding Wallet Accounts
+
+Create accounts with initial assets:
+
+```rust
+use miden_client::asset::FungibleAsset;
+
+// Create a wallet with 100 tokens from the faucet
+let sender = builder.add_existing_wallet_with_assets(
+ Auth::BasicAuth,
+ [FungibleAsset::new(faucet.id(), 100)?.into()],
+)?;
+```
+
+## Creating Custom Accounts
+
+For accounts with custom components, create configuration helpers:
+
+```rust title="integration/src/helpers.rs"
+use miden_client::account::{AccountStorageMode, AccountType, StorageSlot};
+
+#[derive(Clone)]
+pub struct AccountCreationConfig {
+ pub account_type: AccountType,
+ pub storage_mode: AccountStorageMode,
+ pub storage_slots: Vec,
+}
+
+impl Default for AccountCreationConfig {
+ fn default() -> Self {
+ Self {
+ account_type: AccountType::RegularAccountImmutableCode,
+ storage_mode: AccountStorageMode::Public,
+ storage_slots: vec![],
+ }
+ }
+}
+```
+
+### Creating Account from Package
+
+```rust
+use miden_client::account::{StorageMap, StorageSlot, StorageSlotName};
+use std::sync::Arc;
+
+// Build the contract
+let bank_package = Arc::new(build_project_in_dir(
+ Path::new("../contracts/bank-account"),
+ true, // release mode
+)?);
+
+// Configure 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 config = 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()
+};
+
+// Create the account
+let mut account = create_testing_account_from_package(
+ bank_package.clone(),
+ config,
+).await?;
+
+// Add to MockChain
+builder.add_account(account.clone())?;
+```
+
+## Creating Notes
+
+### Note Configuration
+
+```rust title="integration/src/helpers.rs"
+use miden_client::note::{NoteAssets, NoteTag, NoteType};
+use miden_core::Felt;
+
+pub struct NoteCreationConfig {
+ pub note_type: NoteType,
+ pub tag: NoteTag,
+ pub assets: NoteAssets,
+ pub inputs: Vec,
+}
+
+impl Default for NoteCreationConfig {
+ fn default() -> Self {
+ Self {
+ note_type: NoteType::Public,
+ tag: NoteTag::new(0),
+ assets: Default::default(),
+ inputs: Default::default(),
+ }
+ }
+}
+```
+
+### Creating Notes with Assets
+
+```rust
+use miden_client::asset::{Asset, FungibleAsset};
+use miden_client::note::NoteAssets;
+
+// Build note script
+let deposit_note_package = Arc::new(build_project_in_dir(
+ Path::new("../contracts/deposit-note"),
+ true,
+)?);
+
+// Create assets to attach
+let deposit_amount: u64 = 1000;
+let fungible_asset = FungibleAsset::new(faucet.id(), deposit_amount)?;
+let note_assets = NoteAssets::new(vec![Asset::Fungible(fungible_asset)])?;
+
+// Create the note
+let deposit_note = create_testing_note_from_package(
+ deposit_note_package.clone(),
+ sender.id(), // Note sender
+ NoteCreationConfig {
+ assets: note_assets,
+ ..Default::default()
+ },
+)?;
+
+// Add to MockChain
+builder.add_output_note(OutputNote::Full(deposit_note.clone()));
+```
+
+### Creating Notes with Inputs
+
+For notes that read parameters via `active_note::get_inputs()`:
+
+```rust
+use miden_core::Felt;
+
+// Note inputs are a vector of Felts
+let inputs = vec![
+ // Asset data [0-3]
+ Felt::new(withdraw_amount),
+ Felt::new(0),
+ faucet.id().suffix(),
+ faucet.id().prefix().as_felt(),
+ // Serial number [4-7]
+ Felt::new(0x1234567890abcdef),
+ Felt::new(0xfedcba0987654321),
+ Felt::new(0xdeadbeefcafebabe),
+ Felt::new(0x0123456789abcdef),
+ // Additional parameters
+ Felt::new(tag as u64),
+ Felt::new(1), // note_type (1 = Public)
+];
+
+let note = create_testing_note_from_package(
+ note_package.clone(),
+ sender.id(),
+ NoteCreationConfig {
+ inputs,
+ ..Default::default()
+ },
+)?;
+```
+
+## Executing Transactions
+
+### Basic Transaction Execution
+
+```rust
+// Build MockChain after adding all accounts and notes
+let mut mock_chain = builder.build()?;
+
+// Build transaction context
+// Args: (account_id, input_note_ids, expected_output_note_ids)
+let tx_context = mock_chain
+ .build_tx_context(account.id(), &[note.id()], &[])?
+ .build()?;
+
+// Execute
+let executed_tx = tx_context.execute().await?;
+
+// Apply state changes to local account copy
+account.apply_delta(&executed_tx.account_delta())?;
+
+// Add to pending transactions and prove block
+mock_chain.add_pending_executed_transaction(&executed_tx)?;
+mock_chain.prove_next_block()?;
+```
+
+### Transaction with Script
+
+For transaction scripts (like initialization):
+
+```rust
+use miden_client::transaction::TransactionScript;
+
+// Build the transaction script
+let init_package = Arc::new(build_project_in_dir(
+ Path::new("../contracts/init-tx-script"),
+ true,
+)?);
+
+let init_program = init_package.unwrap_program();
+let init_tx_script = TransactionScript::new((*init_program).clone());
+
+// Execute with script
+let tx_context = mock_chain
+ .build_tx_context(account.id(), &[], &[])?
+ .tx_script(init_tx_script)
+ .build()?;
+
+let executed_tx = tx_context.execute().await?;
+```
+
+### Transactions with Expected Output Notes
+
+When your contract creates output notes, specify them:
+
+```rust
+use miden_client::transaction::OutputNote;
+
+// Build the expected output note
+let expected_note = Note::new(
+ output_assets,
+ output_metadata,
+ recipient,
+);
+
+let tx_context = mock_chain
+ .build_tx_context(account.id(), &[input_note.id()], &[])?
+ .extend_expected_output_notes(vec![OutputNote::Full(expected_note)])
+ .build()?;
+```
+
+## Verifying State Changes
+
+### Reading Storage After Transaction
+
+```rust
+// After executing and applying delta...
+
+// Read Value storage (by slot name)
+let value: Word = account.storage().get_item(&initialized_slot)?;
+
+// Read Map storage (by slot name)
+let key = Word::from([
+ depositor.prefix().as_felt(),
+ depositor.suffix(),
+ faucet.id().prefix().as_felt(),
+ faucet.id().suffix(),
+]);
+let balance = account.storage().get_map_item(&balances_slot, key)?;
+
+// Assert expected values
+assert_eq!(
+ balance,
+ Word::from([Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(1000)]),
+ "Balance should match deposited amount"
+);
+```
+
+## Testing Error Conditions
+
+### Expecting Transaction Failure
+
+```rust
+#[tokio::test]
+async fn should_fail_without_initialization() -> anyhow::Result<()> {
+ // Setup WITHOUT initialization step...
+
+ let tx_context = mock_chain
+ .build_tx_context(account.id(), &[note.id()], &[])?
+ .build()?;
+
+ // Execute and expect failure
+ let result = tx_context.execute().await;
+
+ assert!(
+ result.is_err(),
+ "Expected transaction to fail, but it succeeded"
+ );
+
+ // Optionally check error message
+ if let Err(e) = result {
+ println!("Expected error: {}", e);
+ }
+
+ Ok(())
+}
+```
+
+### Testing Constraint Violations
+
+```rust
+#[tokio::test]
+async fn deposit_exceeds_max_should_fail() -> anyhow::Result<()> {
+ // Create deposit with amount > MAX_DEPOSIT_AMOUNT
+ let large_amount: u64 = 2_000_000; // Max is 1,000,000
+
+ // ... setup code ...
+
+ let result = tx_context.execute().await;
+
+ assert!(
+ result.is_err(),
+ "Expected deposit to fail due to max limit"
+ );
+
+ Ok(())
+}
+```
+
+## Complete Test Example
+
+```rust title="integration/tests/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},
+ asset::{Asset, FungibleAsset},
+ note::NoteAssets,
+ transaction::{OutputNote, TransactionScript},
+ Felt, Word,
+};
+use miden_testing::{Auth, MockChain};
+use std::{path::Path, sync::Arc};
+
+#[tokio::test]
+async fn deposit_test() -> anyhow::Result<()> {
+ // 1. Setup MockChain builder
+ let mut builder = MockChain::builder();
+
+ // 2. Create faucet and sender
+ let faucet = builder.add_existing_basic_faucet(Auth::BasicAuth, "TEST", 1000, Some(10))?;
+ let sender = builder.add_existing_wallet_with_assets(
+ Auth::BasicAuth,
+ [FungibleAsset::new(faucet.id(), 100)?.into()],
+ )?;
+
+ // 3. 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
+ )?);
+
+ // 4. 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![
+ 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?;
+
+ // 5. Create deposit note
+ 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() },
+ )?;
+
+ // 6. Add to builder and build chain
+ builder.add_account(bank_account.clone())?;
+ builder.add_output_note(OutputNote::Full(deposit_note.clone()));
+ let mut mock_chain = builder.build()?;
+
+ // 7. 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()?;
+
+ // 8. Execute deposit
+ let tx_context = mock_chain
+ .build_tx_context(bank_account.id(), &[deposit_note.id()], &[])?
+ .build()?;
+ let executed_tx = tx_context.execute().await?;
+ bank_account.apply_delta(&executed_tx.account_delta())?;
+ mock_chain.add_pending_executed_transaction(&executed_tx)?;
+ mock_chain.prove_next_block()?;
+
+ // 9. Verify balance
+ 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 expected = Word::from([
+ Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(deposit_amount)
+ ]);
+ assert_eq!(balance, expected, "Balance should match deposit");
+
+ println!("Deposit test passed!");
+ Ok(())
+}
+```
+
+## Running Tests
+
+```bash title=">_ Terminal"
+# Run all tests
+cargo test -p integration -- --nocapture
+
+# Run specific test
+cargo test -p integration deposit_test -- --nocapture
+
+# Run with verbose output
+RUST_LOG=debug cargo test -p integration -- --nocapture
+```
+
+## Key Takeaways
+
+1. **MockChain Builder Pattern** - Use `MockChain::builder()` to set up test environments
+2. **Build Contracts First** - Use `build_project_in_dir()` to compile contracts before tests
+3. **Configure Storage Slots** - Match your contract's storage layout when creating accounts
+4. **Apply Deltas** - Always call `apply_delta()` on local account copies after transactions
+5. **Prove Blocks** - Call `prove_next_block()` after adding executed transactions
+6. **Test Failures** - Use `result.is_err()` to verify constraint violations
+
+:::tip View Complete Source
+See the complete test implementations in the [miden-bank repository](https://github.com/keinberger/miden-bank/tree/main/integration/tests).
+:::
+
+## Next Steps
+
+- **[Debugging Guide](./debugging)** - Troubleshoot common issues
+- **[Common Pitfalls](./pitfalls)** - Avoid known gotchas
+- **[Miden Bank Tutorial](../miden-bank/)** - See testing in action
diff --git a/versioned_docs/version-0.14/builder/tutorials/img/count_copy_fpi_diagram.png b/versioned_docs/version-0.14/builder/tutorials/img/count_copy_fpi_diagram.png
new file mode 100644
index 00000000..f0fd3025
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tutorials/img/count_copy_fpi_diagram.png differ
diff --git a/versioned_docs/version-0.14/builder/tutorials/img/note_creation_masm.png b/versioned_docs/version-0.14/builder/tutorials/img/note_creation_masm.png
new file mode 100644
index 00000000..9458433b
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tutorials/img/note_creation_masm.png differ
diff --git a/versioned_docs/version-0.14/builder/tutorials/index.md b/versioned_docs/version-0.14/builder/tutorials/index.md
new file mode 100644
index 00000000..dc1a45db
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/index.md
@@ -0,0 +1,43 @@
+---
+title: Tutorials
+sidebar_position: 0
+pagination_prev: null
+---
+
+# Tutorials
+
+Hands-on walkthroughs for building on Miden. Every tutorial pairs with runnable Rust and TypeScript examples from the [miden-tutorials](https://github.com/0xMiden/miden-tutorials) repo and with MockChain tests so you can verify each step locally.
+
+## Pick a path
+
+
+
+ A 9-part curriculum — build a complete banking application covering components, storage, note scripts, cross-component calls, and output notes.
+
+
+ Standalone how-to's for specific tasks: counter contract, create/deploy, foreign procedure invocation, React wallet, and more.
+
+
+ Run a Miden node locally or on testnet with `midenup` and the node binary.
+
+
+
+## Development helpers
+
+
+
+ Test your contracts against MockChain for local simulation.
+
+
+ Interpret errors and debug common issues.
+
+
+ Avoid known issues and limitations.
+
+
+
+## Prerequisites
+
+- [Install the Miden toolchain](../get-started/setup/installation) with `midenup`.
+- Basic familiarity with Rust (or TypeScript for the client examples).
+- Understanding of the [core concepts](../smart-contracts/) — accounts, notes, transactions.
diff --git a/versioned_docs/version-0.14/builder/tutorials/miden-bank/00-project-setup.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/00-project-setup.md
new file mode 100644
index 00000000..6fda6470
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/00-project-setup.md
@@ -0,0 +1,348 @@
+---
+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](https://docs.miden.xyz/builder/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.12" }
+
+[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: StorageValue,
+
+ /// 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) {
+ // Get current value from storage
+ let current: Word = self.initialized.get();
+
+ // Check not already initialized
+ assert!(
+ current[0].as_canonical_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.set(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 `StorageValue` 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.14, 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_bank_account::bank::initialized")
+ .expect("Valid slot name");
+ let balances_slot =
+ StorageSlotName::new("miden_bank_account::bank::balances")
+ .expect("Valid slot name");
+
+ let mut init_storage_data = InitStorageData::default();
+ init_storage_data.insert_value(
+ StorageValueName::from_slot_name(&initialized_slot),
+ Word::default(),
+ )?;
+ let bank_cfg = AccountCreationConfig {
+ init_storage_data,
+ ..Default::default()
+ };
+
+ let bank_account =
+ create_testing_account_from_package(bank_package.clone(), bank_cfg)?;
+
+ // 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/versioned_docs/version-0.14/builder/tutorials/miden-bank/01-account-components.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/01-account-components.md
new file mode 100644
index 00000000..15e1a300
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/01-account-components.md
@@ -0,0 +1,426 @@
+---
+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 (StorageValue) │ │ initialized (StorageValue) │
+│ │ │ 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: StorageValue,
+
+ /// 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:
+
+### StorageValue Storage
+
+The `StorageValue` type provides access to a single storage slot:
+
+```rust
+#[storage(description = "initialized")]
+initialized: StorageValue,
+```
+
+Use `StorageValue` when you need to store a single `Word` of data.
+
+**Reading and writing:**
+
+```rust
+// Get returns a Word
+let current: Word = self.initialized.get();
+
+// Check the first element (our flag)
+if current[0].as_canonical_u64() == 0 {
+ // Not initialized
+}
+
+// Set a new value
+let new_value = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
+self.initialized.set(new_value);
+```
+
+:::tip Type Annotations
+The `.get()` method requires a type annotation: `let current: Word = self.initialized.get();`
+:::
+
+### 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` | `StorageValue` | Initialization flag |
+| `balances` | `StorageMap` | Depositor balances |
+
+The `description` attribute generates named slot identifiers (e.g., `miden_bank_account::bank::initialized`) used in tests to reference specific slots. The naming convention is `{package_name}::{component_struct}::{field_name}`. 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) {
+ // Get current value from storage
+ let current: Word = self.initialized.get();
+
+ // Check not already initialized
+ assert!(
+ current[0].as_canonical_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.set(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.get();
+ assert!(
+ current[0].as_canonical_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: {package_name}::{component_struct}::{field_name}
+ let initialized_slot =
+ StorageSlotName::new("miden_bank_account::bank::initialized")
+ .expect("Valid slot name");
+ let balances_slot =
+ StorageSlotName::new("miden_bank_account::bank::balances")
+ .expect("Valid slot name");
+
+ let mut init_storage_data = InitStorageData::default();
+ init_storage_data.insert_value(
+ StorageValueName::from_slot_name(&initialized_slot),
+ Word::default(),
+ )?;
+ let bank_cfg = AccountCreationConfig {
+ init_storage_data,
+ ..Default::default()
+ };
+
+ let bank_account =
+ create_testing_account_from_package(bank_package.clone(), bank_cfg)?;
+
+ // =========================================================================
+ // 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_canonical_u64());
+
+ // =========================================================================
+ // 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: StorageValue,
+
+ /// 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) {
+ // Get current value from storage
+ let current: Word = self.initialized.get();
+
+ // Check not already initialized
+ assert!(
+ current[0].as_canonical_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.set(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.get();
+ assert!(
+ current[0].as_canonical_u64() == 1,
+ "Bank not initialized - deposits not enabled"
+ );
+ }
+}
+```
+
+
+
+## Key Takeaways
+
+1. **`#[component]`** marks structs and impl blocks as Miden account components
+2. **`StorageValue`** stores a single Word, read with `.get()`, write with `.set()`
+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 [contracts/bank-account/src/lib.rs](https://github.com/0xMiden/miden-tutorials/blob/main/examples/miden-bank/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/versioned_docs/version-0.14/builder/tutorials/miden-bank/02-constants-constraints.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/02-constants-constraints.md
new file mode 100644
index 00000000..c82b65ed
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/02-constants-constraints.md
@@ -0,0 +1,456 @@
+---
+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_canonical_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.get();
+ assert!(
+ current[0].as_canonical_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.set(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_canonical_u64() > MAX_DEPOSIT_AMOUNT {
+ // This works correctly
+}
+```
+
+The `.as_canonical_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.get();
+ assert!(
+ current[0].as_canonical_u64() == 0,
+ "Bank already initialized"
+ );
+
+ let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
+ self.initialized.set(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.get();
+ assert!(
+ current[0].as_canonical_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.value[0];
+
+ // ========================================================================
+ // CONSTRAINT: Maximum deposit amount check
+ // ========================================================================
+ assert!(
+ deposit_amount.as_canonical_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.get();
+ assert!(
+ current[0].as_canonical_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_bank_account::bank::initialized")
+ .expect("Valid slot name");
+ let balances_slot =
+ StorageSlotName::new("miden_bank_account::bank::balances")
+ .expect("Valid slot name");
+
+ // Create an uninitialized bank account
+ let mut init_storage_data = InitStorageData::default();
+ init_storage_data.insert_value(
+ StorageValueName::from_slot_name(&initialized_slot),
+ Word::default(),
+ )?;
+ let bank_cfg = AccountCreationConfig {
+ init_storage_data,
+ ..Default::default()
+ };
+
+ let bank_account =
+ create_testing_account_from_package(bank_package.clone(), bank_cfg)?;
+
+ // Verify the bank starts uninitialized
+ let initialized = bank_account.storage().get_item(&initialized_slot)?;
+ assert_eq!(
+ initialized[0].as_canonical_u64(),
+ 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_canonical_u64());
+ 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_canonical_u64() >= amount.as_canonical_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](https://docs.miden.xyz/builder/tutorials/rust-compiler/pitfalls#felt-arithmetic-underflowoverflow) for more details.
+:::
+
+### State Checks
+
+```rust
+fn require_not_paused(&self) {
+ let paused: Word = self.paused.get();
+ assert!(
+ paused[0].as_canonical_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: StorageValue,
+
+ #[storage(description = "balances")]
+ balances: StorageMap,
+}
+
+#[component]
+impl Bank {
+ /// Initialize the bank account, enabling deposits.
+ pub fn initialize(&mut self) {
+ let current: Word = self.initialized.get();
+ assert!(
+ current[0].as_canonical_u64() == 0,
+ "Bank already initialized"
+ );
+
+ let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
+ self.initialized.set(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.get();
+ assert!(
+ current[0].as_canonical_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.value[0];
+
+ // CONSTRAINT: Maximum deposit amount check
+ assert!(
+ deposit_amount.as_canonical_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_canonical_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 [contracts/bank-account/src/lib.rs](https://github.com/0xMiden/miden-tutorials/blob/main/examples/miden-bank/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/versioned_docs/version-0.14/builder/tutorials/miden-bank/03-asset-management.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/03-asset-management.md
new file mode 100644
index 00000000..f49857b7
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/03-asset-management.md
@@ -0,0 +1,620 @@
+---
+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 amount through `asset.value` and faucet ID through `asset.key`:
+
+```rust
+let amount = deposit_asset.value[0]; // The token amount
+let faucet_suffix = deposit_asset.key[2]; // Faucet ID suffix
+let faucet_prefix = deposit_asset.key[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 value word
+ let deposit_amount = deposit_asset.value[0];
+
+ // ========================================================================
+ // CONSTRAINT: Maximum deposit amount check
+ // ========================================================================
+ assert!(
+ deposit_amount.as_canonical_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.key[3], // faucet_prefix
+ deposit_asset.key[2], // faucet_suffix
+ ]);
+
+ // 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.key[3], // Which asset type (faucet ID prefix)
+ deposit_asset.key[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_canonical_u64() >= withdraw_amount.as_canonical_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 value word
+ let withdraw_amount = withdraw_asset.value[0];
+
+ // Create key from depositor's AccountId and asset faucet ID
+ let key = Word::from([
+ depositor.prefix,
+ depositor.suffix,
+ withdraw_asset.key[3], // faucet_prefix
+ withdraw_asset.key[2], // faucet_suffix
+ ]);
+
+ // ========================================================================
+ // 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_canonical_u64() >= withdraw_amount.as_canonical_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::auth::AuthSchemeId;
+use miden_client::note::NoteAssets;
+use miden_client::transaction::{RawOutputNote, 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 { auth_scheme: AuthSchemeId::Falcon512Poseidon2 }, "TEST", 10_000_000, Some(10))?;
+
+ // Create sender wallet with tokens
+ let sender = builder.add_existing_wallet_with_assets(Auth::BasicAuth { auth_scheme: AuthSchemeId::Falcon512Poseidon2 }, [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_bank_account::bank::initialized")
+ .expect("Valid slot name");
+ let balances_slot =
+ StorageSlotName::new("miden_bank_account::bank::balances")
+ .expect("Valid slot name");
+
+ let mut init_storage_data = InitStorageData::default();
+ init_storage_data.insert_value(
+ StorageValueName::from_slot_name(&initialized_slot),
+ Word::default(),
+ )?;
+ let bank_cfg = AccountCreationConfig {
+ init_storage_data,
+ ..Default::default()
+ };
+
+ let mut bank_account =
+ create_testing_account_from_package(bank_package.clone(), bank_cfg)?;
+
+ // 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(RawOutputNote::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_canonical_u64(),
+ 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_canonical_u64();
+
+ 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: StorageValue,
+
+ #[storage(description = "balances")]
+ balances: StorageMap,
+}
+
+#[component]
+impl Bank {
+ /// Initialize the bank account, enabling deposits.
+ pub fn initialize(&mut self) {
+ let current: Word = self.initialized.get();
+ assert!(
+ current[0].as_canonical_u64() == 0,
+ "Bank already initialized"
+ );
+
+ let initialized_word = Word::from([felt!(1), felt!(0), felt!(0), felt!(0)]);
+ self.initialized.set(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.get();
+ assert!(
+ current[0].as_canonical_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.value[0];
+
+ assert!(
+ deposit_amount.as_canonical_u64() <= MAX_DEPOSIT_AMOUNT,
+ "Deposit amount exceeds maximum allowed"
+ );
+
+ let key = Word::from([
+ depositor.prefix,
+ depositor.suffix,
+ deposit_asset.key[3], // faucet_prefix
+ deposit_asset.key[2], // faucet_suffix
+ ]);
+
+ 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.value[0];
+
+ let key = Word::from([
+ depositor.prefix,
+ depositor.suffix,
+ withdraw_asset.key[3], // faucet_prefix
+ withdraw_asset.key[2], // faucet_suffix
+ ]);
+
+ // CRITICAL: Validate balance BEFORE subtraction
+ let current_balance: Felt = self.balances.get(key);
+ assert!(
+ current_balance.as_canonical_u64() >= withdraw_amount.as_canonical_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**: `value[0]` = amount; `key[2]` = faucet_suffix; `key[3]` = 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 [contracts/bank-account/src/lib.rs](https://github.com/0xMiden/miden-tutorials/blob/main/examples/miden-bank/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/versioned_docs/version-0.14/builder/tutorials/miden-bank/04-note-scripts.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/04-note-scripts.md
new file mode 100644
index 00000000..424d3569
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/04-note-scripts.md
@@ -0,0 +1,544 @@
+---
+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.12" }
+
+[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_storage() - Note Parameters
+
+```rust
+let storage = active_note::get_storage();
+let first_item = storage[0];
+```
+
+Returns a slice of `Felt` values passed when the note was created. We'll use storage items 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::asset::{Asset, FungibleAsset};
+use miden_client::auth::AuthSchemeId;
+use miden_client::note::NoteAssets;
+use miden_client::transaction::{RawOutputNote, TransactionScript};
+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 { auth_scheme: AuthSchemeId::Falcon512Poseidon2 }, "TEST", 10_000_000, Some(10))?;
+
+ // Create sender (depositor) wallet
+ let sender = builder.add_existing_wallet_with_assets(Auth::BasicAuth { auth_scheme: AuthSchemeId::Falcon512Poseidon2 }, [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_bank_account::bank::initialized")
+ .expect("Valid slot name");
+ let balances_slot =
+ StorageSlotName::new("miden_bank_account::bank::balances")
+ .expect("Valid slot name");
+
+ let mut init_storage_data = InitStorageData::default();
+ init_storage_data.insert_value(
+ StorageValueName::from_slot_name(&initialized_slot),
+ Word::default(),
+ )?;
+ let bank_cfg = AccountCreationConfig {
+ init_storage_data,
+ ..Default::default()
+ };
+
+ let mut bank_account =
+ create_testing_account_from_package(bank_package.clone(), bank_cfg)?;
+
+ 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(RawOutputNote::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_canonical_u64();
+
+ 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 Storage (14 Felts)
+/// [0-3]: withdraw asset encoded as [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)
+/// [10-13]: P2ID script_root (MAST root of the P2ID note script, Poseidon2-hashed)
+#[note]
+struct WithdrawRequestNote;
+
+#[note]
+impl WithdrawRequestNote {
+ #[note_script]
+ fn run(self, _arg: Word) {
+ let depositor = active_note::get_sender();
+ let storage = active_note::get_storage();
+
+ // Parse parameters from storage
+ let withdraw_asset = Asset::new(
+ Word::from([felt!(0), felt!(0), storage[2], storage[3]]),
+ Word::from([storage[0], felt!(0), felt!(0), felt!(0)]),
+ );
+
+ let serial_num = Word::from([
+ storage[4], storage[5], storage[6], storage[7]
+ ]);
+
+ let tag = storage[8];
+ let note_type = storage[9];
+
+ // Note: P2ID script root (storage[10..13]) is read by the bank account
+ // directly from the active note's storage inside bank_account::withdraw.
+
+ 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](https://docs.miden.xyz/builder/tutorials/rust-compiler/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_storage()`** 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/0xMiden/miden-tutorials/blob/main/examples/miden-bank/contracts/deposit-note/src/lib.rs)
+- [Withdraw Request Note](https://github.com/0xMiden/miden-tutorials/blob/main/examples/miden-bank/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/versioned_docs/version-0.14/builder/tutorials/miden-bank/05-cross-component-calls.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/05-cross-component-calls.md
new file mode 100644
index 00000000..6cb83beb
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/05-cross-component-calls.md
@@ -0,0 +1,296 @@
+---
+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/0xMiden/miden-tutorials/blob/main/examples/miden-bank/contracts/deposit-note/Cargo.toml)
+- [Withdraw Request Note Cargo.toml](https://github.com/0xMiden/miden-tutorials/blob/main/examples/miden-bank/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/versioned_docs/version-0.14/builder/tutorials/miden-bank/06-transaction-scripts.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/06-transaction-scripts.md
new file mode 100644
index 00000000..e8dd1364
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/06-transaction-scripts.md
@@ -0,0 +1,493 @@
+---
+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.12" }
+
+[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_bank_account::bank::initialized")
+ .expect("Valid slot name");
+ let balances_slot =
+ StorageSlotName::new("miden_bank_account::bank::balances")
+ .expect("Valid slot name");
+
+ let mut init_storage_data = InitStorageData::default();
+ init_storage_data.insert_value(
+ StorageValueName::from_slot_name(&initialized_slot),
+ Word::default(),
+ )?;
+ let bank_cfg = AccountCreationConfig {
+ init_storage_data,
+ ..Default::default()
+ };
+
+ let mut bank_account =
+ create_testing_account_from_package(bank_package.clone(), bank_cfg)?;
+
+ // Verify bank is NOT initialized
+ let initial_storage = bank_account.storage().get_item(&initialized_slot)?;
+ assert_eq!(
+ initial_storage[0].as_canonical_u64(),
+ 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_canonical_u64(),
+ 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.12" }
+
+[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 [contracts/init-tx-script/src/lib.rs](https://github.com/0xMiden/miden-tutorials/blob/main/examples/miden-bank/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/versioned_docs/version-0.14/builder/tutorials/miden-bank/07-output-notes.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/07-output-notes.md
new file mode 100644
index 00000000..3f7ffaab
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/07-output-notes.md
@@ -0,0 +1,732 @@
+---
+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 value word
+ let withdraw_amount = withdraw_asset.value[0];
+
+ // Create key from depositor's AccountId and asset faucet ID
+ let key = Word::from([
+ depositor.prefix,
+ depositor.suffix,
+ withdraw_asset.key[3], // faucet_prefix
+ withdraw_asset.key[2], // faucet_suffix
+ ]);
+
+ // 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_canonical_u64() >= withdraw_amount.as_canonical_u64(),
+ "Withdrawal amount exceeds available balance"
+ );
+
+ // Update balance: current - withdraw_amount
+ let new_balance = current_balance - withdraw_amount;
+ self.balances.set(key, new_balance);
+
+ // Read the P2ID script root from the withdraw-request note's storage (items 10-13).
+ // This avoids hardcoding a version-specific MAST root constant.
+ let storage = active_note::get_storage();
+ let script_root = Word::from([storage[10], storage[11], storage[12], storage[13]]);
+
+ // 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, script_root);
+ }
+}
+```
+
+:::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: How the P2ID Script Root is Supplied
+
+Instead of hardcoding a version-specific MAST root constant in the bank contract, the P2ID script root is passed through the withdraw-request note's storage (items 10-13). The `withdraw()` method reads it directly from the active note:
+
+```rust
+let storage = active_note::get_storage();
+let script_root = Word::from([storage[10], storage[11], storage[12], storage[13]]);
+```
+
+This design keeps the bank contract version-agnostic: callers embed the P2ID script root they want to use into the note storage when they create the withdraw-request note. The test uses `P2idNote::script_root()` from the `miden_client` crate to obtain the correct value at test time.
+
+## 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
+ /// * `script_root` - The P2ID note script MAST root (Poseidon2-hashed)
+ fn create_p2id_note(
+ &mut self,
+ serial_num: Word,
+ asset: &Asset,
+ recipient_id: AccountId,
+ tag: Felt,
+ note_type: Felt,
+ script_root: Word,
+ ) {
+ // 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);
+
+ // Compute the recipient hash from:
+ // - serial_num: unique identifier for this note instance
+ // - script_root: the P2ID note script's MAST root
+ // - storage: the target account ID
+ //
+ // The P2ID script expects inputs as [suffix, prefix]
+ let recipient = note::build_recipient(
+ 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 note::build_recipient()
+
+| Parameter | Description |
+| ------------- | ------------------------------------------ |
+| `serial_num` | Unique 4-Felt value preventing note reuse |
+| `script_root` | The P2ID script's MAST root digest |
+| `storage` | Script storage items (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](https://docs.miden.xyz/builder/tutorials/rust-compiler/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.12" }
+
+[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 storage items
+/// 4. Calls `bank_account::withdraw(depositor, asset, serial_num, tag, note_type)`
+/// 5. Bank updates the depositor's balance
+/// 6. Bank reads P2ID script root from storage[10-13] and creates a P2ID output note
+///
+/// # Note Storage (14 Felts)
+/// [0-3]: withdraw asset encoded as [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)
+/// [10-13]: P2ID script_root (MAST root of the P2ID note script, Poseidon2-hashed)
+#[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 storage items
+ let storage = active_note::get_storage();
+
+ // Asset: reconstruct from [amount, 0, faucet_suffix, faucet_prefix] encoding
+ let withdraw_asset = Asset::new(
+ Word::from([felt!(0), felt!(0), storage[2], storage[3]]),
+ Word::from([storage[0], felt!(0), felt!(0), felt!(0)]),
+ );
+
+ // Serial number: full 4 Felts (random/unique per note)
+ let serial_num = Word::from([storage[4], storage[5], storage[6], storage[7]]);
+
+ // Tag: single Felt for P2ID note routing
+ let tag = storage[8];
+
+ // Note type: 1 = Public, 2 = Private
+ let note_type = storage[9];
+
+ // Note: P2ID script root (storage[10..13]) is read by the bank account
+ // directly from the active note's storage inside bank_account::withdraw.
+
+ // Call the bank account to withdraw the assets
+ bank_account::withdraw(depositor, withdraw_asset, serial_num, tag, note_type);
+ }
+}
+```
+
+### Note Storage Layout
+
+The withdraw-request-note uses 14 Felt storage items:
+
+```text
+Note Storage (14 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) │
+│ 10-13 │ script_root │ P2ID script MAST root (Poseidon2-hashed, 4 Felts)│
+└───────────────────────────────────────────────────────────────────────────┘
+```
+
+:::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 14-Felt storage 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},
+ auth::AuthSchemeId,
+ note::{P2idNote, P2idNoteStorage, Note, NoteAssets, NoteMetadata, NoteTag, NoteType},
+ transaction::{RawOutputNote, 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 { auth_scheme: AuthSchemeId::Falcon512Poseidon2 }, "TEST", deposit_amount, Some(10))?;
+ let sender = builder.add_existing_wallet_with_assets(
+ Auth::BasicAuth { auth_scheme: AuthSchemeId::Falcon512Poseidon2 },
+ [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_bank_account::bank::initialized")
+ .expect("Valid slot name");
+ let balances_slot =
+ StorageSlotName::new("miden_bank_account::bank::balances")
+ .expect("Valid slot name");
+
+ let mut init_storage_data = InitStorageData::default();
+ init_storage_data.insert_value(
+ StorageValueName::from_slot_name(&initialized_slot),
+ Word::default(),
+ )?;
+ let bank_cfg = AccountCreationConfig {
+ init_storage_data,
+ ..Default::default()
+ };
+ let mut bank_account =
+ create_testing_account_from_package(bank_package.clone(), bank_cfg)?;
+
+ // 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(RawOutputNote::Full(deposit_note.clone()));
+
+ // =========================================================================
+ // CRAFT WITHDRAW REQUEST NOTE (14-Felt storage 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
+
+ // Get the P2ID script root (Poseidon2-hashed MAST root)
+ let p2id_script_root = P2idNote::script_root();
+
+ // Note storage: 14 Felts
+ // [0-3]: withdraw asset (amount, 0, faucet_suffix, faucet_prefix)
+ // [4-7]: serial_num
+ // [8]: tag
+ // [9]: note_type
+ // [10-13]: P2ID script_root
+ let withdraw_request_note_storage = 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,
+ p2id_script_root[0],
+ p2id_script_root[1],
+ p2id_script_root[2],
+ p2id_script_root[3],
+ ];
+
+ let withdraw_request_note = create_testing_note_from_package(
+ withdraw_request_note_package.clone(),
+ sender.id(),
+ NoteCreationConfig {
+ storage: withdraw_request_note_storage,
+ ..Default::default()
+ },
+ )?;
+
+ builder.add_output_note(RawOutputNote::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 = P2idNoteStorage::new(sender.id()).into_recipient(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)
+ .with_tag(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![RawOutputNote::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 Storage (14 Felts)
+/// [0-3]: withdraw asset encoded as [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)
+/// [10-13]: P2ID script_root (MAST root of the P2ID note script, Poseidon2-hashed)
+#[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 storage items
+ let storage = active_note::get_storage();
+
+ // Asset: reconstruct from [amount, 0, faucet_suffix, faucet_prefix] encoding
+ let withdraw_asset = Asset::new(
+ Word::from([felt!(0), felt!(0), storage[2], storage[3]]),
+ Word::from([storage[0], felt!(0), felt!(0), felt!(0)]),
+ );
+
+ // Serial number: full 4 Felts (random/unique per note)
+ let serial_num = Word::from([storage[4], storage[5], storage[6], storage[7]]);
+
+ // Tag: single Felt for P2ID note routing
+ let tag = storage[8];
+
+ // Note type: 1 = Public, 2 = Private
+ let note_type = storage[9];
+
+ // Note: P2ID script root (storage[10..13]) is read by the bank account
+ // directly from the active note's storage inside bank_account::withdraw.
+
+ // Call the bank account to withdraw the assets
+ bank_account::withdraw(depositor, withdraw_asset, serial_num, tag, note_type);
+ }
+}
+```
+
+
+
+## Key Takeaways
+
+1. **`note::build_recipient()`** creates a cryptographic commitment from serial number, script root, and storage items
+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 [examples/miden-bank](https://github.com/0xMiden/miden-tutorials/tree/main/examples/miden-bank) directory.
+:::
+
+## 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/versioned_docs/version-0.14/builder/tutorials/miden-bank/08-complete-flows.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/08-complete-flows.md
new file mode 100644
index 00000000..2406a703
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/08-complete-flows.md
@@ -0,0 +1,594 @@
+---
+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() │ │
+│ │ storage = get_storage() │ │
+│ │ asset = Asset from inputs │ │
+│ │ bank_account::withdraw(...) │ │
+│ └──────────────────────────────┘ │
+│ │ │
+│ ▼ │
+│ 3. WITHDRAW METHOD RUNS │
+│ ┌──────────────────────────────────────────────────────┐ │
+│ │ require_initialized() ✓ Passes │ │
+│ │ current_balance = get_balance(User) → 1000 │ │
+│ │ VALIDATE: 1000 >= 400 ✓ Passes │ ◀ CRITICAL
+│ │ balances[User] = 1000 - 400 → 600 │ │
+│ │ create_p2id_note(...) → Output note │ │
+│ └──────────────────────────────────────────────────────┘ │
+│ │ │
+│ ▼ │
+│ 4. P2ID NOTE CREATED (inside create_p2id_note) │
+│ ┌──────────────────────────────────────────────────────┐ │
+│ │ script_root = storage[10..13] → MAST digest │ │
+│ │ recipient = note::build_recipient( │ │
+│ │ serial_num, script_root, │ │
+│ │ [user.suffix, user.prefix] │ │
+│ │ ) │ │
+│ │ note_idx = output_note::create(tag, note_type, │ │
+│ │ recipient) │ │
+│ │ native_account::remove_asset(400 tokens) │ │
+│ │ output_note::add_asset(400 tokens, note_idx) │ │
+│ └──────────────────────────────────────────────────────┘ │
+│ │ │
+│ ▼ │
+│ 5. TRANSACTION COMPLETES │
+│ Bank storage: balances[User] = 600 │
+│ Bank vault: -400 tokens │
+│ Output: P2ID note with 400 tokens → User │
+│ │ │
+│ ▼ │
+│ 6. USER CONSUMES P2ID NOTE (separate transaction) │
+│ User's wallet receives 400 tokens │
+│ │
+└─────────────────────────────────────────────────────────────────────┘
+```
+
+## Try It: Complete End-to-End Test
+
+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::{P2idNote, P2idNoteStorage, Note, NoteAssets, NoteMetadata, NoteTag, NoteType},
+ transaction::{RawOutputNote, TransactionScript},
+ Felt, Word,
+};
+use miden_client::auth::AuthSchemeId;
+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 { auth_scheme: AuthSchemeId::Falcon512Poseidon2 }, "TEST", deposit_amount, Some(10))?;
+
+ // Create note sender account (the depositor)
+ let sender = builder.add_existing_wallet_with_assets(
+ Auth::BasicAuth { auth_scheme: AuthSchemeId::Falcon512Poseidon2 },
+ [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_bank_account::bank::initialized")
+ .expect("Valid slot name");
+ let balances_slot =
+ StorageSlotName::new("miden_bank_account::bank::balances")
+ .expect("Valid slot name");
+
+ // Create bank account with storage slots
+ let mut init_storage_data = InitStorageData::default();
+ init_storage_data.insert_value(
+ StorageValueName::from_slot_name(&initialized_slot),
+ Word::default(),
+ )?;
+ let bank_cfg = AccountCreationConfig {
+ init_storage_data,
+ ..Default::default()
+ };
+ let mut bank_account =
+ create_testing_account_from_package(bank_package.clone(), bank_cfg)?;
+ 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
+
+ // Get the P2ID script root (Poseidon2-hashed MAST root)
+ let p2id_script_root = P2idNote::script_root();
+
+ // Note storage: 14 Felts
+ // [0-3]: withdraw asset (amount, 0, faucet_suffix, faucet_prefix)
+ // [4-7]: serial_num
+ // [8]: tag
+ // [9]: note_type
+ // [10-13]: P2ID script_root
+ let withdraw_request_note_storage = 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,
+ p2id_script_root[0],
+ p2id_script_root[1],
+ p2id_script_root[2],
+ p2id_script_root[3],
+ ];
+
+ let withdraw_request_note = create_testing_note_from_package(
+ withdraw_request_note_package.clone(),
+ sender.id(),
+ NoteCreationConfig {
+ storage: withdraw_request_note_storage,
+ ..Default::default()
+ },
+ )?;
+
+ // Add to builder
+ builder.add_account(bank_account.clone())?;
+ builder.add_output_note(RawOutputNote::Full(deposit_note.clone()));
+ builder.add_output_note(RawOutputNote::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_canonical_u64()
+ );
+
+ // ═══════════════════════════════════════════════════════════════════
+ // STEP 3: Withdraw tokens
+ // ═══════════════════════════════════════════════════════════════════
+ println!("\n3️⃣ WITHDRAWING TOKENS...");
+ println!(" Withdraw amount: {} tokens", withdraw_amount);
+
+ // Build expected P2ID output note
+ let recipient = P2idNoteStorage::new(sender.id()).into_recipient(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)
+ .with_tag(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![RawOutputNote::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_canonical_u64();
+ 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` | `StorageValue` | 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_storage()` | Get note parameters |
+| `native_account::add_asset()` | Receive into vault |
+| `native_account::remove_asset()` | Send from vault |
+| `output_note::create()` | Create output note |
+| `output_note::add_asset()` | Attach assets to note |
+
+## Key Security Patterns
+
+Remember these critical patterns from this tutorial:
+
+:::danger Always Validate Before Subtraction
+
+```rust
+// ❌ DANGEROUS: Silent underflow!
+let new_balance = current_balance - withdraw_amount;
+
+// ✅ SAFE: Validate first
+assert!(
+ current_balance.as_canonical_u64() >= withdraw_amount.as_canonical_u64(),
+ "Insufficient balance"
+);
+let new_balance = current_balance - withdraw_amount;
+```
+
+:::
+
+:::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_canonical_u64()
+if current_balance.as_canonical_u64() < withdraw_amount.as_canonical_u64() { ... }
+```
+
+:::
+
+## Congratulations! 🎉
+
+You've completed the Miden Bank tutorial! You now understand:
+
+- ✅ **Account components** with storage (`StorageValue` 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](https://docs.miden.xyz/builder/tutorials/rust-compiler/testing)** - Deep dive into testing patterns
+- **[Debugging Guide](https://docs.miden.xyz/builder/tutorials/rust-compiler/debugging)** - Troubleshoot common issues
+- **[Common Pitfalls](https://docs.miden.xyz/builder/tutorials/rust-compiler/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/0xMiden/miden-tutorials/tree/main/examples/miden-bank/contracts)
+- [Integration Tests](https://github.com/0xMiden/miden-tutorials/tree/main/examples/miden-bank/integration/tests)
+- [Test Helpers](https://github.com/0xMiden/miden-tutorials/blob/main/examples/miden-bank/integration/src/helpers.rs)
+ :::
+
+Happy building on Miden! 🚀
diff --git a/versioned_docs/version-0.14/builder/tutorials/miden-bank/_category_.json b/versioned_docs/version-0.14/builder/tutorials/miden-bank/_category_.json
new file mode 100644
index 00000000..a93e1a4c
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/_category_.json
@@ -0,0 +1,4 @@
+{
+ "label": "Miden Bank",
+ "position": 1
+}
diff --git a/versioned_docs/version-0.14/builder/tutorials/miden-bank/index.md b/versioned_docs/version-0.14/builder/tutorials/miden-bank/index.md
new file mode 100644
index 00000000..46f918ab
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden-bank/index.md
@@ -0,0 +1,116 @@
+---
+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, part by part.
+
+## What you'll build
+
+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.
+- **Initialization script** — a transaction script to deploy and initialize the bank.
+
+Each part ends with a **runnable MockChain test** so you can verify what you built works correctly.
+
+## Tutorial structure
+
+Every part builds on the previous one and includes:
+
+- **What you'll build** — clear objectives for the section.
+- **Step-by-step code** — progressively building functionality.
+- **Try it** — a MockChain test to verify your code works.
+- **Complete code** — full code listing for reference.
+
+## Walkthrough
+
+
+
+ Create your project with `miden new` and understand the workspace structure.
+
+
+ Learn `#[component]`, `Value` storage, and `StorageMap` for managing state.
+
+
+ Define constants and validate inputs with assertions.
+
+
+ Handle fungible assets with vault operations and balance tracking.
+
+
+ Write scripts that execute when notes are consumed.
+
+
+ Call account methods from note scripts via bindings.
+
+
+ Write scripts for account initialization and owner operations.
+
+
+ Create P2ID notes programmatically for withdrawals.
+
+
+ Walk through end-to-end deposit and withdraw operations.
+
+
+
+## Prerequisites
+
+- Completed the [Get started guide](../../get-started) — `midenup`, `miden new`, basic tooling.
+- Understanding of Miden concepts: [accounts, notes, transactions](../../smart-contracts).
+- Rust programming experience.
+
+
+This tutorial assumes no prior experience with the Miden Rust compiler. We explain every concept as it comes up.
+
+
+## Concepts covered
+
+| Concept | What it does | 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
+
+The complete source code for this tutorial is available in the [examples/miden-bank](https://github.com/0xMiden/miden-tutorials/tree/main/examples/miden-bank) directory of the miden-tutorials repository:
+
+```bash title=">_ Terminal"
+git clone https://github.com/0xMiden/miden-tutorials.git
+cd miden-tutorials/examples/miden-bank
+```
+
+## Supplementary guides
+
+
+
+ Learn to test your contracts with MockChain for local simulation.
+
+
+ Interpret errors and debug common issues.
+
+
+ Avoid known issues and limitations.
+
+
+
+## Getting help
+
+- Detailed technical reference: [docs.miden.xyz](https://docs.miden.xyz).
+- Join the [Build on Miden](https://t.me/BuildOnMiden) Telegram for support.
+- Review the complete code in the [examples/miden-bank](https://github.com/0xMiden/miden-tutorials/tree/main/examples/miden-bank) directory.
+
+Ready? Start with [Part 0: Project setup](./project-setup).
diff --git a/versioned_docs/version-0.14/builder/tutorials/miden_node_setup.md b/versioned_docs/version-0.14/builder/tutorials/miden_node_setup.md
new file mode 100644
index 00000000..731af74a
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/miden_node_setup.md
@@ -0,0 +1,100 @@
+---
+title: Miden Node Setup
+sidebar_position: 2
+---
+
+# Miden Node Setup Tutorial
+
+To run the Miden tutorial examples, you will need to set up a test environment and connect to a Miden node.
+
+There are two ways to connect to a Miden node:
+
+1. Run the Miden node locally
+2. Connect to the Miden testnet
+
+## Running the Miden node locally
+
+:::tip[Prerequisites]
+Building the node from source requires a C/C++ toolchain (for compiling RocksDB). On **macOS**, make sure you have the Xcode Command Line Tools installed:
+
+```bash
+xcode-select --install
+```
+
+On **Ubuntu**, see the [node installation page](https://docs.miden.xyz/miden-node/operator/installation#install-using-cargo) for the required packages. If you run into `'cstdint' file not found` errors on macOS, see the [troubleshooting section](https://docs.miden.xyz/miden-node/operator/installation#install-using-cargo) on the installation page.
+:::
+
+### Step 1: Install the Miden node
+
+Install the miden-node crate using this command:
+
+```bash
+# Installs from GitHub (crates.io publication was not verified):
+cargo install --locked --git https://github.com/0xMiden/miden-node --tag v0.14.6 miden-node
+```
+
+Check the [miden-node releases](https://github.com/0xMiden/miden-node/releases) for the latest version compatible with your target network.
+
+### Step 2: Initializing the node
+
+To start the node, we first need to generate the genesis file. Create the genesis file using this command:
+
+```bash
+mkdir data
+mkdir accounts
+
+miden-node bundled bootstrap \
+ --data-directory data \
+ --accounts-directory accounts
+```
+
+Expected output:
+
+```
+2025-04-16T18:05:30.049129Z INFO miden_node::commands::store: bin/node/src/commands/store.rs:145: Generating account, index: 0, total: 1
+```
+
+### Step 3: Starting the node
+
+To start the node run this command:
+
+```bash
+miden-node bundled start \
+ --data-directory data \
+ --rpc.url http://0.0.0.0:57291
+```
+
+Expected output:
+
+```
+2025-01-17T12:14:55.432445Z INFO try_build_batches: miden-block-producer: /Users/username/.cargo/registry/src/index.crates.io-6f17d22bba15001f/miden-node-block-producer-0.6.0/src/txqueue/mod.rs:85: close, time.busy: 8.88µs, time.idle: 103µs
+2025-01-17T12:14:57.433162Z INFO try_build_batches: miden-block-producer: /Users/username/.cargo/registry/src/index.crates.io-6f17d22bba15001f/miden-node-block-producer-0.6.0/src/txqueue/mod.rs:85: new
+2025-01-17T12:14:57.433256Z INFO try_build_batches: miden-block-producer: /Users/username/.cargo/registry/src/index.crates.io-6f17d22bba15001f/miden-node-block-producer-0.6.0/src/txqueue/mod.rs:85: close, time.busy: 6.46µs, time.idle: 94.0µs
+```
+
+Congratulations, you now have a Miden node running locally. Now we can start creating a testing environment for building applications on Miden!
+
+The endpoint of the Miden node running locally is:
+
+```
+http://localhost:57291
+```
+
+### Resetting the node
+
+_If you need to reset the local state of the node run this command:_
+
+```bash
+rm -r data
+rm -r accounts
+```
+
+After resetting the state of the node, follow steps 2 and 4 again.
+
+## Connecting to the Miden testnet
+
+To run the tutorial examples using the Miden testnet, use this endpoint:
+
+```bash
+https://rpc.testnet.miden.io:443
+```
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/_category_.json b/versioned_docs/version-0.14/builder/tutorials/recipes/_category_.json
new file mode 100644
index 00000000..c5e2a889
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/_category_.json
@@ -0,0 +1,5 @@
+{
+ "label": "Recipes",
+ "position": 3,
+ "collapsed": true
+}
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/img/count_copy_fpi_diagram.png b/versioned_docs/version-0.14/builder/tutorials/recipes/img/count_copy_fpi_diagram.png
new file mode 100644
index 00000000..f0fd3025
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tutorials/recipes/img/count_copy_fpi_diagram.png differ
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/img/note_creation_masm.png b/versioned_docs/version-0.14/builder/tutorials/recipes/img/note_creation_masm.png
new file mode 100644
index 00000000..9458433b
Binary files /dev/null and b/versioned_docs/version-0.14/builder/tutorials/recipes/img/note_creation_masm.png differ
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/_category_.json b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/_category_.json
new file mode 100644
index 00000000..ce988c87
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/_category_.json
@@ -0,0 +1,5 @@
+{
+ "label": "Rust",
+ "position": 1,
+ "collapsed": true
+}
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/counter_contract_tutorial.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/counter_contract_tutorial.md
new file mode 100644
index 00000000..866deb2b
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/counter_contract_tutorial.md
@@ -0,0 +1,617 @@
+---
+title: "Deploying a Counter Contract"
+sidebar_position: 4
+---
+
+# Deploying a Counter Contract
+
+_Using the Miden client in Rust to deploy and interact with a custom smart contract on Miden_
+
+## Overview
+
+In this tutorial, we will build a simple counter smart contract that maintains a count, deploy it to the Miden testnet, and interact with it by incrementing the count. You can also deploy the counter contract on a locally running Miden node, similar to previous tutorials.
+
+Using a script, we will invoke the increment function within the counter contract to update the count. This tutorial provides a foundational understanding of developing and deploying custom smart contracts on Miden.
+
+## What we'll cover
+
+- Deploying a custom smart contract on Miden
+- Getting up to speed with the basics of Miden assembly
+- Calling procedures in an account
+- Pure vs state changing procedures
+
+## Prerequisites
+
+This tutorial assumes you have a basic understanding of Miden assembly. To quickly get up to speed with Miden assembly (MASM), please play around with running basic Miden assembly programs in the [Miden playground](https://0xMiden.github.io/examples/).
+
+## Step 1: Initialize your repository
+
+Create a new Rust repository for your Miden project and navigate to it with the following command:
+
+```bash
+cargo new miden-counter-contract
+cd miden-counter-contract
+```
+
+Add the following dependencies to your `Cargo.toml` file:
+
+```toml
+[dependencies]
+miden-client = { version = "0.14", features = ["testing", "tonic"] }
+miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" }
+miden-protocol = { version = "0.14" }
+rand = { version = "0.9" }
+serde = { version = "1", features = ["derive"] }
+serde_json = { version = "1.0", features = ["raw_value"] }
+tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] }
+rand_chacha = "0.9.0"
+```
+
+### Set up your `src/main.rs` file
+
+In the previous section, we explained how to instantiate the Miden client. We can reuse the same `initialize_client` function for our counter contract.
+
+Copy and paste the following code into your `src/main.rs` file:
+
+```rust no_run
+use miden_client::auth::NoAuth;
+use miden_client::transaction::TransactionKernel;
+use rand::RngCore;
+use std::{fs, path::Path, sync::Arc};
+
+use miden_client::{
+ address::NetworkId,
+ assembly::{
+ Assembler,
+ CodeBuilder,
+ DefaultSourceManager,
+ Module,
+ ModuleKind,
+ Path as AssemblyPath,
+ },
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::{
+ account::{
+ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode,
+ AccountType, StorageSlot, StorageSlotName,
+ },
+ Word,
+};
+
+fn create_library(
+ library_path: &str,
+ source_code: &str,
+) -> Result, Box> {
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ source_code,
+ source_manager,
+ )?;
+ let library = assembler.assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ Ok(())
+}
+```
+
+_When running the code above, there will be some unused imports, however, we will use these imports later on in the tutorial._
+
+**Note**: Running the code above, will generate a `store.sqlite3` file and a `keystore` directory. The Miden client uses the `store.sqlite3` file to keep track of the state of accounts and notes. The `keystore` directory keeps track of private keys used by accounts. Be sure to add both to your `.gitignore`!
+
+## Step 2: Build the counter contract
+
+For better code organization, we will separate the Miden assembly code from our Rust code.
+
+Create a directory named `masm` at the **root** of your `miden-counter-contract` directory. This will contain our contract and script masm code.
+
+Initialize the `masm` directory:
+
+```bash
+mkdir -p masm/accounts masm/scripts
+```
+
+This will create:
+
+```text
+masm/
+├── accounts/
+└── scripts/
+```
+
+### Custom Miden smart contract
+
+Below is our counter contract. It has a two exported procedures: `get_count` and `increment_count`.
+
+At the beginning of the MASM file, we define our imports. In this case, we import
+`miden::protocol::active_account`, `miden::protocol::native_account`, `miden::core::word`, and
+`miden::core::sys`.
+
+The `miden::protocol::active_account` and `miden::protocol::native_account` modules contain
+procedures for reading and writing contract state. We use `miden::core::word` to convert the slot
+name into a slot ID for the account storage APIs.
+
+The import `miden::core::sys` contains a useful procedure for truncating the operand stack at the
+end of a procedure.
+
+#### Here's a breakdown of what the `get_count` procedure does:
+
+1. Pushes the slot ID prefix and suffix for `miden::tutorials::counter` onto the stack.
+2. Calls `active_account::get_item` with the slot ID.
+3. Calls `sys::truncate_stack` to truncate the stack to size 16.
+4. The value returned from `active_account::get_item` is still on the stack and will be returned
+ when this procedure is called.
+
+#### Here's a breakdown of what the `increment_count` procedure does:
+
+1. Pushes the slot ID prefix and suffix for `miden::tutorials::counter` onto the stack.
+2. Calls `active_account::get_item` with the slot ID.
+3. Pushes `1` onto the stack.
+4. Adds `1` to the count value returned from `active_account::get_item`.
+5. Pushes the slot ID prefix and suffix again so we can write the updated count.
+6. Calls `native_account::set_item` which saves the incremented count to storage.
+7. Calls `sys::truncate_stack` to clean up the stack.
+
+Inside of the `masm/accounts/` directory, create the `counter.masm` file:
+
+```masm
+use miden::protocol::active_account
+use miden::protocol::native_account
+use miden::core::word
+use miden::core::sys
+
+const COUNTER_SLOT = word("miden::tutorials::counter")
+
+#! Inputs: []
+#! Outputs: [count]
+pub proc get_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ # => [count]
+
+ exec.sys::truncate_stack
+ # => [count]
+end
+
+#! Inputs: []
+#! Outputs: []
+pub proc increment_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ # => [count]
+
+ add.1
+ # => [count+1]
+
+ push.COUNTER_SLOT[0..2] exec.native_account::set_item
+ # => []
+
+ exec.sys::truncate_stack
+ # => []
+end
+
+```
+
+**Note**: _It's a good habit to add comments below each line of MASM code with the expected stack state. This improves readability and helps with debugging._
+
+### Authentication Component
+
+**Important**: Starting with Miden Client 0.10.0, all accounts must have an authentication component. For smart contracts that don't require authentication (like our counter contract), we use a `NoAuth` component.
+
+This `NoAuth` component allows any user to interact with the smart contract without requiring signature verification.
+
+### Custom script
+
+This is a Miden assembly script that will call the `increment_count` procedure during the transaction.
+
+The string `{increment_count}` will be replaced with the hash of the `increment_count` procedure in our rust program.
+
+Inside of the `masm/scripts/` directory, create the `counter_script.masm` file:
+
+```masm
+use external_contract::counter_contract
+
+begin
+ call.counter_contract::increment_count
+end
+```
+
+## Step 3: Build the counter smart contract
+
+To build the counter contract copy and paste the following code at the end of your `src/main.rs` file:
+
+```rust ignore
+// -------------------------------------------------------------------------
+// STEP 1: Create a basic counter contract
+// -------------------------------------------------------------------------
+println!("\n[STEP 1] Creating counter contract.");
+
+// Load the MASM file for the counter contract
+let counter_path = Path::new("../masm/accounts/counter.masm");
+let counter_code = fs::read_to_string(counter_path).unwrap();
+
+// Compile the account code into `AccountComponent` with one storage slot
+let counter_slot_name =
+ StorageSlotName::new("miden::tutorials::counter").expect("valid slot name");
+let component_code = CodeBuilder::new()
+ .compile_component_code("external_contract::counter_contract", &counter_code)
+ .unwrap();
+let counter_component = AccountComponent::new(
+ component_code,
+ vec![StorageSlot::with_value(counter_slot_name.clone(), Word::default())],
+ AccountComponentMetadata::new("external_contract::counter_contract", AccountType::all()),
+)
+.unwrap();
+
+// Init seed for the counter contract
+let mut seed = [0_u8; 32];
+client.rng().fill_bytes(&mut seed);
+
+// Build the new `Account` with the component
+let counter_contract = AccountBuilder::new(seed)
+ .account_type(AccountType::RegularAccountImmutableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_component(counter_component.clone())
+ .with_auth_component(NoAuth)
+ .build()
+ .unwrap();
+
+println!(
+ "counter_contract commitment: {:?}",
+ counter_contract.to_commitment()
+);
+println!("counter_contract id: {:?}", counter_contract.id());
+println!("counter_contract storage: {:?}", counter_contract.storage());
+
+client.add_account(&counter_contract, false).await.unwrap();
+```
+
+Run the following command to execute `src/main.rs`:
+
+```bash
+cargo run --release
+```
+
+After the program executes, you should see the counter contract hash and contract id printed to the terminal, for example:
+
+```text
+[STEP 1] Creating counter contract.
+counter_contract commitment: RpoDigest([3700134472268167470, 14878091556015233722, 3335592073702485043, 16978997897830363420])
+counter_contract id: ""
+counter_contract storage: AccountStorage { slots: [Value([0, 0, 0, 0]), Value([0, 0, 0, 0])] }
+```
+
+## Step 4: Incrementing the count
+
+Now that we built the counter contract, lets create a transaction request to increment the count:
+
+Paste the following code at the end of your `src/main.rs` file:
+
+```rust ignore
+// -------------------------------------------------------------------------
+// STEP 2: Call the Counter Contract with a script
+// -------------------------------------------------------------------------
+println!("\n[STEP 2] Call Counter Contract With Script");
+
+// Load the MASM script referencing the increment procedure
+let script_path = Path::new("../masm/scripts/counter_script.masm");
+let script_code = fs::read_to_string(script_path).unwrap();
+
+// Create a library from the counter contract code
+let account_component_lib = create_library(
+ "external_contract::counter_contract",
+ &counter_code,
+)
+.unwrap();
+
+let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&account_component_lib)
+ .unwrap()
+ .compile_tx_script(&script_code)
+ .unwrap();
+
+// Build a transaction request with the custom script
+let tx_increment_request = TransactionRequestBuilder::new()
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+// Execute and submit the transaction
+let tx_id = client
+ .submit_new_transaction(counter_contract.id(), tx_increment_request)
+ .await
+ .unwrap();
+
+println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+);
+
+println!(
+ "Counter contract id: {:?}",
+ counter_contract.id().to_bech32(NetworkId::Testnet)
+);
+
+client.sync_state().await.unwrap();
+
+// Retrieve updated contract data to see the incremented counter
+let account = client
+ .get_account(counter_contract.id())
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+println!(
+ "counter contract storage: {:?}",
+ account.storage().get_item(&counter_slot_name)
+);
+```
+
+**Note**: _Once our counter contract is deployed, other users can increment the count of the smart contract simply by knowing the account id of the contract and the procedure hash of the `increment_count` procedure._
+
+## Summary
+
+The final `src/main.rs` file should look like this:
+
+```rust no_run
+use miden_client::auth::NoAuth;
+use miden_client::transaction::TransactionKernel;
+use rand::RngCore;
+use std::{fs, path::Path, sync::Arc};
+
+use miden_client::{
+ address::NetworkId,
+ assembly::{
+ Assembler,
+ CodeBuilder,
+ DefaultSourceManager,
+ Module,
+ ModuleKind,
+ Path as AssemblyPath,
+ },
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::{
+ account::{
+ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode,
+ AccountType, StorageSlot, StorageSlotName,
+ },
+ Word,
+};
+
+fn create_library(
+ library_path: &str,
+ source_code: &str,
+) -> Result, Box> {
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ source_code,
+ source_manager,
+ )?;
+ let library = assembler.assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // -------------------------------------------------------------------------
+ // STEP 1: Create a basic counter contract
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 1] Creating counter contract.");
+
+ // Load the MASM file for the counter contract
+ let counter_path = Path::new("../masm/accounts/counter.masm");
+ let counter_code = fs::read_to_string(counter_path).unwrap();
+
+ // Compile the account code into `AccountComponent` with one storage slot
+ let counter_slot_name =
+ StorageSlotName::new("miden::tutorials::counter").expect("valid slot name");
+ let component_code = CodeBuilder::new()
+ .compile_component_code("external_contract::counter_contract", &counter_code)
+ .unwrap();
+ let counter_component = AccountComponent::new(
+ component_code,
+ vec![StorageSlot::with_value(counter_slot_name.clone(), Word::default())],
+ AccountComponentMetadata::new("external_contract::counter_contract", AccountType::all()),
+ )
+ .unwrap();
+
+ // Init seed for the counter contract
+ let mut seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut seed);
+
+ // Build the new `Account` with the component
+ let counter_contract = AccountBuilder::new(seed)
+ .account_type(AccountType::RegularAccountImmutableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_component(counter_component.clone())
+ .with_auth_component(NoAuth)
+ .build()
+ .unwrap();
+
+ println!(
+ "counter_contract commitment: {:?}",
+ counter_contract.to_commitment()
+ );
+ println!("counter_contract id: {:?}", counter_contract.id());
+ println!("counter_contract storage: {:?}", counter_contract.storage());
+
+ client.add_account(&counter_contract, false).await.unwrap();
+
+ // -------------------------------------------------------------------------
+ // STEP 2: Call the Counter Contract with a script
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 2] Call Counter Contract With Script");
+
+ // Load the MASM script referencing the increment procedure
+ let script_path = Path::new("../masm/scripts/counter_script.masm");
+ let script_code = fs::read_to_string(script_path).unwrap();
+
+ // Create a library from the counter contract code
+ let account_component_lib = create_library(
+ "external_contract::counter_contract",
+ &counter_code,
+ )
+ .unwrap();
+
+ let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&account_component_lib)
+ .unwrap()
+ .compile_tx_script(&script_code)
+ .unwrap();
+
+ // Build a transaction request with the custom script
+ let tx_increment_request = TransactionRequestBuilder::new()
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+ // Execute and submit the transaction
+ let tx_id = client
+ .submit_new_transaction(counter_contract.id(), tx_increment_request)
+ .await
+ .unwrap();
+
+ println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+
+ println!(
+ "Counter contract id: {:?}",
+ counter_contract.id().to_bech32(NetworkId::Testnet)
+ );
+
+ client.sync_state().await.unwrap();
+
+ // Retrieve updated contract data to see the incremented counter
+ let account = client
+ .get_account(counter_contract.id())
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+ println!(
+ "counter contract storage: {:?}",
+ account.storage().get_item(&counter_slot_name)
+ );
+
+ Ok(())
+}
+```
+
+The output of our program will look something like this:
+
+```text
+Latest block: 374255
+
+[STEP 1] Creating counter contract.
+one or more warnings were emitted
+counter_contract commitment: Word([3964727668949550262, 4265714847747507878, 5784293172192015964, 16803438753763367241])
+counter_contract id: ""
+counter_contract storage: AccountStorage { slots: [Value(Word([0, 0, 0, 0]))] }
+
+[STEP 2] Call Counter Contract With Script
+Stack state before step 2610:
+├── 0: 1
+├── 1: 0
+├── 2: 0
+├── 3: 0
+├── 4: 0
+├── 5: 0
+├── 6: 0
+├── 7: 0
+├── 8: 0
+├── 9: 0
+├── 10: 0
+├── 11: 0
+├── 12: 0
+├── 13: 0
+├── 14: 0
+├── 15: 0
+├── 16: 0
+├── 17: 0
+├── 18: 0
+└── 19: 0
+
+└── (0 more items)
+
+View transaction on MidenScan: https://testnet.midenscan.com/tx/0x9767940bbed7bd3a74c24dc43f1ea8fe90a876dc7925621c217f648c63c4ab7a
+counter contract storage: Ok(Word([0, 0, 0, 1]))
+```
+
+The line in the output `Stack state before step 2505` ouputs the stack state when we call "debug.stack" in the `counter.masm` file.
+
+To increment the count of the counter contract all you need is to know the account id of the counter and the procedure hash of the `increment_count` procedure. To increment the count without deploying the counter each time, you can modify the program above to hardcode the account id of the counter and the procedure hash of the `increment_count` prodedure in the masm script.
+
+### Running the example
+
+To run the full example, navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run this command:
+
+```bash
+cd rust-client
+cargo run --release --bin counter_contract_deploy
+```
+
+### Continue learning
+
+Next tutorial: [Interacting with Public Smart Contracts](public_account_interaction_tutorial.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/create_deploy_tutorial.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/create_deploy_tutorial.md
new file mode 100644
index 00000000..f355e862
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/create_deploy_tutorial.md
@@ -0,0 +1,400 @@
+---
+title: "Creating Accounts and Faucets"
+sidebar_position: 2
+---
+
+# Creating Accounts and Faucets
+
+_Using the Miden client in Rust to create accounts and deploy faucets_
+
+## Overview
+
+In this tutorial, we will create a Miden account for _Alice_ and deploy a fungible faucet. In the next section, we will mint tokens from the faucet to fund her account and transfer tokens from Alice's account to other Miden accounts.
+
+## What we'll cover
+
+- Understanding the differences between public and private accounts & notes
+- Instantiating the Miden client
+- Creating new accounts (public or private)
+- Deploying a faucet to fund an account
+
+## Prerequisites
+
+Before you begin, ensure that a Miden node is running locally in a separate terminal window. To get the Miden node running locally, you can follow the instructions on the [Miden Node Setup](../miden_node_setup.md) page.
+
+## Public vs. private accounts & notes
+
+Before diving into coding, let's clarify the concepts of public and private accounts & notes on Miden:
+
+- Public accounts: The account's data and code are stored on-chain and are openly visible, including its assets.
+- Private accounts: The account's state and logic are off-chain, only known to its owner.
+- Public notes: The note's state is visible to anyone - perfect for scenarios where transparency is desired.
+- Private notes: The note's state is stored off-chain, you will need to share the note data with the relevant parties (via email or Telegram) for them to be able to consume the note.
+
+Note: _The term "account" can be used interchangeably with the term "smart contract" since account abstraction on Miden is handled natively._
+
+_It is useful to think of notes on Miden as "cryptographic cashier's checks" that allow users to send tokens. If the note is private, the note transfer is only known to the sender and receiver._
+
+## Step 1: Initialize your repository
+
+Create a new Rust repository for your Miden project and navigate to it with the following command:
+
+```bash
+cargo new miden-rust-client
+cd miden-rust-client
+```
+
+Add the following dependencies to your `Cargo.toml` file:
+
+```toml
+[dependencies]
+miden-client = { version = "0.14", features = ["testing", "tonic"] }
+miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" }
+miden-protocol = { version = "0.14" }
+rand = { version = "0.9" }
+serde = { version = "1", features = ["derive"] }
+serde_json = { version = "1.0", features = ["raw_value"] }
+tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] }
+rand_chacha = "0.9.0"
+```
+
+## Step 2: Initialize the client
+
+Before interacting with the Miden network, we must instantiate the client. In this step, we specify several parameters:
+
+- **RPC endpoint** - The URL of the Miden node you will connect to.
+- **Client RNG** - The random number generator used by the client, ensuring that the serial number of newly created notes are unique.
+- **SQLite Store** – An SQL database used by the client to store account and note data.
+- **Authenticator** - The component responsible for generating transaction signatures.
+
+Copy and paste the following code into your `src/main.rs` file.
+
+```rust no_run
+use miden_client::auth::{AuthSchemeId, AuthSingleSig};
+use rand::RngCore;
+use std::sync::Arc;
+use tokio::time::Duration;
+
+use miden_client::{
+ account::{
+ component::{BasicFungibleFaucet, BasicWallet},
+ AccountId,
+ },
+ address::NetworkId,
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ note::{NoteType, P2idNote},
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_protocol::account::AccountIdVersion;
+use miden_client::{
+ account::{AccountBuilder, AccountStorageMode, AccountType},
+ asset::{FungibleAsset, TokenSymbol},
+ Felt,
+};
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ Ok(())
+}
+```
+
+_When running the code above, there will be some unused imports, however, we will use these imports later on in the tutorial._
+
+**Note**: Running the code above, will generate a `store.sqlite3` file and a `keystore` directory. The Miden client uses the `store.sqlite3` file to keep track of the state of accounts and notes. The `keystore` directory keeps track of private keys used by accounts. Be sure to add both to your `.gitignore`!
+
+Run the following command to execute `src/main.rs`:
+
+```bash
+cargo run --release
+```
+
+After the program executes, you should see the latest block number printed to the terminal, for example:
+
+```text
+Latest block number: 3855
+```
+
+## Step 3: Creating a wallet
+
+Now that we've initialized the client, we can create a wallet for Alice.
+
+To create a wallet for Alice using the Miden client, we define the account type as mutable or immutable and specify whether it is public or private. A mutable wallet means you can change the account code after deployment. A wallet on Miden is simply an account with standardized code.
+
+In the example below we create a mutable public account for Alice.
+
+Add this snippet to the end of your file in the `main()` function:
+
+```rust ignore
+//------------------------------------------------------------
+// STEP 1: Create a basic wallet for Alice
+//------------------------------------------------------------
+println!("\n[STEP 1] Creating a new account for Alice");
+
+// Account seed
+let mut init_seed = [0_u8; 32];
+client.rng().fill_bytes(&mut init_seed);
+
+let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+// Build the account
+let alice_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicWallet)
+ .build()
+ .unwrap();
+
+// Add the account to the client
+client.add_account(&alice_account, false).await?;
+
+// Add the key pair to the keystore
+use miden_client::keystore::Keystore;
+keystore.add_key(&key_pair, alice_account.id()).await.unwrap();
+
+let alice_account_id_bech32 = alice_account.id().to_bech32(NetworkId::Testnet);
+println!("Alice's account ID: {:?}", alice_account_id_bech32);
+```
+
+## Step 4: Deploying a fungible faucet
+
+To provide Alice with testnet assets, we must first deploy a faucet. A faucet account on Miden mints fungible tokens.
+
+We'll create a public faucet with a token symbol, decimals, and a max supply. We will use this faucet to mint tokens to Alice's account in the next section.
+
+Add this snippet to the end of your file in the `main()` function:
+
+```rust ignore
+//------------------------------------------------------------
+// STEP 2: Deploy a fungible faucet
+//------------------------------------------------------------
+println!("\n[STEP 2] Deploying a new fungible faucet.");
+
+// Faucet seed
+let mut init_seed = [0u8; 32];
+client.rng().fill_bytes(&mut init_seed);
+
+// Faucet parameters
+let symbol = TokenSymbol::new("MID").unwrap();
+let decimals = 8;
+let max_supply = Felt::new(1_000_000);
+
+// Generate key pair
+let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+// Build the faucet account
+let faucet_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply).unwrap())
+ .build()
+ .unwrap();
+
+// Add the faucet to the client
+client.add_account(&faucet_account, false).await?;
+
+// Add the key pair to the keystore
+use miden_client::keystore::Keystore;
+keystore.add_key(&key_pair, faucet_account.id()).await.unwrap();
+
+let faucet_account_id_bech32 = faucet_account.id().to_bech32(NetworkId::Testnet);
+println!("Faucet account ID: {:?}", faucet_account_id_bech32);
+
+// Resync to show newly deployed faucet
+client.sync_state().await?;
+tokio::time::sleep(Duration::from_secs(2)).await;
+```
+
+_When tokens are minted from this faucet, each token batch is represented as a "note" (UTXO). You can think of a Miden Note as a cryptographic cashier's check that has certain spend conditions attached to it._
+
+## Summary
+
+Your updated `main()` function in `src/main.rs` should look like this:
+
+```rust no_run
+use miden_client::auth::{AuthSchemeId, AuthSingleSig};
+use rand::RngCore;
+use std::sync::Arc;
+use tokio::time::Duration;
+
+use miden_client::{
+ account::{
+ component::{AuthControlled, BasicFungibleFaucet, BasicWallet},
+ AccountId,
+ },
+ address::NetworkId,
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::{NoteType, P2idNote},
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_protocol::account::AccountIdVersion;
+use miden_client::{
+ account::{AccountBuilder, AccountStorageMode, AccountType},
+ asset::{FungibleAsset, TokenSymbol},
+ Felt,
+};
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ //------------------------------------------------------------
+ // STEP 1: Create a basic wallet for Alice
+ //------------------------------------------------------------
+ println!("\n[STEP 1] Creating a new account for Alice");
+
+ // Account seed
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ // Build the account
+ let alice_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicWallet)
+ .build()
+ .unwrap();
+
+ // Add the account to the client
+ client.add_account(&alice_account, false).await?;
+
+ // Add the key pair to the keystore
+ keystore.add_key(&key_pair, alice_account.id()).await.unwrap();
+
+ let alice_account_id_bech32 = alice_account.id().to_bech32(NetworkId::Testnet);
+ println!("Alice's account ID: {:?}", alice_account_id_bech32);
+
+ //------------------------------------------------------------
+ // STEP 2: Deploy a fungible faucet
+ //------------------------------------------------------------
+ println!("\n[STEP 2] Deploying a new fungible faucet.");
+
+ // Faucet seed
+ let mut init_seed = [0u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ // Faucet parameters
+ let symbol = TokenSymbol::new("MID").unwrap();
+ let decimals = 8;
+ let max_supply = Felt::new(1_000_000);
+
+ // Generate key pair
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ // Build the faucet account
+ let faucet_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply).unwrap())
+ .with_component(AuthControlled::allow_all())
+ .build()
+ .unwrap();
+
+ // Add the faucet to the client
+ client.add_account(&faucet_account, false).await?;
+
+ // Add the key pair to the keystore
+ keystore.add_key(&key_pair, faucet_account.id()).await.unwrap();
+
+ let faucet_account_id_bech32 = faucet_account.id().to_bech32(NetworkId::Testnet);
+ println!("Faucet account ID: {:?}", faucet_account_id_bech32);
+
+ // Resync to show newly deployed faucet
+ client.sync_state().await?;
+ tokio::time::sleep(Duration::from_secs(2)).await;
+
+ Ok(())
+}
+```
+
+Let's run the `src/main.rs` program again:
+
+```bash
+cargo run --release
+```
+
+The output will look like this:
+
+```text
+Latest block: 17771
+
+[STEP 1] Creating a new account for Alice
+Alice's account ID: "0x3cb3e596d14ad410000017901eaa7b"
+
+[STEP 2] Deploying a new fungible faucet.
+Faucet account ID: "0x6ad1894ac233e4200000088311bb6b"
+```
+
+In this section we explained how to instantiate the Miden client, create a wallet account, and deploy a faucet.
+
+In the next section we will cover how to mint tokens from the faucet, consume notes, and send tokens to other accounts.
+
+### Running the example
+
+To run a full working example navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run this command:
+
+```bash
+cd rust-client
+cargo run --release --bin create_mint_consume_send
+```
+
+### Continue learning
+
+Next tutorial: [Mint, Consume, and Create Notes](mint_consume_create_tutorial.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/creating_notes_in_masm_tutorial.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/creating_notes_in_masm_tutorial.md
new file mode 100644
index 00000000..4f831b41
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/creating_notes_in_masm_tutorial.md
@@ -0,0 +1,523 @@
+---
+title: "How to Create Notes in Miden Assembly"
+sidebar_position: 11
+---
+
+# Creating Notes in Miden Assembly
+
+_Creating notes inside the MidenVM using Miden assembly_
+
+## Overview
+
+In this tutorial, we will create a custom note that generates a copy of itself when it is consumed by an account. The purpose of this tutorial is to demonstrate how to create notes inside the MidenVM using Miden assembly (MASM). By the end of this tutorial, you will understand how to write MASM code that creates notes.
+
+## What We'll Cover
+
+- Computing the note inputs commitment in MASM
+- Creating notes in MASM
+
+## Prerequisites
+
+This tutorial assumes you have a basic understanding of Miden assembly and that you have completed the tutorial on [creating a custom note](./custom_note_how_to.md).
+
+## Why Creating Notes in MASM Is Useful
+
+Being able to create a note in MASM enables you to build various types of applications. Creating a note during the consumption of another note or from an account allows you to develop complex DeFi applications.
+
+Here are some tangible examples of when creating a note in MASM is useful in a DeFi context:
+
+- Creating snapshots of an account's state at a specific point in time (not possible in an EVM context)
+- Representing partially fillable buy/sell orders as notes (SWAPP)
+- Handling withdrawals from a smart contract
+
+## What We Will Be Building
+
+
+
+In the diagram above, note A is consumed by an account, and during the transaction, note A' is created.
+
+In this tutorial, we will create a note that contains an asset. When consumed, it outputs a copy of itself and allows the consuming account to take half of the asset. Although this type of note would not be used in a real-world context, it demonstrates several key concepts for writing MASM code that can create notes.
+
+## Step 1: Initialize Your Repository
+
+Create a new Rust repository for your Miden project and navigate to it with the following command:
+
+```bash
+cargo new miden-project
+cd miden-project
+```
+
+Add the following dependencies to your `Cargo.toml` file:
+
+```toml
+[dependencies]
+miden-client = { version = "0.14", features = ["testing", "tonic"] }
+miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" }
+miden-protocol = { version = "0.14" }
+rand = { version = "0.9" }
+serde = { version = "1", features = ["derive"] }
+serde_json = { version = "1.0", features = ["raw_value"] }
+tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] }
+rand_chacha = "0.9.0"
+```
+
+## Step 2: Write the Note Script
+
+For better code organization, we will separate the Miden assembly code from our Rust code.
+
+Create a directory named `masm` at the **root** of your `miden-project` directory. This directory will contain our contract and MASM script code.
+
+Initialize the `masm` directory:
+
+```bash
+mkdir masm/notes
+```
+
+This will create:
+
+```text
+masm/
+└── notes/
+```
+
+Inside the `masm/notes/` directory, create the file `iterative_output_note.masm`:
+
+```masm
+use miden::protocol::active_note
+use miden::protocol::note
+use miden::protocol::output_note
+use miden::core::sys
+use miden::standards::wallets::basic->wallet
+
+# Memory Addresses
+# get_assets writes: ASSET_KEY at ASSET_KEY_PTR, ASSET_VALUE at ASSET_KEY_PTR+4 (ASSET_SIZE=8)
+const ASSET_KEY_PTR=0
+const ASSET_VALUE_PTR=4
+const ASSET_HALF_VALUE_PTR=8 # half-amount ASSET_VALUE stored here
+const ACCOUNT_ID_PREFIX=12 # storage: [prefix, suffix, tag, 0]
+const TAG=14 # = ACCOUNT_ID_PREFIX + 2
+
+# => []
+begin
+ # Drop word if user accidentally pushes note_args
+ dropw
+ # => []
+
+ # Get asset contained in note into memory (ASSET_KEY at 0, ASSET_VALUE at 4)
+ push.ASSET_KEY_PTR exec.active_note::get_assets drop drop
+ # => []
+
+ # Load ASSET_VALUE and compute half amount
+ padw push.ASSET_VALUE_PTR mem_loadw_le
+ # => [av0, av1, av2, av3] (av0 = amount for fungible asset, av1/av2/av3 = 0)
+
+ # Halve the amount (av0 is amount for fungible assets in v0.14)
+ push.2 div
+ # => [av0/2, av1, av2, av3]
+
+ # Store as ASSET_HALF_VALUE
+ mem_storew_le.ASSET_HALF_VALUE_PTR dropw
+ # => []
+
+ # Receive all assets from note into the account wallet
+ exec.wallet::add_assets_to_account
+ # => []
+
+ # Push script hash
+ exec.active_note::get_script_root
+ # => [SCRIPT_HASH]
+
+ # Get the current note serial number
+ exec.active_note::get_serial_number
+ # => [SERIAL_NUM, SCRIPT_HASH]
+
+ # Increment the last element of the serial number by 1
+ # (serial_num[3] is at depth 3; matches Rust: serial_num[3] + 1)
+ swap.3 push.1 add swap.3
+ # => [SERIAL_NUM+1, SCRIPT_HASH]
+
+ # Load note storage into memory for recipient construction
+ push.ACCOUNT_ID_PREFIX
+ exec.active_note::get_storage
+ # => [num_storage_items, dest_ptr, SERIAL_NUM+1, SCRIPT_HASH]
+
+ swap
+ # => [dest_ptr, num_storage_items, SERIAL_NUM+1, SCRIPT_HASH]
+
+ exec.note::build_recipient
+ # => [RECIPIENT]
+
+ # Push note type to stack (public note = 1)
+ push.1
+ # => [note_type, RECIPIENT]
+
+ # Load tag from memory
+ mem_load.TAG
+ # => [tag, note_type, RECIPIENT]
+
+ exec.output_note::create
+ # => [note_idx]
+
+ # Build [ASSET_KEY, ASSET_HALF_VALUE, note_idx] for move_asset_to_note
+ # Inputs: [ASSET_KEY, ASSET_VALUE, note_idx, pad(7)]
+
+ # Push ASSET_HALF_VALUE (note_idx moves to depth 4)
+ padw push.ASSET_HALF_VALUE_PTR mem_loadw_le
+ # => [ASSET_HALF_VALUE, note_idx]
+
+ # Push ASSET_KEY (ASSET_HALF_VALUE moves to depth 4, note_idx to depth 8)
+ padw push.ASSET_KEY_PTR mem_loadw_le
+ # => [ASSET_KEY, ASSET_HALF_VALUE, note_idx]
+
+ call.wallet::move_asset_to_note
+ # => [pad(16)]
+
+ dropw dropw dropw dropw
+ # => []
+
+ exec.sys::truncate_stack
+ # => []
+end
+```
+
+### How the Assembly Code Works:
+
+1. **Retrieving the asset:**
+ The note calls `active_note::get_assets` to write the asset into memory, with `ASSET_KEY` at address 0 and `ASSET_VALUE` at address 4. It halves the amount in `ASSET_VALUE` and stores it at `ASSET_HALF_VALUE_PTR`. Finally, it calls `wallet::add_assets_to_account` to receive all note assets into the consuming account.
+2. **Getting the script hash and serial number:**
+ The note script calls `active_note::get_script_root` to fetch the script hash and `active_note::get_serial_number` to fetch the current serial number, then increments element 3 (the last element) by 1 to avoid duplicate recipients.
+3. **Building the `RECIPIENT`:**
+ The script loads the note storage into memory with `active_note::get_storage`, then calls `note::build_recipient`. This computes the storage commitment and stores the preimage in the advice map, which is required for public notes.
+4. **Creating the note:**
+ To create the note, the script pushes the note type and tag onto the stack, then calls the `output_note::create` procedure, which returns the note index.
+5. **Moving assets to the note:**
+ After the note is created, the script loads `ASSET_KEY` and `ASSET_HALF_VALUE` from memory onto the stack and calls `wallet::move_asset_to_note` with the note index.
+6. **Stack cleanup:**
+ Finally, the script cleans up the stack by calling `sys::truncate_stack`.
+
+## Step 3: Rust Program
+
+With the Miden assembly note script written, we can move on to writing the Rust script to create and consume the note.
+
+Copy and paste the following code into your `src/main.rs` file.
+
+```rust no_run
+use miden_client::auth::{AuthSchemeId, AuthSingleSig};
+use rand::RngCore;
+use std::{fs, path::Path, sync::Arc};
+use tokio::time::{sleep, Duration};
+
+use miden_client::{
+ account::{
+ component::{AuthControlled, BasicFungibleFaucet, BasicWallet},
+ Account,
+ },
+ address::NetworkId,
+ asset::{FungibleAsset, TokenSymbol},
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ crypto::FeltRng,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::{
+ Note, NoteAssets, NoteMetadata, NoteRecipient, NoteStorage, NoteTag, NoteType,
+ },
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ Client, ClientError, Felt,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::{
+ account::{AccountBuilder, AccountStorageMode, AccountType},
+ note::NoteDetails,
+};
+
+// Helper to create a basic account
+async fn create_basic_account(
+ client: &mut Client,
+ keystore: &Arc,
+) -> Result {
+ let mut init_seed = [0u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ let account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicWallet)
+ .build()
+ .unwrap();
+
+ client.add_account(&account, false).await?;
+ keystore.add_key(&key_pair, account.id()).await.unwrap();
+
+ Ok(account)
+}
+
+async fn create_basic_faucet(
+ client: &mut Client,
+ keystore: &Arc,
+) -> Result {
+ let mut init_seed = [0u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+ let symbol = TokenSymbol::new("MID").unwrap();
+ let decimals = 8;
+ let max_supply = Felt::new(1_000_000);
+
+ let account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply).unwrap())
+ .with_component(AuthControlled::allow_all())
+ .build()
+ .unwrap();
+
+ client.add_account(&account, false).await?;
+ keystore.add_key(&key_pair, account.id()).await.unwrap();
+
+ Ok(account)
+}
+
+// Helper to wait until an account has the expected number of consumable notes
+async fn wait_for_notes(
+ client: &mut Client,
+ account_id: &Account,
+ expected: usize,
+) -> Result<(), ClientError> {
+ loop {
+ client.sync_state().await?;
+ let notes = client.get_consumable_notes(Some(account_id.id())).await?;
+ if notes.len() >= expected {
+ break;
+ }
+ println!(
+ "{} consumable notes found for account {}. Waiting...",
+ notes.len(),
+ account_id.id().to_bech32(NetworkId::Testnet)
+ );
+ sleep(Duration::from_secs(3)).await;
+ }
+ Ok(())
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // -------------------------------------------------------------------------
+ // STEP 1: Create accounts and deploy faucet
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 1] Creating new accounts");
+ let alice_account = create_basic_account(&mut client, &keystore).await?;
+ println!(
+ "Alice's account ID: {:?}",
+ alice_account.id().to_bech32(NetworkId::Testnet)
+ );
+ let bob_account = create_basic_account(&mut client, &keystore).await?;
+ println!(
+ "Bob's account ID: {:?}",
+ bob_account.id().to_bech32(NetworkId::Testnet)
+ );
+
+ println!("\nDeploying a new fungible faucet.");
+ let faucet = create_basic_faucet(&mut client, &keystore).await?;
+ println!(
+ "Faucet account ID: {:?}",
+ faucet.id().to_bech32(NetworkId::Testnet)
+ );
+ client.sync_state().await?;
+
+ // -------------------------------------------------------------------------
+ // STEP 2: Mint tokens with P2ID
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 2] Mint tokens with P2ID");
+ let faucet_id = faucet.id();
+ let amount: u64 = 100;
+ let mint_amount = FungibleAsset::new(faucet_id, amount).unwrap();
+
+ let tx_req = TransactionRequestBuilder::new()
+ .build_mint_fungible_asset(
+ mint_amount,
+ alice_account.id(),
+ NoteType::Public,
+ client.rng(),
+ )
+ .unwrap();
+
+ let tx_id = client.submit_new_transaction(faucet.id(), tx_req).await?;
+ println!("Minted tokens. TX: {:?}", tx_id);
+
+ wait_for_notes(&mut client, &alice_account, 1).await?;
+
+ // Consume the minted note
+ let consumable_notes = client
+ .get_consumable_notes(Some(alice_account.id()))
+ .await?;
+
+ if let Some((note_record, _)) = consumable_notes.first() {
+ let note: Note = note_record.clone().try_into()?;
+ let consume_req = TransactionRequestBuilder::new().build_consume_notes(vec![note])?;
+
+ let tx_id = client
+ .submit_new_transaction(alice_account.id(), consume_req)
+ .await?;
+ println!("Consumed minted note. TX: {:?}", tx_id);
+ }
+
+ client.sync_state().await?;
+
+ // -------------------------------------------------------------------------
+ // STEP 3: Create iterative output note
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 3] Create iterative output note");
+
+ let code = fs::read_to_string(Path::new("../masm/notes/iterative_output_note.masm")).unwrap();
+ let serial_num = client.rng().draw_word();
+
+ // Create note metadata and tag
+ let tag = NoteTag::new(0);
+ let metadata = NoteMetadata::new(alice_account.id(), NoteType::Public).with_tag(tag);
+ let note_script = client.code_builder().compile_note_script(&code).unwrap();
+ let note_storage = NoteStorage::new(vec![
+ alice_account.id().prefix().as_felt(),
+ alice_account.id().suffix(),
+ tag.into(),
+ Felt::new(0),
+ ])
+ .unwrap();
+
+ let recipient = NoteRecipient::new(serial_num, note_script.clone(), note_storage.clone());
+ let vault = NoteAssets::new(vec![mint_amount.into()])?;
+ let custom_note = Note::new(vault, metadata, recipient);
+
+ let note_req = TransactionRequestBuilder::new()
+ .own_output_notes(vec![custom_note.clone()])
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(alice_account.id(), note_req)
+ .await?;
+ println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+
+ client.sync_state().await?;
+
+ // -------------------------------------------------------------------------
+ // STEP 4: Consume the iterative output note
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 4] Bob consumes the note and creates a copy");
+
+ // Increment the serial number for the new note
+ let serial_num_1 = [
+ serial_num[0],
+ serial_num[1],
+ serial_num[2],
+ Felt::new(serial_num[3].as_canonical_u64() + 1),
+ ]
+ .into();
+
+ // Reuse the note_script and note_storage
+ let recipient = NoteRecipient::new(serial_num_1, note_script, note_storage);
+
+ // Note: Change metadata to include Bob's account as the creator
+ let metadata = NoteMetadata::new(bob_account.id(), NoteType::Public).with_tag(tag);
+
+ let asset_amount_1 = FungibleAsset::new(faucet_id, 50).unwrap();
+ let vault = NoteAssets::new(vec![asset_amount_1.into()])?;
+ let output_note = Note::new(vault, metadata, recipient);
+
+ let consume_custom_req = TransactionRequestBuilder::new()
+ .input_notes([(custom_note, None)])
+ .expected_future_notes(vec![(
+ NoteDetails::from(output_note.clone()),
+ output_note.metadata().tag(),
+ )
+ .clone()])
+ .expected_output_recipients(vec![output_note.recipient().clone()])
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(bob_account.id(), consume_custom_req)
+ .await?;
+ println!(
+ "Consumed Note Tx on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+
+ Ok(())
+}
+```
+
+Run the following command to execute `src/main.rs`:
+
+```bash
+cargo run --release
+```
+
+The output will look something like this:
+
+```text
+Latest block: 4186
+
+[STEP 1] Creating new accounts
+Alice's account ID: "mtst1aqnztxg76d5exyr7ja05pzhvegx3jc84"
+Bob's account ID: "mtst1az0mfyzm3xqe2yrezucvmc24dv5gset0"
+
+Deploying a new fungible faucet.
+Faucet account ID: "mtst1ary7kplxvatxkgpes4llcc53yu8px433"
+
+[STEP 2] Mint tokens with P2ID
+Minted tokens. TX: 0x8577213057226b7d0545d73f6e1bc416354a324089450cb859f5784c133d4cc0
+0 consumable notes found for account mtst1aqnztxg76d5exyr7ja05pzhvegx3jc84. Waiting...
+Consumed minted note. TX: 0xd93e791d3283192df121de4cf938a4f9cfaed48f4e593e1b82d147e2d642b7bd
+
+[STEP 3] Create iterative output note
+View transaction on MidenScan: https://testnet.midenscan.com/tx/0xcd71a1d87c60ab7632a7836723fff4014b02992ef7ea5d3fe2030a971e795a8a
+
+[STEP 4] Bob consumes the note and creates a copy
+Consumed Note Tx on MidenScan: https://testnet.midenscan.com/tx/0x4d91519c180874c931480fa58620f864fd7c7aada35ada2d40082291298084bb
+Account delta: AccountVaultDelta { fungible: FungibleAssetDelta({V0(Accoun
+```
+
+---
+
+### Running the example
+
+To run the full example, navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run this command:
+
+```bash
+cd rust-client
+cargo run --release --bin note_creation_in_masm
+```
+
+### Continue learning
+
+Next tutorial: [Delegated Proving](./delegated_proving_tutorial.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/custom_note_how_to.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/custom_note_how_to.md
new file mode 100644
index 00000000..7fc58599
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/custom_note_how_to.md
@@ -0,0 +1,437 @@
+---
+title: "How To Create Notes with Custom Logic"
+sidebar_position: 7
+---
+
+# How to Create a Custom Note
+
+_Creating notes with custom logic_
+
+## Overview
+
+In this guide, we will create a custom note on Miden that can only be consumed by someone who knows the preimage of the hash stored in the note. This approach securely embeds assets into the note and restricts spending to those who possess the correct secret number.
+
+By following the steps below and using the Miden Assembly code and Rust example, you will learn how to:
+
+- Create a note with custom logic.
+- Leverage Miden’s privacy features to keep certain transaction details private.
+
+Unlike Ethereum, where all pending transactions are publicly visible in the mempool, Miden enables you to partially or completely hide transaction details.
+
+## What we'll cover
+
+- Writing Miden assembly for a note
+- Consuming notes
+
+## Step-by-step process
+
+### 1. Creating two accounts: Alice & Bob
+
+First, we create two basic accounts for the two users:
+
+- **Alice:** The account that creates and funds the custom note.
+- **Bob:** The account that will consume the note if they know the correct secret.
+
+### 2. Hashing the secret number
+
+The security of the custom note hinges on a secret number. Here, we will:
+
+- Choose a secret number (for example, an array of four integers).
+- For simplicity, we're only hashing 4 elements. Therefore, we prepend an empty word—consisting of 4 zero integers—as a placeholder. This is required by the RPO hashing algorithm to ensure the input has the correct structure and length for proper processing.
+- Compute the hash of the secret. The resulting hash will serve as the note’s input, meaning that the note can only be consumed if the secret number’s hash preimage is provided during consumption.
+
+### 3. Creating the custom note
+
+Now, combine the minted asset and the secret hash to build the custom note. The note is created using the following key steps:
+
+1. **Note Inputs:**
+ - The note is set up with the asset and the hash of the secret number as its input.
+2. **Miden Assembly Code:**
+ - The Miden assembly note script ensures that the note can only be consumed if the provided secret, when hashed, matches the hash stored in the note input.
+
+Below is the Miden Assembly code for the note:
+
+```masm
+use miden::protocol::active_note
+use miden::standards::wallets::basic->wallet
+
+# CONSTANTS
+# =================================================================================================
+
+const EXPECTED_DIGEST_PTR=0
+
+# ERRORS
+# =================================================================================================
+
+const ERROR_DIGEST_MISMATCH="Expected digest does not match computed digest"
+
+#! Inputs (arguments): [HASH_PREIMAGE_SECRET]
+#! Outputs: []
+#!
+#! Note storage is assumed to be as follows:
+#! => EXPECTED_DIGEST
+begin
+ # => HASH_PREIMAGE_SECRET
+ # Hashing the secret number
+ hash
+ # => [DIGEST]
+
+ # Writing the note storage to memory
+ push.EXPECTED_DIGEST_PTR exec.active_note::get_storage drop drop
+
+ # Pad stack and load expected digest from memory (LE: mem[addr] ends up on top)
+ padw push.EXPECTED_DIGEST_PTR mem_loadw_le
+ # => [EXPECTED_DIGEST, DIGEST]
+
+ # Assert that the note input matches the digest
+ # Will fail if the two hashes do not match
+ assert_eqw.err=ERROR_DIGEST_MISMATCH
+ # => []
+
+ # ---------------------------------------------------------------------------------------------
+ # If the check is successful, we allow for the asset to be consumed
+ # ---------------------------------------------------------------------------------------------
+
+ # Add all assets from the note to the account
+ exec.wallet::add_assets_to_account
+ # => []
+end
+```
+
+### How the assembly code works:
+
+1. **Constants and Error Handling:**
+ The code defines a memory pointer (`EXPECTED_DIGEST_PTR`) for storing the expected hash and an error message for digest mismatches.
+2. **Passing the Secret:**
+ The secret number is passed as `Note Arguments` into the note.
+3. **Hashing the Secret:**
+ The `hash` instruction applies a Poseidon2 hash permutation to the secret number, resulting in a digest that takes up four stack elements.
+4. **Digest Comparison:**
+ The assembly code loads the expected digest from note storage into memory, then reads it back with `mem_loadw_le` (which places `mem[addr]` on top, matching the hash output order) and compares with the computed hash. If they don't match, the transaction fails with a clear error message.
+5. **Asset Transfer:**
+ If the hash matches, `wallet::add_assets_to_account` transfers all note assets into the consuming account's vault.
+
+### 5. Consuming the note
+
+With the note created, Bob can now consume it—but only if he provides the correct secret. When Bob initiates the transaction to consume the note, he must supply the same secret number used when Alice created the note. The custom note’s logic will hash the secret and compare it with its stored hash. If they match, Bob’s wallet receives the asset.
+
+---
+
+## Full Rust code example
+
+The following Rust code demonstrates how to implement the steps outlined above using the Miden client library:
+
+```rust no_run
+use miden_client::auth::{AuthSchemeId, AuthSingleSig};
+use rand::RngCore;
+use std::{fs, path::Path, sync::Arc};
+use tokio::time::{sleep, Duration};
+
+use miden_client::{
+ account::{
+ component::{AuthControlled, BasicFungibleFaucet, BasicWallet},
+ Account,
+ },
+ address::NetworkId,
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ crypto::FeltRng,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::{
+ Note, NoteAssets, NoteMetadata, NoteRecipient, NoteStorage, NoteTag, NoteType,
+ },
+ rpc::{Endpoint, GrpcClient},
+ store::TransactionFilter,
+ transaction::{TransactionId, TransactionRequestBuilder, TransactionStatus},
+ Client, ClientError, Felt,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::{
+ account::{AccountBuilder, AccountStorageMode, AccountType},
+ asset::{FungibleAsset, TokenSymbol},
+};
+use miden_protocol::Hasher;
+
+// Helper to create a basic account
+async fn create_basic_account(
+ client: &mut Client,
+ keystore: &Arc,
+) -> Result {
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ let account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicWallet)
+ .build()
+ .unwrap();
+
+ client.add_account(&account, false).await?;
+ keystore.add_key(&key_pair, account.id()).await.unwrap();
+
+ Ok(account)
+}
+
+async fn create_basic_faucet(
+ client: &mut Client,
+ keystore: &Arc,
+) -> Result {
+ let mut init_seed = [0u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+ let symbol = TokenSymbol::new("MID").unwrap();
+ let decimals = 8;
+ let max_supply = Felt::new(1_000_000);
+
+ let account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply).unwrap())
+ .with_component(AuthControlled::allow_all())
+ .build()
+ .unwrap();
+
+ client.add_account(&account, false).await?;
+ keystore.add_key(&key_pair, account.id()).await.unwrap();
+
+ Ok(account)
+}
+
+/// Waits for a specific transaction to be committed.
+async fn wait_for_tx(
+ client: &mut Client,
+ tx_id: TransactionId,
+) -> Result<(), ClientError> {
+ loop {
+ client.sync_state().await?;
+
+ // Check transaction status
+ let txs = client
+ .get_transactions(TransactionFilter::Ids(vec![tx_id]))
+ .await?;
+ let tx_committed = if !txs.is_empty() {
+ matches!(txs[0].status, TransactionStatus::Committed { .. })
+ } else {
+ false
+ };
+
+ if tx_committed {
+ println!("✅ transaction {} committed", tx_id.to_hex());
+ break;
+ }
+
+ println!(
+ "Transaction {} not yet committed. Waiting...",
+ tx_id.to_hex()
+ );
+ sleep(Duration::from_secs(2)).await;
+ }
+ Ok(())
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // -------------------------------------------------------------------------
+ // STEP 1: Create accounts and deploy faucet
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 1] Creating new accounts");
+ let alice_account = create_basic_account(&mut client, &keystore).await?;
+ println!(
+ "Alice's account ID: {:?}",
+ alice_account.id().to_bech32(NetworkId::Testnet)
+ );
+ let bob_account = create_basic_account(&mut client, &keystore).await?;
+ println!(
+ "Bob's account ID: {:?}",
+ bob_account.id().to_bech32(NetworkId::Testnet)
+ );
+
+ println!("\nDeploying a new fungible faucet.");
+ let faucet = create_basic_faucet(&mut client, &keystore).await?;
+ println!(
+ "Faucet account ID: {:?}",
+ faucet.id().to_bech32(NetworkId::Testnet)
+ );
+ client.sync_state().await?;
+
+ // -------------------------------------------------------------------------
+ // STEP 2: Mint tokens with P2ID
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 2] Mint tokens with P2ID");
+ let faucet_id = faucet.id();
+ let amount: u64 = 100;
+ let mint_amount = FungibleAsset::new(faucet_id, amount).unwrap();
+ let tx_request = TransactionRequestBuilder::new()
+ .build_mint_fungible_asset(
+ mint_amount,
+ alice_account.id(),
+ NoteType::Public,
+ client.rng(),
+ )
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(faucet.id(), tx_request)
+ .await?;
+ println!("Minted tokens. TX: {:?}", tx_id);
+
+ // Wait for the note to be available
+ client.sync_state().await?;
+ wait_for_tx(&mut client, tx_id).await?;
+
+ // Consume the minted note
+ let consumable_notes = client
+ .get_consumable_notes(Some(alice_account.id()))
+ .await?;
+
+ if let Some((note_record, _)) = consumable_notes.first() {
+ let note: Note = note_record.clone().try_into()?;
+ let consume_request = TransactionRequestBuilder::new()
+ .build_consume_notes(vec![note])
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(alice_account.id(), consume_request)
+ .await?;
+ println!("Consumed minted note. TX: {:?}", tx_id);
+ }
+
+ client.sync_state().await?;
+
+ // -------------------------------------------------------------------------
+ // STEP 3: Create custom note
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 3] Create custom note");
+ let secret_vals = vec![Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
+ let digest = Hasher::hash_elements(&secret_vals);
+ println!("digest: {:?}", digest);
+
+ let code = fs::read_to_string(Path::new("../masm/notes/hash_preimage_note.masm")).unwrap();
+ let serial_num = client.rng().draw_word();
+
+ let note_script = client.code_builder().compile_note_script(code).unwrap();
+ let note_storage = NoteStorage::new(digest.to_vec()).unwrap();
+ let recipient = NoteRecipient::new(serial_num, note_script, note_storage);
+ let tag = NoteTag::new(0);
+ let metadata = NoteMetadata::new(alice_account.id(), NoteType::Public).with_tag(tag);
+ let vault = NoteAssets::new(vec![mint_amount.into()])?;
+ let custom_note = Note::new(vault, metadata, recipient);
+ println!("note hash: {:?}", custom_note.id().to_hex());
+
+ let note_request = TransactionRequestBuilder::new()
+ .own_output_notes(vec![custom_note.clone()])
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(alice_account.id(), note_request)
+ .await?;
+ println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+
+ client.sync_state().await?;
+
+ // -------------------------------------------------------------------------
+ // STEP 4: Consume the Custom Note
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 4] Bob consumes the Custom Note with Correct Secret");
+
+ let secret = [Felt::new(1), Felt::new(2), Felt::new(3), Felt::new(4)];
+ let consume_custom_request = TransactionRequestBuilder::new()
+ .input_notes([(custom_note, Some(secret.into()))])
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(bob_account.id(), consume_custom_request)
+ .await?;
+ println!(
+ "Consumed Note Tx on MidenScan: https://testnet.midenscan.com/tx/{:?} \n",
+ tx_id
+ );
+
+ Ok(())
+}
+```
+
+The output of our program will look something like this:
+
+```text
+Latest block: 226943
+
+[STEP 1] Creating new accounts
+Alice's account ID: ""
+Bob's account ID: ""
+
+Deploying a new fungible faucet.
+Faucet account ID: ""
+
+[STEP 2] Mint tokens with P2ID
+Note 0x88d8c4a50c0e6342e58026b051fb6038867de21d3bd3963aec67fd6c45861faf not found. Waiting...
+Note 0x88d8c4a50c0e6342e58026b051fb6038867de21d3bd3963aec67fd6c45861faf not found. Waiting...
+✅ note found 0x88d8c4a50c0e6342e58026b051fb6038867de21d3bd3963aec67fd6c45861faf
+
+[STEP 3] Create custom note
+digest: RpoDigest([14371582251229115050, 1386930022051078873, 17689831064175867466, 9632123050519021080])
+note hash: "0x14c66143377223e090e5b4da0d1e5ce6c6521622ad5b92161a704a25c915769b"
+View transaction on MidenScan: https://testnet.midenscan.com/tx/0xffbee228a2c6283efe958c6b3cd31af88018c029221b413b0f23fcfacb2cb611
+
+[STEP 4] Bob consumes the Custom Note with Correct Secret
+Consumed Note Tx on MidenScan: https://testnet.midenscan.com/tx/0xe6c8bb7b469e03dcacd8f1f400011a781e96ad4266ede11af8e711379e85b929
+
+account delta: AccountVaultDelta { fungible: FungibleAssetDelta({V0(AccountIdV0 { prefix: 6702563556733766432, suffix: 1016103534633728 }): 100}), non_fungible: NonFungibleAssetDelta({}) }
+```
+
+## Conclusion
+
+You have now seen how to create a custom note on Miden that requires a secret preimage to be consumed. We covered:
+
+1. Creating and funding accounts (Alice and Bob)
+2. Hashing a secret number
+3. Building a note with custom logic in Miden Assembly
+4. Consuming the note by providing the correct secret
+
+By leveraging Miden’s privacy features, you can create customized logic for secure asset transfers that depend on keeping parts of the transaction private.
+
+### Running the example
+
+To run the custom note example, navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run this command:
+
+```bash
+cd rust-client
+cargo run --release --bin hash_preimage_note
+```
+
+### Continue learning
+
+Next tutorial: [How to Use Unauthenticated Notes](unauthenticated_note_how_to.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/delegated_proving_tutorial.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/delegated_proving_tutorial.md
new file mode 100644
index 00000000..e3413ce8
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/delegated_proving_tutorial.md
@@ -0,0 +1,211 @@
+---
+title: "Delegated Proving"
+sidebar_position: 12
+---
+
+# Delegated Proving
+
+_Using delegated proving to minimize transaction proving times on computationally constrained devices_
+
+## Overview
+
+In this tutorial we will cover how to use delegated proving with the Miden Rust client to minimize the time it takes to generate a valid transaction proof. In the code below, we will create an account, mint tokens from a faucet, then send the tokens to another account using delegated proving.
+
+## Prerequisites
+
+This tutorial assumes you have basic familiarity with the Miden Rust client.
+
+## What we'll cover
+
+- Explaining what "delegated proving" is and its pros and cons
+- How to use delegated proving with the Rust client
+
+## What is Delegated Proving?
+
+Before diving into our code example, let's clarify what "delegated proving" means.
+
+Delegated proving is the process of outsourcing the ZK proof generation of your transaction to a third party. For certain computationally constrained devices such as mobile phones and web browser environments, generating ZK proofs might take too long to ensure an acceptable user experience. Devices that do not have the computational resources to generate Miden proofs in under 1-2 seconds can use delegated proving to provide a more responsive user experience.
+
+_How does it work?_ When a user choses to use delegated proving, they send off their locally executed transaction to a dedicated server. This dedicated server generates the ZK proof for the executed transaction and sends the proof back to the user. Proving a transaction with delegated proving is trustless, meaning if the delegated prover is malicious, they could not compromise the security of the account that is submitting a transaction to be processed by the delegated prover.
+
+The only downside of using delegated proving is that it reduces the privacy of the account that uses delegated proving, because the delegated prover would have knowledge of the inputs to the transaction that is being proven. For example, it would not be advisable to use delegated proving in the case of our "How to Create a Custom Note" tutorial, since the note we create requires knowledge of a hash preimage to redeem the assets in the note. Using delegated proving would reveal the hash preimage to the server running the delegated proving service.
+
+Anyone can run their own delegated prover server. If you are building a product on Miden, it may make sense to run your own delegated prover server for your users. To run your own delegated proving server, follow the instructions here: https://crates.io/crates/miden-remote-prover.
+
+To keep this tutorial runnable without external services, the code below uses a local prover. The
+flow is the same if you swap in `RemoteTransactionProver` and point it at your delegated prover.
+
+## Step 1: Initialize your repository
+
+Create a new Rust repository for your Miden project and navigate to it with the following command:
+
+```bash
+cargo new miden-delegated-proving-app
+cd miden-delegated-proving-app
+```
+
+Add the following dependencies to your `Cargo.toml` file:
+
+```toml
+[dependencies]
+miden-client = { version = "0.14", features = ["testing", "tonic"] }
+miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" }
+miden-protocol = { version = "0.14" }
+rand = { version = "0.9" }
+serde = { version = "1", features = ["derive"] }
+serde_json = { version = "1.0", features = ["raw_value"] }
+tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] }
+rand_chacha = "0.9.0"
+```
+
+## Step 2: Initialize the client and prover and construct transactions
+
+Similarly to previous tutorials, we must instantiate the client.
+We construct a `LocalTransactionProver` for this walkthrough.
+
+```rust no_run
+use miden_client::auth::AuthSecretKey;
+use miden_client::auth::{AuthSchemeId, AuthSingleSig};
+use rand::RngCore;
+use std::sync::Arc;
+
+use miden_client::{
+ account::component::BasicWallet,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ rpc::{Endpoint, GrpcClient},
+ transaction::{
+ LocalTransactionProver,
+ ProvingOptions,
+ TransactionProver,
+ TransactionRequestBuilder,
+ },
+ ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::account::{AccountBuilder, AccountStorageMode, AccountType};
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // Create Alice's account
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ let alice_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountImmutableCode)
+ .storage_mode(AccountStorageMode::Private)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicWallet)
+ .build()
+ .unwrap();
+
+ client.add_account(&alice_account, false).await?;
+ keystore.add_key(&key_pair, alice_account.id()).await.unwrap();
+
+ // -------------------------------------------------------------------------
+ // Setup the local tx prover
+ // -------------------------------------------------------------------------
+ let local_tx_prover = LocalTransactionProver::new(ProvingOptions::default());
+ let tx_prover: Arc = Arc::new(local_tx_prover);
+
+ // We use a dummy transaction request to showcase delegated proving.
+ // The only effect of this tx should be increasing Alice's nonce.
+ println!("Alice nonce initial: {:?}", alice_account.nonce());
+ let script_code = "begin push.1 drop end";
+ let tx_script = client
+ .code_builder()
+ .compile_tx_script(script_code)
+ .unwrap();
+
+ let transaction_request = TransactionRequestBuilder::new()
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+ // Step 1: Execute the transaction locally
+ println!("Executing transaction...");
+ let tx_result = client
+ .execute_transaction(alice_account.id(), transaction_request)
+ .await?;
+
+ // Step 2: Prove the transaction using the local prover
+ println!("Proving transaction with local prover...");
+ let proven_transaction = client.prove_transaction_with(&tx_result, tx_prover).await?;
+
+ // Step 3: Submit the proven transaction
+ println!("Submitting proven transaction...");
+ let submission_height = client
+ .submit_proven_transaction(proven_transaction, &tx_result)
+ .await?;
+
+ // Step 4: Apply the transaction to local store
+ client
+ .apply_transaction(&tx_result, submission_height)
+ .await?;
+
+ println!("Transaction submitted successfully using local prover!");
+
+ client.sync_state().await.unwrap();
+
+ let account = client
+ .get_account(alice_account.id())
+ .await
+ .unwrap()
+ .expect("alice account not found");
+
+ println!("Alice nonce has increased: {:?}", account.nonce());
+
+ Ok(())
+}
+```
+
+Now let's run the `src/main.rs` program:
+
+```bash
+cargo run --release
+```
+
+The output will look like this:
+
+```text
+Latest block: 226954
+Alice initial account balance: Ok(1000)
+Alice final account balance: Ok(900)
+```
+
+### Running the example
+
+To run a full working example navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run this command:
+
+```bash
+cd rust-client
+cargo run --release --bin delegated_prover
+```
+
+### Continue learning
+
+Next tutorial: [Consuming On-Chain Price Data from the Pragma Oracle](oracle_tutorial.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/foreign_procedure_invocation_tutorial.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/foreign_procedure_invocation_tutorial.md
new file mode 100644
index 00000000..f7df1084
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/foreign_procedure_invocation_tutorial.md
@@ -0,0 +1,666 @@
+---
+title: "Foreign Procedure Invocation"
+sidebar_position: 7
+---
+
+# Foreign Procedure Invocation Tutorial
+
+_Using foreign procedure invocation to craft read-only cross-contract calls in the Miden VM_
+
+## Overview
+
+In previous tutorials we deployed a public counter contract and incremented the count from a different client instance.
+
+In this tutorial we will cover the basics of "foreign procedure invocation" (FPI) in the Miden VM. To demonstrate FPI, we will build a "count copy" smart contract that reads the count from our previously deployed counter contract and copies the count to its own local storage.
+
+Foreign procedure invocation (FPI) is a powerful tool for building smart contracts in the Miden VM. FPI allows one smart contract to call "read-only" procedures in other smart contracts.
+
+The term "foreign procedure invocation" might sound a bit verbose, but it is as simple as one smart contract calling a non-state modifying procedure in another smart contract. The "EVM equivalent" of foreign procedure invocation would be a smart contract calling a read-only function in another contract.
+
+FPI is useful for developing smart contracts that extend the functionality of existing contracts on Miden. FPI is the core primitive used by price oracles on Miden.
+
+## What We Will Build
+
+
+
+The diagram above depicts the "count copy" smart contract using foreign procedure invocation to read the count state of the counter contract. After reading the state via FPI, the "count copy" smart contract writes the value returned from the counter contract to storage.
+
+## What we'll cover
+
+- Foreign Procedure Invocation (FPI)
+- Building a "count copy" Smart Contract
+
+## Prerequisites
+
+This tutorial assumes you have a basic understanding of Miden assembly and completed the previous tutorial on deploying the counter contract. We will be working within the same `miden-counter-contract` repository that we created in the [Interacting with Public Smart Contracts](./public_account_interaction_tutorial.md) tutorial.
+
+## Step 1: Set up your repository
+
+We will be using the same repository used in the "Interacting with Public Smart Contracts" tutorial. To set up your repository for this tutorial, first follow up until step two [here](./public_account_interaction_tutorial.md).
+
+## Step 2: Set up the "count reader" contract
+
+Inside of the `masm/accounts/` directory, create the `count_reader.masm` file. This is the smart contract that will read the "count" value from the counter contract.
+
+`masm/accounts/count_reader.masm`:
+
+```masm
+use miden::protocol::active_account
+use miden::protocol::native_account
+use miden::protocol::tx
+use miden::core::word
+use miden::core::sys
+
+const COUNT_READER_SLOT = word("miden::tutorials::count_reader")
+
+# => [account_id_suffix, account_id_prefix, PROC_HASH(4), foreign_procedure_inputs(16)]
+pub proc copy_count
+ exec.tx::execute_foreign_procedure
+ # => [count, pad(12)]
+
+ push.COUNT_READER_SLOT[0..2]
+ # [slot_id_prefix, slot_id_suffix, count, pad(12)]
+
+ exec.native_account::set_item
+ # => [OLD_VALUE, pad(12)]
+
+ dropw dropw dropw dropw
+ # => []
+
+ exec.sys::truncate_stack
+ # => []
+end
+```
+
+In the count reader smart contract we have a `copy_count` procedure that uses `tx::execute_foreign_procedure` to call the `get_count` procedure in the counter contract.
+
+To call the `get_count` procedure, we push its hash along with the counter contract's ID suffix and prefix.
+
+This is what the stack state should look like before we call `tx::execute_foreign_procedure`:
+
+```text
+# => [account_id_suffix, account_id_prefix, GET_COUNT_HASH, foreign_procedure_inputs(16)]
+```
+
+`execute_foreign_procedure` always requires exactly 16 `foreign_procedure_inputs` on the stack
+below the procedure hash and account ID. Since `get_count` takes no arguments, we pass 16 zero
+words (`padw padw padw padw`) as the inputs. After the call, the procedure returns 16 output
+elements; the count word sits at the top and we clean up the rest with `dropw dropw dropw dropw`.
+
+After calling the `get_count` procedure in the counter contract, we save the count into the
+`miden::tutorials::count_reader` storage slot.
+
+**Note**: _The bracket symbols used in the count copy contract are not valid MASM syntax. These are simply placeholder elements that we will replace with the actual values before compilation._
+
+Inside the `masm/scripts/` directory, create the `reader_script.masm` file:
+
+```masm
+use external_contract::count_reader_contract
+use miden::core::sys
+
+begin
+ padw padw padw padw
+ # => [pad(16)]
+
+ push.{get_count_proc_hash}
+ # => [GET_COUNT_HASH, pad(16)]
+
+ push.{account_id_prefix}
+ # => [account_id_prefix, GET_COUNT_HASH, pad(16)]
+
+ push.{account_id_suffix}
+ # => [account_id_suffix, account_id_prefix, GET_COUNT_HASH, pad(16)]
+
+ call.count_reader_contract::copy_count
+ # => []
+
+ exec.sys::truncate_stack
+ # => []
+end
+```
+
+**Note**: _`push.{get_count_proc_hash}` is not valid MASM, we will format the string with the value get_count_proc_hash before passing this script code to the assembler._
+
+### Step 3: Set up your `src/main.rs` file:
+
+```rust no_run
+use rand::RngCore;
+use std::{fs, path::Path, sync::Arc, time::Duration};
+use tokio::time::sleep;
+
+use miden_client::{
+ account::{
+ component::AccountComponentMetadata, AccountBuilder, AccountComponent,
+ AccountStorageMode, AccountType, StorageSlot, StorageSlotName,
+ },
+ account::AccountId,
+ assembly::{
+ CodeBuilder,
+ DefaultSourceManager,
+ Library,
+ Module,
+ ModuleKind,
+ Path as AssemblyPath,
+ },
+ auth::NoAuth,
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{domain::account::AccountStorageRequirements, Endpoint, GrpcClient},
+ transaction::{ForeignAccount, TransactionKernel, TransactionRequestBuilder},
+ ClientError, Word,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+
+fn create_library(
+ library_path: &str,
+ source_code: &str,
+) -> Result, Box> {
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ source_code,
+ source_manager,
+ )?;
+ let library = assembler.assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // -------------------------------------------------------------------------
+ // STEP 1: Create the Count Reader Contract
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 1] Creating count reader contract.");
+
+ let count_reader_path = Path::new("../masm/accounts/count_reader.masm");
+ let count_reader_code = fs::read_to_string(count_reader_path).unwrap();
+
+ let count_reader_slot_name =
+ StorageSlotName::new("miden::tutorials::count_reader").expect("valid slot name");
+ let count_reader_component_code = CodeBuilder::new()
+ .compile_component_code("external_contract::count_reader_contract", &count_reader_code)
+ .unwrap();
+ let count_reader_component = AccountComponent::new(
+ count_reader_component_code,
+ vec![StorageSlot::with_value(
+ count_reader_slot_name.clone(),
+ Word::default(),
+ )],
+ AccountComponentMetadata::new("external_contract::count_reader_contract", AccountType::all()),
+ )
+ .unwrap();
+
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let count_reader_contract = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountImmutableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_component(count_reader_component.clone())
+ .with_auth_component(NoAuth)
+ .build()
+ .unwrap();
+
+ println!(
+ "count_reader hash: {:?}",
+ count_reader_contract.to_commitment()
+ );
+ println!("contract id: {:?}", count_reader_contract.id());
+
+ client
+ .add_account(&count_reader_contract, false)
+ .await
+ .unwrap();
+
+ Ok(())
+}
+```
+
+Run the following command to execute src/main.rs:
+
+```bash
+cargo run --release
+```
+
+The output of our program will look something like this:
+
+```text
+Latest block: 226976
+
+[STEP 1] Creating count reader contract.
+count_reader hash: RpoDigest([15888177100833057221, 15548657445961063290, 5580812380698193124, 9604096693288041818])
+contract id: ""
+```
+
+## Step 4: Import the pre-deployed counter contract
+
+The FPI call needs a counter contract already deployed on-chain. We import the counter contract that was deployed in the [counter contract tutorial](./counter_contract_tutorial.md) by its testnet address:
+
+```rust ignore
+// -------------------------------------------------------------------------
+// STEP 2: Build & Get State of the Counter Contract
+// -------------------------------------------------------------------------
+println!("\n[STEP 2] Building counter contract from public state");
+
+// Define the Counter Contract account id from counter contract deploy
+let (_, counter_contract_id) =
+ AccountId::from_bech32("mtst1apsd609q5966cqra992t4a00tgstrkfk").unwrap();
+
+println!("counter contract id: {:?}", counter_contract_id);
+
+client
+ .import_account_by_id(counter_contract_id)
+ .await
+ .unwrap();
+
+let counter_contract = client
+ .get_account(counter_contract_id)
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+println!(
+ "Account details: {:?}",
+ counter_contract.storage().slots().first().unwrap()
+);
+```
+
+## Step 5: Call the counter contract via foreign procedure invocation
+
+Add this snippet to the end of your file in the `main()` function:
+
+```rust ignore
+// -------------------------------------------------------------------------
+// STEP 3: Call the Counter Contract via Foreign Procedure Invocation (FPI)
+// -------------------------------------------------------------------------
+println!("\n[STEP 3] Call counter contract with FPI from count reader contract");
+
+// Derive the get_count procedure hash from the locally compiled counter library.
+let counter_contract_path = Path::new("../masm/accounts/counter.masm");
+let counter_contract_code = fs::read_to_string(counter_contract_path).unwrap();
+
+// Compile the counter as a component (same path as the deploy binary) to get
+// the correct procedure root that matches the on-chain MAST.
+let counter_component_code = CodeBuilder::new()
+ .compile_component_code("external_contract::counter_contract", &counter_contract_code)
+ .unwrap();
+let counter_component = AccountComponent::new(
+ counter_component_code,
+ vec![],
+ AccountComponentMetadata::new("external_contract::counter_contract", AccountType::all()),
+)
+.unwrap();
+
+let get_count_root = counter_component
+ .component_code()
+ .as_library()
+ .get_procedure_root_by_path("external_contract::counter_contract::get_count")
+ .expect("get_count export not found");
+let get_count_hash = format!("{}", get_count_root);
+
+println!("get_count hash: {:?}", get_count_hash);
+println!("counter id prefix: {:?}", counter_contract_id.prefix());
+println!("counter id suffix: {:?}", counter_contract_id.suffix());
+
+let script_path = Path::new("../masm/scripts/reader_script.masm");
+let script_code_original = fs::read_to_string(script_path).unwrap();
+let script_code = script_code_original
+ .replace("{get_count_proc_hash}", &get_count_hash)
+ .replace(
+ "{account_id_suffix}",
+ &counter_contract_id.suffix().as_canonical_u64().to_string(),
+ )
+ .replace(
+ "{account_id_prefix}",
+ &u64::from(counter_contract_id.prefix()).to_string(),
+ );
+
+let account_component_lib = create_library(
+ "external_contract::count_reader_contract",
+ &count_reader_code,
+)
+.unwrap();
+
+let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&account_component_lib)
+ .unwrap()
+ .compile_tx_script(&script_code)
+ .unwrap();
+
+let foreign_account =
+ ForeignAccount::public(counter_contract_id, AccountStorageRequirements::default()).unwrap();
+
+let tx_request = TransactionRequestBuilder::new()
+ .foreign_accounts([foreign_account])
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+let tx_id = client
+ .submit_new_transaction(count_reader_contract.id(), tx_request)
+ .await
+ .unwrap();
+
+println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+);
+
+client.sync_state().await.unwrap();
+sleep(Duration::from_secs(5)).await;
+client.sync_state().await.unwrap();
+
+// Retrieve final state to confirm the count was copied.
+let counter_slot_name =
+ StorageSlotName::new("miden::tutorials::counter").expect("valid slot name");
+let account_1 = client
+ .get_account(counter_contract_id)
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+println!(
+ "counter contract storage: {:?}",
+ account_1.storage().get_item(&counter_slot_name)
+);
+
+let account_2 = client
+ .get_account(count_reader_contract.id())
+ .await
+ .unwrap()
+ .expect("count reader contract not found");
+println!(
+ "count reader contract storage: {:?}",
+ account_2.storage().get_item(&count_reader_slot_name)
+);
+```
+
+The key here is the use of the `.foreign_accounts()` method on the `TransactionRequestBuilder`. Using this method, it is possible to create transactions with multiple foreign procedure calls.
+
+## Summary
+
+In this tutorial created a smart contract that calls the `get_count` procedure in the counter contract using foreign procedure invocation, and then saves the returned value to its local storage.
+
+The final `src/main.rs` file should look like this:
+
+```rust no_run
+use rand::RngCore;
+use std::{fs, path::Path, sync::Arc, time::Duration};
+use tokio::time::sleep;
+
+use miden_client::{
+ account::{
+ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountId,
+ AccountStorageMode, AccountType, StorageSlot, StorageSlotName,
+ },
+ assembly::{
+ CodeBuilder, DefaultSourceManager, Library, Module, ModuleKind,
+ Path as AssemblyPath,
+ },
+ auth::NoAuth,
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{domain::account::AccountStorageRequirements, Endpoint, GrpcClient},
+ transaction::{ForeignAccount, TransactionKernel, TransactionRequestBuilder},
+ ClientError, Word,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+
+fn create_library(
+ library_path: &str,
+ source_code: &str,
+) -> Result, Box> {
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let assembler = TransactionKernel::assembler_with_source_manager(source_manager.clone());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ source_code,
+ source_manager,
+ )?;
+ let library = assembler.assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // -------------------------------------------------------------------------
+ // STEP 1: Create the Count Reader Contract
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 1] Creating count reader contract.");
+
+ let count_reader_path = Path::new("../masm/accounts/count_reader.masm");
+ let count_reader_code = fs::read_to_string(count_reader_path).unwrap();
+
+ let count_reader_slot_name =
+ StorageSlotName::new("miden::tutorials::count_reader").expect("valid slot name");
+ let count_reader_component_code = CodeBuilder::new()
+ .compile_component_code(
+ "external_contract::count_reader_contract",
+ &count_reader_code,
+ )
+ .unwrap();
+ let count_reader_component = AccountComponent::new(
+ count_reader_component_code,
+ vec![StorageSlot::with_value(
+ count_reader_slot_name.clone(),
+ Word::default(),
+ )],
+ AccountComponentMetadata::new(
+ "external_contract::count_reader_contract",
+ AccountType::all(),
+ ),
+ )
+ .unwrap();
+
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let count_reader_contract = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountImmutableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_component(count_reader_component.clone())
+ .with_auth_component(NoAuth)
+ .build()
+ .unwrap();
+
+ println!(
+ "count_reader hash: {:?}",
+ count_reader_contract.to_commitment()
+ );
+ println!("count_reader id: {:?}", count_reader_contract.id());
+
+ client
+ .add_account(&count_reader_contract, false)
+ .await
+ .unwrap();
+
+ // -------------------------------------------------------------------------
+ // STEP 2: Build & Get State of the Counter Contract
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 2] Building counter contract from public state");
+
+ // Define the Counter Contract account id from counter contract deploy
+ let (_, counter_contract_id) =
+ AccountId::from_bech32("mtst1apsd609q5966cqra992t4a00tgstrkfk").unwrap();
+
+ println!("counter contract id: {:?}", counter_contract_id);
+
+ client
+ .import_account_by_id(counter_contract_id)
+ .await
+ .unwrap();
+
+ let counter_contract = client
+ .get_account(counter_contract_id)
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+ println!(
+ "Account details: {:?}",
+ counter_contract.storage().slots().first().unwrap()
+ );
+
+ // -------------------------------------------------------------------------
+ // STEP 3: Call the Counter Contract via Foreign Procedure Invocation (FPI)
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 3] Call counter contract with FPI from count reader contract");
+
+ let counter_contract_path = Path::new("../masm/accounts/counter.masm");
+ let counter_contract_code = fs::read_to_string(counter_contract_path).unwrap();
+
+ // Compile the counter as a component (same path as the deploy binary) to get
+ // the correct procedure root that matches the on-chain MAST.
+ let counter_component_code = CodeBuilder::new()
+ .compile_component_code("external_contract::counter_contract", &counter_contract_code)
+ .unwrap();
+ let counter_component = AccountComponent::new(
+ counter_component_code,
+ vec![],
+ AccountComponentMetadata::new("external_contract::counter_contract", AccountType::all()),
+ )
+ .unwrap();
+
+ let get_count_root = counter_component
+ .component_code()
+ .as_library()
+ .get_procedure_root_by_path("external_contract::counter_contract::get_count")
+ .expect("get_count export not found");
+ let get_count_hash = format!("{}", get_count_root);
+
+ println!("get_count hash: {:?}", get_count_hash);
+ println!("counter id prefix: {:?}", counter_contract_id.prefix());
+ println!("counter id suffix: {:?}", counter_contract_id.suffix());
+
+ let script_path = Path::new("../masm/scripts/reader_script.masm");
+ let script_code_original = fs::read_to_string(script_path).unwrap();
+ let script_code = script_code_original
+ .replace("{get_count_proc_hash}", &get_count_hash)
+ .replace(
+ "{account_id_suffix}",
+ &counter_contract_id.suffix().as_canonical_u64().to_string(),
+ )
+ .replace(
+ "{account_id_prefix}",
+ &u64::from(counter_contract_id.prefix()).to_string(),
+ );
+
+ let account_component_lib = create_library(
+ "external_contract::count_reader_contract",
+ &count_reader_code,
+ )
+ .unwrap();
+
+ let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&account_component_lib)
+ .unwrap()
+ .compile_tx_script(&script_code)
+ .unwrap();
+
+ let foreign_account =
+ ForeignAccount::public(counter_contract_id, AccountStorageRequirements::default())
+ .unwrap();
+
+ let tx_request = TransactionRequestBuilder::new()
+ .foreign_accounts([foreign_account])
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(count_reader_contract.id(), tx_request)
+ .await
+ .unwrap();
+
+ println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+
+ client.sync_state().await.unwrap();
+ sleep(Duration::from_secs(5)).await;
+ client.sync_state().await.unwrap();
+
+ // Retrieve final state to confirm the count was copied.
+ let counter_slot_name =
+ StorageSlotName::new("miden::tutorials::counter").expect("valid slot name");
+ let account_1 = client
+ .get_account(counter_contract_id)
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+ println!(
+ "counter contract storage: {:?}",
+ account_1.storage().get_item(&counter_slot_name)
+ );
+
+ let account_2 = client
+ .get_account(count_reader_contract.id())
+ .await
+ .unwrap()
+ .expect("count reader contract not found");
+ println!(
+ "count reader contract storage: {:?}",
+ account_2.storage().get_item(&count_reader_slot_name)
+ );
+
+ Ok(())
+}
+```
+
+The output will show the count reader contract being created, the counter contract being imported from testnet, and finally both storage slots reflecting the same count value after the FPI transaction is confirmed.
+
+### Running the example
+
+To run the full example, navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run this command:
+
+```bash
+cd rust-client
+cargo run --release --bin counter_contract_fpi
+```
+
+### Continue learning
+
+Next tutorial: [How to Use Unauthenticated Notes](unauthenticated_note_how_to.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/index.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/index.md
new file mode 100644
index 00000000..4515a95b
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/index.md
@@ -0,0 +1,18 @@
+---
+title: "Rust Client"
+sidebar_position: 1
+---
+
+# Rust Client
+
+Rust library, which can be used to programmatically interact with the Miden rollup.
+
+The Miden Rust client can be used for a variety of things, including:
+
+- Deploying, testing, and creating transactions to interact with accounts and notes on Miden.
+- Storing the state of accounts and notes locally.
+- Generating and submitting proofs of transactions.
+
+This section of the docs is an overview of the different things one can achieve using the Rust client, and how to implement them.
+
+Keep in mind that both the Rust client and the documentation are works-in-progress!
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/mappings_in_masm_how_to.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/mappings_in_masm_how_to.md
new file mode 100644
index 00000000..46ab6e87
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/mappings_in_masm_how_to.md
@@ -0,0 +1,358 @@
+---
+title: "How to Use Mappings in Miden Assembly"
+sidebar_position: 10
+---
+
+# How to Use Mappings in Miden Assembly
+
+_Using mappings in Miden assembly for storing key value pairs_
+
+## Overview
+
+In this example, we will explore how to use mappings in Miden Assembly. Mappings are essential data structures that store key-value pairs. We will demonstrate how to create an account that contains a mapping and then call a procedure in that account to update the mapping.
+
+At a high level, this example involves:
+
+- Setting up an account with a mapping stored in one of its storage slots.
+- Writing a smart contract in Miden Assembly that includes procedures to read from and write to the mapping.
+- Creating a transaction script that calls these procedures.
+- Using Rust code to deploy the account and submit a transaction that updates the mapping.
+ After the Miden Assembly snippets, we explain that the transaction script calls a procedure in the account. This procedure then updates the mapping by modifying the mapping stored in the account's storage slot.
+
+## What we'll cover
+
+- **How to Use Mappings in Miden Assembly:** See how to create a smart contract that uses a mapping.
+- **How to Link Libraries in Miden Assembly:** Demonstrate how to link procedures across Accounts, Notes, and Scripts.
+
+## Step-by-step process
+
+1. **Setting up an account with a mapping**
+ In this step, you create an account that has a storage slot configured as a mapping. The account smart contract code (shown below) defines procedures to write to and read from this mapping.
+
+2. **Creating a script that calls a procedure in the account:**
+ Next, you create a transaction script that calls the procedures defined in the account. This script sends the key-value data and then invokes the account procedure, which updates the mapping.
+
+3. **How to read and write to a mapping in MASM:**
+ Finally, we demonstrate how to use MASM instructions to interact with the mapping. The smart contract uses standard procedures to set a mapping item, retrieve a value from the mapping, and get the current mapping root.
+
+---
+
+### Example of smart contract that uses a mapping
+
+```masm
+use miden::protocol::active_account
+use miden::protocol::native_account
+use miden::core::word
+use miden::core::sys
+
+const MAP_SLOT = word("miden::tutorials::mapping::map")
+
+# Inputs: [KEY, VALUE]
+# Outputs: []
+pub proc write_to_map
+ # The storage map is in the mapping slot.
+ push.MAP_SLOT[0..2]
+ # => [slot_id_prefix, slot_id_suffix, KEY, VALUE]
+
+ # Setting the key value pair in the map
+ exec.native_account::set_map_item
+ # => [OLD_VALUE]
+
+ dropw
+ # => []
+end
+
+# Inputs: [KEY]
+# Outputs: [VALUE]
+pub proc get_value_in_map
+ # The storage map is in the mapping slot.
+ push.MAP_SLOT[0..2]
+ # => [slot_id_prefix, slot_id_suffix, KEY]
+
+ exec.active_account::get_map_item
+ # => [VALUE]
+end
+
+# Inputs: []
+# Outputs: [CURRENT_ROOT]
+pub proc get_current_map_root
+ # Getting the current root from the mapping slot.
+ push.MAP_SLOT[0..2] exec.active_account::get_item
+ # => [CURRENT_ROOT]
+
+ exec.sys::truncate_stack
+ # => [CURRENT_ROOT]
+end
+```
+
+### Explanation of the assembly code
+
+- **write_to_map:**
+ The procedure takes a key and a value as inputs. It pushes the slot ID prefix and suffix for the mapping slot onto the stack, then calls the `set_map_item` procedure from the account library to update the mapping. After updating the map, it drops the old value.
+- **get_value_in_map:**
+ This procedure takes a key as input and retrieves the corresponding value from the mapping by calling `get_map_item` after pushing the mapping slot ID.
+
+- **get_current_map_root:**
+ This procedure retrieves the current root of the mapping by calling `get_item` with the mapping slot ID and then truncating the stack to leave only the mapping root.
+
+**Security Note**: The procedure `write_to_map` calls the account procedure `incr_nonce`. This allows any external account to be able to write to the storage map of the account. Smart contract developers should know that procedures that call the `account::incr_nonce` procedure allow anyone to call the procedure and modify the state of the account.
+
+### Transaction script that calls the smart contract
+
+```masm
+use miden_by_example::mapping_example_contract
+use miden::core::sys
+
+begin
+ push.1.2.3.4
+ push.0.0.0.0
+ # => [KEY, VALUE]
+
+ call.mapping_example_contract::write_to_map
+ # => []
+
+ push.0.0.0.0
+ # => [KEY]
+
+ call.mapping_example_contract::get_value_in_map
+ # => [VALUE]
+
+ dropw
+ # => []
+
+ call.mapping_example_contract::get_current_map_root
+ # => [CURRENT_ROOT]
+
+ exec.sys::truncate_stack
+end
+```
+
+### Explanation of the transaction script
+
+The transaction script does the following:
+
+- It pushes a key (`[0.0.0.0]`) and a value (`[1.2.3.4]`) onto the stack.
+- It calls the `write_to_map` procedure, which is defined in the account’s smart contract. This updates the mapping in the account.
+- It then pushes the key again and calls `get_value_in_map` to retrieve the value associated with the key.
+- Finally, it calls `get_current_map_root` to get the current state (root) of the mapping.
+
+The script calls the `write_to_map` procedure in the account which writes the key value pair to the mapping.
+
+---
+
+### Rust code that sets everything up
+
+Below is the Rust code that deploys the smart contract, creates the transaction script, and submits a transaction to update the mapping in the account:
+
+```rust no_run
+use miden_client::auth::NoAuth;
+use miden_client::transaction::TransactionKernel;
+use rand::RngCore;
+use std::{fs, path::Path, sync::Arc};
+
+use miden_client::{
+ assembly::{
+ Assembler,
+ CodeBuilder,
+ DefaultSourceManager,
+ Module,
+ ModuleKind,
+ Path as AssemblyPath,
+ },
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::{
+ account::{
+ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode,
+ AccountType, StorageMap, StorageSlot, StorageSlotName,
+ },
+ Felt, Word,
+};
+
+fn create_library(
+ assembler: Assembler,
+ library_path: &str,
+ source_code: &str,
+) -> Result, Box> {
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ source_code,
+ source_manager.clone(),
+ )?;
+ let library = assembler.clone().assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // -------------------------------------------------------------------------
+ // STEP 1: Deploy a smart contract with a mapping
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 1] Deploy a smart contract with a mapping");
+
+ // Load the MASM file for the counter contract
+ let file_path = Path::new("../masm/accounts/mapping_example_contract.masm");
+ let account_code = fs::read_to_string(file_path).unwrap();
+
+ // Prepare assembler (debug mode = true)
+ let assembler: Assembler = TransactionKernel::assembler();
+
+ // Using an empty storage value in slot 0 since this is usually reserved
+ // for the account pub_key and metadata
+ let empty_slot_name =
+ StorageSlotName::new("miden::tutorials::mapping::value").expect("valid slot name");
+ let empty_storage_slot = StorageSlot::with_value(empty_slot_name.clone(), Word::default());
+
+ // initialize storage map
+ let storage_map = StorageMap::new();
+ let map_slot_name =
+ StorageSlotName::new("miden::tutorials::mapping::map").expect("valid slot name");
+ let storage_slot_map = StorageSlot::with_map(map_slot_name.clone(), storage_map.clone());
+
+ // Compile the account code into `AccountComponent` with one storage slot
+ let component_code = CodeBuilder::new()
+ .compile_component_code("miden_by_example::mapping_example_contract", &account_code)
+ .unwrap();
+ let mapping_contract_component = AccountComponent::new(
+ component_code,
+ vec![empty_storage_slot, storage_slot_map],
+ AccountComponentMetadata::new("miden_by_example::mapping_example_contract", AccountType::all()),
+ )
+ .unwrap();
+
+ // Init seed for the counter contract
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ // Build the new `Account` with the component
+ let mapping_example_contract = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountImmutableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_component(mapping_contract_component.clone())
+ .with_auth_component(NoAuth)
+ .build()
+ .unwrap();
+
+ client
+ .add_account(&mapping_example_contract, false)
+ .await
+ .unwrap();
+
+ // -------------------------------------------------------------------------
+ // STEP 2: Call the Mapping Contract with a Script
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 2] Call Mapping Contract With Script");
+
+ let script_code =
+ fs::read_to_string(Path::new("../masm/scripts/mapping_example_script.masm")).unwrap();
+
+ // Create the library from the account source code using the helper function.
+ let account_component_lib = create_library(
+ assembler.clone(),
+ "miden_by_example::mapping_example_contract",
+ &account_code,
+ )
+ .unwrap();
+
+ // Compile the transaction script with the library.
+ let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&account_component_lib)
+ .unwrap()
+ .compile_tx_script(&script_code)
+ .unwrap();
+
+ // Build a transaction request with the custom script
+ let tx_increment_request = TransactionRequestBuilder::new()
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+ // Execute and submit the transaction
+ let tx_id = client
+ .submit_new_transaction(mapping_example_contract.id(), tx_increment_request)
+ .await
+ .unwrap();
+
+ println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+
+ client.sync_state().await.unwrap();
+
+ let account = client
+ .get_account(mapping_example_contract.id())
+ .await
+ .unwrap()
+ .expect("mapping contract not found");
+ let key = [Felt::new(0), Felt::new(0), Felt::new(0), Felt::new(0)].into();
+ println!(
+ "Mapping state\n Slot: {:?}\n Key: {:?}\n Value: {:?}",
+ map_slot_name,
+ key,
+ account.storage().get_map_item(&map_slot_name, key)
+ );
+
+ Ok(())
+}
+```
+
+### What the Rust code does
+
+- **Client Initialization:**
+ The client is initialized with a connection to the Miden Testnet and a SQLite store. This sets up the environment to deploy and interact with accounts.
+
+- **Deploying the Smart Contract:**
+ The account containing the mapping is created by reading the MASM smart contract from a file, compiling it into an `AccountComponent`, and deploying it using an `AccountBuilder`.
+
+- **Creating and Executing a Transaction Script:**
+ A separate MASM script is compiled into a `TransactionScript`. This script calls the smart contract's procedures to write to and then read from the mapping.
+
+- **Displaying the Result:**
+ Finally, after the transaction is processed, the code reads the updated state of the mapping in the account.
+
+---
+
+### Running the example
+
+To run the full example, navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run this command:
+
+```bash
+cd rust-client
+cargo run --release --bin mapping_example
+```
+
+This example shows how the script calls the procedure in the account, which then updates the mapping stored within the account. The mapping update is verified by reading the mapping’s key-value pair after the transaction completes.
+
+### Continue learning
+
+Next tutorial: [How to Create Notes in Miden Assembly](creating_notes_in_masm_tutorial.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/mint_consume_create_tutorial.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/mint_consume_create_tutorial.md
new file mode 100644
index 00000000..302aca40
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/mint_consume_create_tutorial.md
@@ -0,0 +1,598 @@
+---
+title: "Mint, Consume, and Create Notes"
+sidebar_position: 3
+---
+
+# Mint, Consume, and Create Notes
+
+_Using the Miden client in Rust to mint, consume, and create notes_
+
+## Overview
+
+In the previous section, we initialized our repository and covered how to create an account and deploy a faucet. In this section, we will mint tokens from the faucet for _Alice_, consume the newly created notes, and demonstrate how to send assets to other accounts.
+
+## What we'll cover
+
+- Minting tokens from a faucet
+- Consuming notes to fund an account
+- Sending tokens to other users
+
+## Step 1: Minting tokens from the faucet
+
+To mint notes with tokens from the faucet we created, Alice needs to call the faucet with a mint transaction request.
+
+_In essence, a transaction request is a structured template that outlines the data required to generate a zero-knowledge proof of a state change of an account. It specifies which input notes (if any) will be consumed, includes an optional transaction script to execute, and enumerates the set of notes expected to be created (if any)._
+
+Below is an example of a transaction request minting tokens from the faucet for Alice. This code snippet will create 5 transaction mint transaction requests.
+
+Add this snippet to the end of your file in the `main()` function that we created in the previous chapter:
+
+```rust ignore
+//------------------------------------------------------------
+// STEP 3: Mint 5 notes of 100 tokens for Alice
+//------------------------------------------------------------
+println!("\n[STEP 3] Minting 5 notes of 100 tokens each for Alice.");
+
+let amount: u64 = 100;
+let fungible_asset = FungibleAsset::new(faucet_account.id(), amount).unwrap();
+
+for i in 1..=5 {
+ let transaction_request = TransactionRequestBuilder::new()
+ .build_mint_fungible_asset(
+ fungible_asset,
+ alice_account.id(),
+ NoteType::Public,
+ client.rng(),
+ )
+ .unwrap();
+
+ println!("tx request built");
+
+ let tx_id = client
+ .submit_new_transaction(faucet_account.id(), transaction_request)
+ .await?;
+ println!(
+ "Minted note #{} of {} tokens for Alice. TX: {:?}",
+ i, amount, tx_id
+ );
+}
+println!("All 5 notes minted for Alice successfully!");
+
+// Re-sync so minted notes become visible
+client.sync_state().await?;
+```
+
+## Step 2: Identifying consumable notes
+
+Once Alice has minted a note from the faucet, she will eventually want to spend the tokens that she received in the note created by the mint transaction.
+
+Minting a note from a faucet on Miden means a faucet account creates a new note targeted to the requesting account. The requesting account needs to consume this new note to have the assets appear in their account.
+
+To identify consumable notes, the Miden client provides the `get_consumable_notes` function. Before calling it, ensure that the client state is synced.
+
+_Tip: If you know how many notes to expect after a transaction, use an await or loop condition to check how many notes of the type you expect are available for consumption instead of using a set timeout before calling `get_consumable_notes`. This ensures your application isn't idle for longer than necessary._
+
+#### Identifying which notes are available:
+
+```rust ignore
+let consumable_notes = client.get_consumable_notes(Some(alice_account.id())).await?;
+```
+
+## Step 3: Consuming multiple notes in a single transaction:
+
+Now that we know how to identify notes ready to consume, let's consume the notes created by the faucet in a single transaction. After consuming the notes, Alice's wallet balance will be updated.
+
+The following code snippet identifies consumable notes and consumes them in a single transaction.
+
+Add this snippet to the end of your file in the `main()` function:
+
+```rust ignore
+//------------------------------------------------------------
+// STEP 4: Alice consumes all her notes
+//------------------------------------------------------------
+println!("\n[STEP 4] Alice will now consume all of her notes to consolidate them.");
+
+// Consume all minted notes in a single transaction
+loop {
+ // Resync to get the latest data
+ client.sync_state().await?;
+
+ let consumable_notes = client
+ .get_consumable_notes(Some(alice_account.id()))
+ .await?;
+ let notes = consumable_notes
+ .iter()
+ .map(|(note, _)| note.clone().try_into())
+ .collect::, _>>()?;
+
+ if notes.len() == 5 {
+ println!("Found 5 consumable notes for Alice. Consuming them now...");
+ let transaction_request = TransactionRequestBuilder::new()
+ .build_consume_notes(notes)
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(alice_account.id(), transaction_request)
+ .await?;
+ println!(
+ "All of Alice's notes consumed successfully. TX: {:?}",
+ tx_id
+ );
+ break;
+ } else {
+ println!(
+ "Currently, Alice has {} consumable notes. Waiting...",
+ notes.len()
+ );
+ tokio::time::sleep(Duration::from_secs(3)).await;
+ }
+}
+```
+
+## Step 4: Sending tokens to other accounts
+
+After consuming the notes, Alice has tokens in her wallet. Now, she wants to send tokens to her friends. She has two options: create a separate transaction for each transfer or batch multiple transfers into a single transaction.
+
+_The standard asset transfer note on Miden is the P2ID note (Pay-to-Id). There is also the P2IDE (Pay-to-Id Extended) variant which allows for both timelocking the note (target can only spend the note after a certain block height) and for the note to be reclaimable (the creator of the note can reclaim the note after a certain block height)._
+
+In our example, Alice will now send 50 tokens to 5 different accounts.
+
+For the sake of the example, the first four P2ID transfers are handled in a single transaction, and the fifth transfer is a standard P2ID transfer.
+
+### Output multiple P2ID notes in a single transaction
+
+To output multiple notes in a single transaction we need to create a list of our expected output notes. The expected output notes are the notes that we expect to create in our transaction request.
+
+In the snippet below, we create an empty vector to store five P2ID output notes, loop over five iterations `(using 0..=4)` to create five unique dummy account IDs, build a P2ID note for each one, and push each note onto the vector. Finally, we build a transaction request using `.own_output_notes()`—passing in all five notes—and submit it to the node.
+
+Add this snippet to the end of your file in the `main()` function:
+
+```rust ignore
+//------------------------------------------------------------
+// STEP 5: Alice sends 5 notes of 50 tokens to 5 users
+//------------------------------------------------------------
+println!("\n[STEP 5] Alice sends 5 notes of 50 tokens each to 5 different users.");
+
+// Send 50 tokens to 4 accounts in one transaction
+println!("Creating multiple P2ID notes for 4 target accounts in one transaction...");
+let mut p2id_notes = vec![];
+
+// Creating 4 P2ID notes to 4 'dummy' AccountIds
+for _ in 1..=4 {
+ let init_seed: [u8; 15] = {
+ let mut init_seed = [0_u8; 15];
+ client.rng().fill_bytes(&mut init_seed);
+ init_seed
+ };
+ let target_account_id = AccountId::dummy(
+ init_seed,
+ AccountIdVersion::Version0,
+ AccountType::RegularAccountUpdatableCode,
+ AccountStorageMode::Public,
+ );
+
+ let send_amount = 50;
+ let fungible_asset = FungibleAsset::new(faucet_account.id(), send_amount).unwrap();
+
+ let p2id_note = P2idNote::create(
+ alice_account.id(),
+ target_account_id,
+ vec![fungible_asset.into()],
+ NoteType::Public,
+ NoteAttachment::default(),
+ client.rng(),
+ )?;
+ p2id_notes.push(p2id_note);
+}
+
+// Specifying output notes and creating a tx request to create them
+let transaction_request = TransactionRequestBuilder::new()
+ .own_output_notes(p2id_notes)
+ .build()
+ .unwrap();
+
+let tx_id = client
+ .submit_new_transaction(alice_account.id(), transaction_request)
+ .await?;
+
+println!("Submitted a transaction with 4 P2ID notes. TX: {:?}", tx_id);
+```
+
+### Basic P2ID transfer
+
+Now as an example, Alice will send some tokens to an account in a single transaction.
+
+Add this snippet to the end of your file in the `main()` function:
+
+```rust ignore
+println!("Submitting one more single P2ID transaction...");
+let init_seed: [u8; 15] = {
+ let mut init_seed = [0_u8; 15];
+ client.rng().fill_bytes(&mut init_seed);
+ init_seed
+};
+let target_account_id = AccountId::dummy(
+ init_seed,
+ AccountIdVersion::Version0,
+ AccountType::RegularAccountUpdatableCode,
+ AccountStorageMode::Public,
+);
+
+let send_amount = 50;
+let fungible_asset = FungibleAsset::new(faucet_account.id(), send_amount).unwrap();
+
+let p2id_note = P2idNote::create(
+ alice_account.id(),
+ target_account_id,
+ vec![fungible_asset.into()],
+ NoteType::Public,
+ NoteAttachment::default(),
+ client.rng(),
+)?;
+
+let transaction_request = TransactionRequestBuilder::new()
+ .own_output_notes(vec![p2id_note])
+ .build()
+ .unwrap();
+
+let tx_id = client
+ .submit_new_transaction(alice_account.id(), transaction_request)
+ .await?;
+
+println!("Submitted final P2ID transaction. TX: {:?}", tx_id);
+```
+
+Note: _In a production environment do not use `AccountId::dummy()`, this is simply for the sake of the tutorial example._
+
+## Summary
+
+Your `src/main.rs` function should now look like this:
+
+```rust no_run
+use miden_client::auth::{AuthSchemeId, AuthSingleSig};
+use rand::RngCore;
+use std::sync::Arc;
+use tokio::time::Duration;
+
+use miden_client::{
+ account::{
+ component::{AuthControlled, BasicFungibleFaucet, BasicWallet},
+ AccountId,
+ },
+ address::NetworkId,
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::{NoteAttachment, NoteType, P2idNote},
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_protocol::account::AccountIdVersion;
+use miden_client::{
+ account::{AccountBuilder, AccountStorageMode, AccountType},
+ asset::{FungibleAsset, TokenSymbol},
+ Felt,
+};
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ //------------------------------------------------------------
+ // STEP 1: Create a basic wallet for Alice
+ //------------------------------------------------------------
+ println!("\n[STEP 1] Creating a new account for Alice");
+
+ // Account seed
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ // Build the account
+ let alice_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicWallet)
+ .build()
+ .unwrap();
+
+ // Add the account to the client
+ client.add_account(&alice_account, false).await?;
+
+ // Add the key pair to the keystore
+ keystore.add_key(&key_pair, alice_account.id()).await.unwrap();
+
+ let alice_account_id_bech32 = alice_account.id().to_bech32(NetworkId::Testnet);
+ println!("Alice's account ID: {:?}", alice_account_id_bech32);
+
+ //------------------------------------------------------------
+ // STEP 2: Deploy a fungible faucet
+ //------------------------------------------------------------
+ println!("\n[STEP 2] Deploying a new fungible faucet.");
+
+ // Faucet seed
+ let mut init_seed = [0u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ // Faucet parameters
+ let symbol = TokenSymbol::new("MID").unwrap();
+ let decimals = 8;
+ let max_supply = Felt::new(1_000_000);
+
+ // Generate key pair
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ // Build the faucet account
+ let faucet_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply).unwrap())
+ .with_component(AuthControlled::allow_all())
+ .build()
+ .unwrap();
+
+ // Add the faucet to the client
+ client.add_account(&faucet_account, false).await?;
+
+ // Add the key pair to the keystore
+ keystore.add_key(&key_pair, faucet_account.id()).await.unwrap();
+
+ let faucet_account_id_bech32 = faucet_account.id().to_bech32(NetworkId::Testnet);
+ println!("Faucet account ID: {:?}", faucet_account_id_bech32);
+
+ // Resync to show newly deployed faucet
+ client.sync_state().await?;
+ tokio::time::sleep(Duration::from_secs(2)).await;
+
+ //------------------------------------------------------------
+ // STEP 3: Mint 5 notes of 100 tokens for Alice
+ //------------------------------------------------------------
+ println!("\n[STEP 3] Minting 5 notes of 100 tokens each for Alice.");
+
+ let amount: u64 = 100;
+ let fungible_asset = FungibleAsset::new(faucet_account.id(), amount).unwrap();
+
+ for i in 1..=5 {
+ let transaction_request = TransactionRequestBuilder::new()
+ .build_mint_fungible_asset(
+ fungible_asset,
+ alice_account.id(),
+ NoteType::Public,
+ client.rng(),
+ )
+ .unwrap();
+
+ println!("tx request built");
+
+ let tx_id = client
+ .submit_new_transaction(faucet_account.id(), transaction_request)
+ .await?;
+ println!(
+ "Minted note #{} of {} tokens for Alice. TX: {:?}",
+ i, amount, tx_id
+ );
+ }
+ println!("All 5 notes minted for Alice successfully!");
+
+ // Re-sync so minted notes become visible
+ client.sync_state().await?;
+
+ //------------------------------------------------------------
+ // STEP 4: Alice consumes all her notes
+ //------------------------------------------------------------
+ println!("\n[STEP 4] Alice will now consume all of her notes to consolidate them.");
+
+ // Consume all minted notes in a single transaction
+ loop {
+ // Resync to get the latest data
+ client.sync_state().await?;
+
+ let consumable_notes = client
+ .get_consumable_notes(Some(alice_account.id()))
+ .await?;
+ let notes = consumable_notes
+ .iter()
+ .map(|(note, _)| note.clone().try_into())
+ .collect::, _>>()?;
+
+ if notes.len() == 5 {
+ println!("Found 5 consumable notes for Alice. Consuming them now...");
+ let transaction_request = TransactionRequestBuilder::new()
+ .build_consume_notes(notes)
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(alice_account.id(), transaction_request)
+ .await?;
+ println!(
+ "All of Alice's notes consumed successfully. TX: {:?}",
+ tx_id
+ );
+ break;
+ } else {
+ println!(
+ "Currently, Alice has {} consumable notes. Waiting...",
+ notes.len()
+ );
+ tokio::time::sleep(Duration::from_secs(3)).await;
+ }
+ }
+
+ //------------------------------------------------------------
+ // STEP 5: Alice sends 5 notes of 50 tokens to 5 users
+ //------------------------------------------------------------
+ println!("\n[STEP 5] Alice sends 5 notes of 50 tokens each to 5 different users.");
+
+ // Send 50 tokens to 4 accounts in one transaction
+ println!("Creating multiple P2ID notes for 4 target accounts in one transaction...");
+ let mut p2id_notes = vec![];
+
+ // Creating 4 P2ID notes to 4 'dummy' AccountIds
+ for _ in 1..=4 {
+ let init_seed: [u8; 15] = {
+ let mut init_seed = [0_u8; 15];
+ client.rng().fill_bytes(&mut init_seed);
+ init_seed
+ };
+ let target_account_id = AccountId::dummy(
+ init_seed,
+ AccountIdVersion::Version0,
+ AccountType::RegularAccountUpdatableCode,
+ AccountStorageMode::Public,
+ );
+
+ let send_amount = 50;
+ let fungible_asset = FungibleAsset::new(faucet_account.id(), send_amount).unwrap();
+
+ let p2id_note = P2idNote::create(
+ alice_account.id(),
+ target_account_id,
+ vec![fungible_asset.into()],
+ NoteType::Public,
+ NoteAttachment::default(),
+ client.rng(),
+ )?;
+ p2id_notes.push(p2id_note);
+ }
+
+ // Specifying output notes and creating a tx request to create them
+ let transaction_request = TransactionRequestBuilder::new()
+ .own_output_notes(p2id_notes)
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(alice_account.id(), transaction_request)
+ .await?;
+
+ println!("Submitted a transaction with 4 P2ID notes. TX: {:?}", tx_id);
+
+ println!("Submitting one more single P2ID transaction...");
+ let init_seed: [u8; 15] = {
+ let mut init_seed = [0_u8; 15];
+ client.rng().fill_bytes(&mut init_seed);
+ init_seed
+ };
+ let target_account_id = AccountId::dummy(
+ init_seed,
+ AccountIdVersion::Version0,
+ AccountType::RegularAccountUpdatableCode,
+ AccountStorageMode::Public,
+ );
+
+ let send_amount = 50;
+ let fungible_asset = FungibleAsset::new(faucet_account.id(), send_amount).unwrap();
+
+ let p2id_note = P2idNote::create(
+ alice_account.id(),
+ target_account_id,
+ vec![fungible_asset.into()],
+ NoteType::Public,
+ NoteAttachment::default(),
+ client.rng(),
+ )?;
+
+ let transaction_request = TransactionRequestBuilder::new()
+ .own_output_notes(vec![p2id_note])
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(alice_account.id(), transaction_request)
+ .await?;
+
+ println!("Submitted final P2ID transaction. TX: {:?}", tx_id);
+
+ println!("\nAll steps completed successfully!");
+ println!("Alice created a wallet, a faucet was deployed,");
+ println!("5 notes of 100 tokens were minted to Alice, those notes were consumed,");
+ println!("and then Alice sent 5 separate 50-token notes to 5 different users.");
+
+ Ok(())
+}
+```
+
+Let's run the `src/main.rs` program again:
+
+```bash
+cargo run --release
+```
+
+The output will look like this:
+
+```text
+Latest block: 226896
+
+[STEP 1] Creating a new account for Alice
+Alice's account ID: ""
+
+[STEP 2] Deploying a new fungible faucet.
+Faucet account ID: ""
+
+[STEP 3] Minting 5 notes of 100 tokens each for Alice.
+tx request built
+Minted note #1 of 100 tokens for Alice.
+tx request built
+Minted note #2 of 100 tokens for Alice.
+tx request built
+Minted note #3 of 100 tokens for Alice.
+tx request built
+Minted note #4 of 100 tokens for Alice.
+tx request built
+Minted note #5 of 100 tokens for Alice.
+All 5 notes minted for Alice successfully!
+
+[STEP 4] Alice will now consume all of her notes to consolidate them.
+Currently, Alice has 2 consumable notes. Waiting...
+Currently, Alice has 4 consumable notes. Waiting...
+Found 5 consumable notes for Alice. Consuming them now...
+All of Alice's notes consumed successfully.
+
+[STEP 5] Alice sends 5 notes of 50 tokens each to 5 different users.
+Creating multiple P2ID notes for 4 target accounts in one transaction...
+Submitted a transaction with 4 P2ID notes.
+Submitting one more single P2ID transaction...
+
+All steps completed successfully!
+Alice created a wallet, a faucet was deployed,
+5 notes of 100 tokens were minted to Alice, those notes were consumed,
+and then Alice sent 5 separate 50-token notes to 5 different users.
+```
+
+### Running the example
+
+To run a full working example navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run this command:
+
+```bash
+cd rust-client
+cargo run --release --bin create_mint_consume_send
+```
+
+### Continue learning
+
+Next tutorial: [Deploying a Counter Contract](counter_contract_tutorial.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/network_transactions_tutorial.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/network_transactions_tutorial.md
new file mode 100644
index 00000000..af58f456
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/network_transactions_tutorial.md
@@ -0,0 +1,859 @@
+---
+title: "Network Transactions on Miden"
+sidebar_position: 6
+---
+
+# Network Transactions on Miden
+
+_Using the Miden client in Rust to deploy and interact with smart contracts using network transactions_
+
+## Overview
+
+In this tutorial, we will explore Network Transactions (NTXs) on Miden - a powerful feature that enables autonomous smart contract execution and public shared state management. Unlike local transactions that require users to execute and prove, network transactions are executed and proven by a network transaction builder.
+
+We'll build a network counter smart contract using the same MASM code as the regular counter, but with different storage configuration in Rust to enable network execution.
+
+## What we'll cover
+
+- Understanding Network Transactions and when to use them
+- Deploying smart contracts with network storage mode
+- Using transaction scripts to initialize network contracts on-chain
+- Creating network notes for user interactions
+- Validating network transaction results
+
+## Prerequisites
+
+This tutorial assumes you have completed the [counter contract tutorial](counter_contract_tutorial.md) and understand basic Miden assembly.
+
+## What are Network Transactions?
+
+Network transactions are executed and proven by the Miden operator rather than the client. They are useful for:
+
+- **Public shared state**: Multiple users can interact with the same contract state without race conditions
+- **Autonomous execution**: Smart contracts can execute when conditions are met without user intervention
+- **Resource-constrained devices**: Clients that can't generate ZK proofs efficiently
+- **AMM applications**: Using network notes, you can build sophisticated AMMs where trades execute automatically
+
+The main trade-off is reduced privacy since the operator can see transaction inputs.
+
+## Step 1: Initialize your repository
+
+Create a new Rust repository for your Miden project and navigate to it:
+
+```bash
+cargo new miden-network-transactions
+cd miden-network-transactions
+```
+
+Add the following dependencies to your `Cargo.toml` file:
+
+```toml
+[dependencies]
+miden-client = { version = "0.14", features = ["testing", "tonic"] }
+miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" }
+miden-protocol = { version = "0.14" }
+rand = { version = "0.9" }
+serde = { version = "1", features = ["derive"] }
+serde_json = { version = "1.0", features = ["raw_value"] }
+tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] }
+rand_chacha = "0.9.0"
+```
+
+## Step 2: Set up MASM files
+
+Create the directory structure:
+
+```bash
+mkdir -p masm/accounts masm/scripts masm/notes
+```
+
+### Counter Contract
+
+We'll use the same counter contract MASM code as the regular counter tutorial. The key difference is in the Rust configuration, not the MASM code.
+
+Create `masm/accounts/counter.masm`:
+
+```masm
+use miden::protocol::active_account
+use miden::protocol::native_account
+use miden::core::word
+use miden::core::sys
+
+const COUNTER_SLOT = word("miden::tutorials::counter")
+
+#! Inputs: []
+#! Outputs: [count]
+pub proc get_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ # => [count]
+
+ exec.sys::truncate_stack
+ # => [count]
+end
+
+#! Inputs: []
+#! Outputs: []
+pub proc increment_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ # => [count]
+
+ add.1
+ # => [count+1]
+
+ push.COUNTER_SLOT[0..2] exec.native_account::set_item
+ # => []
+
+ exec.sys::truncate_stack
+ # => []
+end
+```
+
+### Transaction Script for Deployment
+
+Create `masm/scripts/counter_script.masm`:
+
+```masm
+use external_contract::counter_contract
+
+begin
+ call.counter_contract::increment_count
+end
+```
+
+This script executes a function call (increment) that creates a necessary state change for our contract to be deployed and stored on the network on-chain. In Miden, network contracts must have their state modified through a transaction to be properly registered and committed to the blockchain - simply creating the account isn't sufficient for network storage mode.
+
+### Network Note for User Interaction
+
+Create `masm/notes/network_increment_note.masm`:
+
+```masm
+use external_contract::counter_contract
+
+begin
+ call.counter_contract::increment_count
+end
+```
+
+After deployment, users will interact with the contract through these network notes.
+
+## Step 3: Initialize the client and create a user account
+
+Before deploying the network account and creating network notes, we need to set up the client and create a user account that will interact with our network contract.
+
+Copy and paste the following code into your `src/main.rs` file:
+
+```rust no_run
+use std::{fs, path::Path, sync::Arc};
+
+use miden_client::account::component::BasicWallet;
+use miden_client::{
+ address::NetworkId,
+ auth::AuthSecretKey,
+ crypto::FeltRng,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::{
+ NetworkAccountTarget, Note, NoteAssets, NoteError, NoteExecutionHint, NoteMetadata,
+ NoteRecipient, NoteStorage, NoteTag, NoteType,
+ },
+ rpc::{Endpoint, GrpcClient},
+ store::TransactionFilter,
+ transaction::{TransactionId, TransactionRequestBuilder, TransactionStatus},
+ Client, ClientError, Felt, Word,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::auth::{self, AuthSchemeId, AuthSingleSig};
+use miden_client::transaction::TransactionKernel;
+use miden_client::{
+ account::{
+ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode,
+ AccountType, StorageSlot, StorageSlotName,
+ },
+ assembly::{
+ Assembler,
+ CodeBuilder,
+ DefaultSourceManager,
+ Library,
+ Module,
+ ModuleKind,
+ Path as AssemblyPath,
+ },
+};
+use rand::RngCore;
+use tokio::time::{sleep, Duration};
+
+/// Waits for a specific transaction to be committed.
+async fn wait_for_tx(
+ client: &mut Client,
+ tx_id: TransactionId,
+) -> Result<(), ClientError> {
+ loop {
+ client.sync_state().await?;
+
+ // Check transaction status
+ let txs = client
+ .get_transactions(TransactionFilter::Ids(vec![tx_id]))
+ .await?;
+ let tx_committed = if !txs.is_empty() {
+ matches!(txs[0].status, TransactionStatus::Committed { .. })
+ } else {
+ false
+ };
+
+ if tx_committed {
+ println!("✅ transaction {} committed", tx_id.to_hex());
+ break;
+ }
+
+ println!(
+ "Transaction {} not yet committed. Waiting...",
+ tx_id.to_hex()
+ );
+ sleep(Duration::from_secs(2)).await;
+ }
+ Ok(())
+}
+
+/// Creates a Miden library from the provided account code and library path.
+fn create_library(
+ account_code: String,
+ library_path: &str,
+) -> Result, Box> {
+ let assembler: Assembler = TransactionKernel::assembler();
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ account_code,
+ source_manager.clone(),
+ )?;
+ let library = assembler.clone().assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // -------------------------------------------------------------------------
+ // STEP 1: Create Basic User Account
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 1] Creating a new account for Alice");
+
+ // Account seed
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ // Build the account
+ let alice_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicWallet)
+ .build()
+ .unwrap();
+
+ // Add the account to the client
+ client.add_account(&alice_account, false).await?;
+
+ // Add the key pair to the keystore
+ keystore.add_key(&key_pair, alice_account.id()).await.unwrap();
+
+ println!(
+ "Alice's account ID: {:?}",
+ alice_account.id().to_bech32(NetworkId::Testnet)
+ );
+
+ Ok(())
+}
+```
+
+This step initializes the Miden client and creates a basic user account (Alice) that will interact with our network contract.
+
+## Step 4: Create the network counter smart contract
+
+Now we'll create a network smart contract. The key difference from regular contracts is using `AccountStorageMode::Network` instead of `AccountStorageMode::Public`.
+
+Add this code to your `main()` function:
+
+```rust ignore
+// -------------------------------------------------------------------------
+// STEP 2: Create Network Counter Smart Contract
+// -------------------------------------------------------------------------
+println!("\n[STEP 2] Creating a network counter smart contract");
+
+let counter_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap();
+
+// Create the network counter smart contract account
+// First, compile the MASM code into an account component
+let counter_slot_name =
+ StorageSlotName::new("miden::tutorials::counter").expect("valid slot name");
+let component_code = CodeBuilder::new()
+ .compile_component_code("external_contract::counter_contract", &counter_code)
+ .unwrap();
+let counter_component = AccountComponent::new(
+ component_code,
+ vec![StorageSlot::with_value(counter_slot_name.clone(), [Felt::new(0); 4].into())], // Initialize counter storage to 0
+ AccountComponentMetadata::new("external_contract::counter_contract", AccountType::all()),
+)
+.unwrap();
+
+// Generate a random seed for the account
+let mut init_seed = [0_u8; 32];
+client.rng().fill_bytes(&mut init_seed);
+
+// Build the immutable network account with no authentication
+let counter_contract = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountImmutableCode) // Immutable code
+ .storage_mode(AccountStorageMode::Network) // Stored on network
+ .with_auth_component(auth::NoAuth) // No authentication required
+ .with_component(counter_component)
+ .build()
+ .unwrap();
+
+client.add_account(&counter_contract, false).await.unwrap();
+
+println!(
+ "contract id: {:?}",
+ counter_contract.id().to_bech32(NetworkId::Testnet)
+);
+```
+
+This step creates a network smart contract with `AccountStorageMode::Network`, which enables the contract to be executed by the network operator.
+
+## Step 5: Deploy the network account with a transaction script
+
+We use a transaction script to deploy the network account and ensure it's properly registered on-chain. The script calls the `increment` function, which initializes the counter to 1.
+
+Add this code to your `main()` function:
+
+```rust ignore
+// -------------------------------------------------------------------------
+// STEP 3: Deploy Network Account with Transaction Script
+// -------------------------------------------------------------------------
+println!("\n[STEP 3] Deploy network counter smart contract");
+
+let script_code = fs::read_to_string(Path::new("../masm/scripts/counter_script.masm")).unwrap();
+
+let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap();
+let library_path = "external_contract::counter_contract";
+
+let library = create_library(account_code, library_path).unwrap();
+
+let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&library)?
+ .compile_tx_script(&script_code)?;
+
+let tx_increment_request = TransactionRequestBuilder::new()
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+let tx_id = client
+ .submit_new_transaction(counter_contract.id(), tx_increment_request)
+ .await
+ .unwrap();
+
+println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+);
+
+// Wait for the transaction to be committed
+wait_for_tx(&mut client, tx_id).await.unwrap();
+```
+
+This step uses a transaction script to deploy the network account and ensure it's properly registered on-chain. The script calls the `increment` function, which initializes the counter to 1.
+
+## Step 6: Create a network note for user interaction
+
+We create a public note that the network operator can consume to execute the increment function. This increments the counter from 1 to 2.
+
+Add this code to your `main()` function:
+
+```rust ignore
+// -------------------------------------------------------------------------
+// STEP 4: Prepare & Create the Network Note
+// -------------------------------------------------------------------------
+println!("\n[STEP 4] Creating a network note for network counter contract");
+
+let network_note_code =
+ fs::read_to_string(Path::new("../masm/notes/network_increment_note.masm")).unwrap();
+let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap();
+
+let library_path = "external_contract::counter_contract";
+let library = create_library(account_code, library_path).unwrap();
+
+// Create and submit the network note that will increment the counter
+// Generate a random serial number for the note
+let serial_num = client.rng().draw_word();
+
+// Compile the note script with the counter contract library
+let note_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&library)?
+ .compile_note_script(&network_note_code)?;
+
+// Create note recipient with empty storage
+let note_storage = NoteStorage::new([].to_vec())?;
+let recipient = NoteRecipient::new(serial_num, note_script, note_storage);
+
+// Set up note metadata - tag it with the counter contract ID so it gets consumed
+let tag = NoteTag::with_account_target(counter_contract.id());
+let attachment = NetworkAccountTarget::new(counter_contract.id(), NoteExecutionHint::Always)
+ .map_err(|e| NoteError::other(e.to_string()))?
+ .into();
+let metadata = NoteMetadata::new(alice_account.id(), NoteType::Public)
+ .with_tag(tag)
+ .with_attachment(attachment);
+
+// Create the complete note
+let increment_note = Note::new(NoteAssets::default(), metadata, recipient);
+
+// Build and submit the transaction containing the note
+let note_req = TransactionRequestBuilder::new()
+ .own_output_notes(vec![increment_note])
+ .build()?;
+
+let note_tx_id = client
+ .submit_new_transaction(alice_account.id(), note_req)
+ .await?;
+
+println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ note_tx_id
+);
+
+client.sync_state().await?;
+
+println!("network increment note creation tx submitted, waiting for onchain commitment");
+
+// Wait for the note transaction to be committed
+wait_for_tx(&mut client, note_tx_id).await.unwrap();
+
+// Waiting for network note to be picked up by the network transaction builder
+sleep(Duration::from_secs(6)).await;
+
+client.sync_state().await?;
+
+let mut last_val = None;
+for _ in 0..10 {
+ client.sync_state().await?;
+
+ // Checking updated state
+ let new_account_state = client.get_account(counter_contract.id()).await.unwrap();
+
+ if let Some(account) = new_account_state.as_ref() {
+ let count: Word = account.storage().get_item(&counter_slot_name).unwrap().into();
+ let val = count[0].as_canonical_u64();
+ if val >= 2 {
+ println!("🔢 Final counter value: {}", val);
+ return Ok(());
+ }
+ last_val = Some(val);
+ }
+
+ // Give the network note builder time to process the note.
+ sleep(Duration::from_secs(6)).await;
+}
+
+if let Some(val) = last_val {
+ println!(
+ "Counter value did not reach 2 yet (last observed value: {}).",
+ val
+ );
+} else {
+ println!("Counter value not available yet.");
+}
+```
+
+This step creates a public note that the network operator can consume to execute the increment function. This increments the counter from 1 to 2.
+
+## Summary
+
+Your complete `main()` function should look like this:
+
+```rust no_run
+use std::{fs, path::Path, sync::Arc};
+
+use miden_client::account::component::BasicWallet;
+use miden_client::{
+ address::NetworkId,
+ auth::AuthSecretKey,
+ crypto::FeltRng,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::{
+ NetworkAccountTarget, Note, NoteAssets, NoteError, NoteExecutionHint, NoteMetadata,
+ NoteRecipient, NoteStorage, NoteTag, NoteType,
+ },
+ rpc::{Endpoint, GrpcClient},
+ store::TransactionFilter,
+ transaction::{TransactionId, TransactionRequestBuilder, TransactionStatus},
+ Client, ClientError, Felt, Word,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::auth::{self, AuthSchemeId, AuthSingleSig};
+use miden_client::transaction::TransactionKernel;
+use miden_client::{
+ account::{
+ component::AccountComponentMetadata, AccountBuilder, AccountComponent, AccountStorageMode,
+ AccountType, StorageSlot, StorageSlotName,
+ },
+ assembly::{
+ Assembler,
+ CodeBuilder,
+ DefaultSourceManager,
+ Library,
+ Module,
+ ModuleKind,
+ Path as AssemblyPath,
+ },
+};
+use rand::RngCore;
+use tokio::time::{sleep, Duration};
+
+/// Waits for a specific transaction to be committed.
+async fn wait_for_tx(
+ client: &mut Client,
+ tx_id: TransactionId,
+) -> Result<(), ClientError> {
+ loop {
+ client.sync_state().await?;
+
+ // Check transaction status
+ let txs = client
+ .get_transactions(TransactionFilter::Ids(vec![tx_id]))
+ .await?;
+ let tx_committed = if !txs.is_empty() {
+ matches!(txs[0].status, TransactionStatus::Committed { .. })
+ } else {
+ false
+ };
+
+ if tx_committed {
+ println!("✅ transaction {} committed", tx_id.to_hex());
+ break;
+ }
+
+ println!(
+ "Transaction {} not yet committed. Waiting...",
+ tx_id.to_hex()
+ );
+ sleep(Duration::from_secs(2)).await;
+ }
+ Ok(())
+}
+
+/// Creates a Miden library from the provided account code and library path.
+fn create_library(
+ account_code: String,
+ library_path: &str,
+) -> Result, Box> {
+ let assembler: Assembler = TransactionKernel::assembler();
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ account_code,
+ source_manager.clone(),
+ )?;
+ let library = assembler.clone().assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // -------------------------------------------------------------------------
+ // STEP 1: Create Basic User Account
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 1] Creating a new account for Alice");
+
+ // Account seed
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ // Build the account
+ let alice_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicWallet)
+ .build()
+ .unwrap();
+
+ // Add the account to the client
+ client.add_account(&alice_account, false).await?;
+
+ // Add the key pair to the keystore
+ keystore.add_key(&key_pair, alice_account.id()).await.unwrap();
+
+ println!(
+ "Alice's account ID: {:?}",
+ alice_account.id().to_bech32(NetworkId::Testnet)
+ );
+
+ // -------------------------------------------------------------------------
+ // STEP 2: Create Network Counter Smart Contract
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 2] Creating a network counter smart contract");
+
+ let counter_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap();
+
+ // Create the network counter smart contract account
+ // First, compile the MASM code into an account component
+ let counter_slot_name =
+ StorageSlotName::new("miden::tutorials::counter").expect("valid slot name");
+ let component_code = CodeBuilder::new()
+ .compile_component_code("external_contract::counter_contract", &counter_code)
+ .unwrap();
+ let counter_component = AccountComponent::new(
+ component_code,
+ vec![StorageSlot::with_value(counter_slot_name.clone(), [Felt::new(0); 4].into())], // Initialize counter storage to 0
+ AccountComponentMetadata::new("external_contract::counter_contract", AccountType::all()),
+ )
+ .unwrap();
+
+ // Generate a random seed for the account
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ // Build the immutable network account with no authentication
+ let counter_contract = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountImmutableCode) // Immutable code
+ .storage_mode(AccountStorageMode::Network) // Stored on network
+ .with_auth_component(auth::NoAuth) // No authentication required
+ .with_component(counter_component)
+ .build()
+ .unwrap();
+
+ client.add_account(&counter_contract, false).await.unwrap();
+
+ println!(
+ "contract id: {:?}",
+ counter_contract.id().to_bech32(NetworkId::Testnet)
+ );
+
+ // -------------------------------------------------------------------------
+ // STEP 3: Deploy Network Account with Transaction Script
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 3] Deploy network counter smart contract");
+
+ let script_code = fs::read_to_string(Path::new("../masm/scripts/counter_script.masm")).unwrap();
+
+ let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap();
+ let library_path = "external_contract::counter_contract";
+
+ let library = create_library(account_code, library_path).unwrap();
+
+ let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&library)?
+ .compile_tx_script(&script_code)?;
+
+ let tx_increment_request = TransactionRequestBuilder::new()
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(counter_contract.id(), tx_increment_request)
+ .await
+ .unwrap();
+
+ println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+
+ // Wait for the transaction to be committed
+ wait_for_tx(&mut client, tx_id).await.unwrap();
+
+ // -------------------------------------------------------------------------
+ // STEP 4: Prepare & Create the Network Note
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 4] Creating a network note for network counter contract");
+
+ let network_note_code =
+ fs::read_to_string(Path::new("../masm/notes/network_increment_note.masm")).unwrap();
+ let account_code = fs::read_to_string(Path::new("../masm/accounts/counter.masm")).unwrap();
+
+ let library_path = "external_contract::counter_contract";
+ let library = create_library(account_code, library_path).unwrap();
+
+ // Create and submit the network note that will increment the counter
+ // Generate a random serial number for the note
+ let serial_num = client.rng().draw_word();
+
+ // Compile the note script with the counter contract library
+ let note_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&library)?
+ .compile_note_script(&network_note_code)?;
+
+ // Create note recipient with empty inputs
+ let note_storage = NoteStorage::new([].to_vec())?;
+ let recipient = NoteRecipient::new(serial_num, note_script, note_storage);
+
+ // Set up note metadata - tag it with the counter contract ID so it gets consumed
+ let tag = NoteTag::with_account_target(counter_contract.id());
+ let attachment = NetworkAccountTarget::new(counter_contract.id(), NoteExecutionHint::Always)
+ .map_err(|e| NoteError::other(e.to_string()))?
+ .into();
+ let metadata = NoteMetadata::new(alice_account.id(), NoteType::Public)
+ .with_tag(tag)
+ .with_attachment(attachment);
+
+ // Create the complete note
+ let increment_note = Note::new(NoteAssets::default(), metadata, recipient);
+
+ // Build and submit the transaction containing the note
+ let note_req = TransactionRequestBuilder::new()
+ .own_output_notes(vec![increment_note])
+ .build()?;
+
+ let note_tx_id = client
+ .submit_new_transaction(alice_account.id(), note_req)
+ .await?;
+
+ println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ note_tx_id
+ );
+
+ client.sync_state().await?;
+
+ println!("network increment note creation tx submitted, waiting for onchain commitment");
+
+ // Wait for the note transaction to be committed
+ wait_for_tx(&mut client, note_tx_id).await.unwrap();
+
+ // Waiting for network note to be picked up by the network transaction builder
+ sleep(Duration::from_secs(6)).await;
+
+ client.sync_state().await?;
+
+ let mut last_val = None;
+ for _ in 0..10 {
+ client.sync_state().await?;
+
+ // Checking updated state
+ let new_account_state = client.get_account(counter_contract.id()).await.unwrap();
+
+ if let Some(account) = new_account_state.as_ref() {
+ let count: Word = account.storage().get_item(&counter_slot_name).unwrap().into();
+ let val = count[0].as_canonical_u64();
+ if val >= 2 {
+ println!("🔢 Final counter value: {}", val);
+ return Ok(());
+ }
+ last_val = Some(val);
+ }
+
+ // Give the network note builder time to process the note.
+ sleep(Duration::from_secs(6)).await;
+ }
+
+ if let Some(val) = last_val {
+ println!(
+ "Counter value did not reach 2 yet (last observed value: {}).",
+ val
+ );
+ } else {
+ println!("Counter value not available yet.");
+ }
+
+ Ok(())
+}
+```
+
+## Step 7: Running the Example
+
+To run the complete network transaction example:
+
+```bash
+cd rust-client
+cargo run --release --bin network_notes_counter_contract
+```
+
+Expected output:
+
+```text
+Latest block: 4342
+
+[STEP 1] Creating a new account for Alice
+Alice's account ID: "mtst1azkn605dchqv7yrd9crnvrkknvw8j4d3"
+
+[STEP 2] Creating a network counter smart contract
+contract id: "mtst1ar5dqpk49zjsvsqenfuqzskcvvmf9spc"
+
+[STEP 3] Deploy network counter smart contract
+View transaction on MidenScan: https://testnet.midenscan.com/tx/0xe28fa8e527335499d972e653dfd944ad591752e537a41b151e7b80d598c5660c
+✅ transaction 0xe28fa8e527335499d972e653dfd944ad591752e537a41b151e7b80d598c5660c committed
+
+[STEP 4] Creating a network note for network counter contract
+View transaction on MidenScan: https://testnet.midenscan.com/tx/0x3cd653f2848f2fbc3de76d7b0a92c82d23ad1f9f24c9fb86d58772534e17ee30
+network increment note created, waiting for onchain commitment
+✅ transaction 0x3cd653f2848f2fbc3de76d7b0a92c82d23ad1f9f24c9fb86d58772534e17ee30 committed
+🔢 Final counter value: 2
+```
+
+## Summary
+
+Network transactions on Miden enable powerful use cases by allowing the operator to execute transactions on behalf of users. The key steps are:
+
+1. **Create user account**: Standard account creation for interaction
+2. **Create network account**: Use `AccountStorageMode::Network` instead of `Public`
+3. **Deploy with transaction script**: Ensures the contract is registered on-chain
+4. **Interact with network notes**: Users create public notes that the operator executes
+
+The same MASM code works for both regular and network contracts - the difference is purely in the Rust configuration. This makes network transactions a powerful tool for building applications like AMMs where multiple users need to interact with shared state efficiently.
+
+### Continue learning
+
+Next tutorial: [How To Create Notes with Custom Logic](custom_note_how_to.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/oracle_tutorial.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/oracle_tutorial.md
new file mode 100644
index 00000000..9b649848
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/oracle_tutorial.md
@@ -0,0 +1,443 @@
+---
+title: "Consuming On-Chain Price Data from the Pragma Oracle"
+sidebar_position: 13
+---
+
+# Consuming On-Chain Price Data from the Pragma Oracle
+
+_Using the Pragma oracle to get on chain price data_
+
+## Overview
+
+In this tutorial, we will build a simple “price reader” smart contract that will read Bitcoin price data from the on-chain Pragma oracle.
+
+We will use a script to call the `read_price` function in our "price reader" smart contract, which, in turn, calls the Pragma oracle via foreign procedure invocation (FPI). This tutorial lays the foundation for how you can integrate on-chain price data into your DeFi applications on Miden.
+
+## What we'll cover
+
+- Deploying a smart contract that can read oracle price data
+- Using foreign procedure invocation to get real time on-chain price data
+
+## Prerequisites
+
+This tutorial assumes you have a basic understanding of Miden assembly, have completed the previous tutorials on using the Rust client, and have completed the tutorial on foreign procedure invocation.
+
+To quickly get up to speed with Miden assembly (MASM), please play around with running Miden programs in the [Miden playground](https://0xMiden.github.io/examples/).
+
+## Step 1: Initialize your repository
+
+Create a new Rust repository for your Miden project and navigate to it with the following command:
+
+```bash
+cargo new miden-defi-app
+cd miden-defi-app
+```
+
+Add the following dependencies to your `Cargo.toml` file:
+
+```toml
+[dependencies]
+miden-client = { version = "0.14", features = ["testing", "tonic"] }
+miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" }
+miden-protocol = { version = "0.14" }
+rand = { version = "0.9" }
+serde = { version = "1", features = ["derive"] }
+serde_json = { version = "1.0", features = ["raw_value"] }
+tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] }
+rand_chacha = "0.9.0"
+```
+
+### Step 1: Set up your `src/main.rs` file
+
+Copy and paste the following code into your `src/main.rs` file:
+
+```rust no_run
+use miden_client::{
+ assembly::{
+ Assembler,
+ CodeBuilder,
+ DefaultSourceManager,
+ Module,
+ ModuleKind,
+ Path as AssemblyPath,
+ },
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{
+ domain::account::{AccountStorageRequirements, StorageMapKey},
+ Endpoint, GrpcClient,
+ },
+ transaction::{ForeignAccount, TransactionRequestBuilder},
+ Client, ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::{auth::NoAuth, transaction::TransactionKernel};
+use miden_client::{
+ account::{
+ component::AccountComponentMetadata, AccountComponent, AccountId, AccountStorageMode,
+ AccountType, StorageSlot, StorageSlotName, StorageSlotType,
+ },
+ Felt, Word, ZERO,
+};
+use rand::RngCore;
+use std::{fs, path::Path, sync::Arc};
+
+/// Import the oracle + its publishers and return the ForeignAccount list
+/// Due to Pragma's decentralized oracle architecture, we need to get the
+/// list of all data publisher accounts to read price from via a nested FPI call
+pub async fn get_oracle_foreign_accounts(
+ client: &mut Client,
+ oracle_account_id: AccountId,
+ trading_pair: u64,
+) -> Result, ClientError> {
+ client.import_account_by_id(oracle_account_id).await?;
+
+ let oracle_account = client
+ .get_account(oracle_account_id)
+ .await
+ .expect("RPC failed")
+ .expect("oracle account not found");
+
+ let storage = oracle_account.storage();
+ let publisher_count_slot = storage
+ .slots()
+ .iter()
+ .find(|slot| {
+ let name = slot.name().as_str();
+ name.contains("publisher") && name.contains("count")
+ })
+ .map(|slot| slot.name().clone())
+ .or_else(|| storage.slots().first().map(|slot| slot.name().clone()))
+ .expect("oracle storage is expected to have at least one slot");
+
+ let publisher_count = storage
+ .get_item(&publisher_count_slot)
+ .map(|word| word[0].as_canonical_u64())
+ .unwrap_or(0);
+
+ let publisher_id_slots: Vec = storage
+ .slots()
+ .iter()
+ .filter(|slot| slot.slot_type() == StorageSlotType::Value)
+ .filter(|slot| slot.name() != &publisher_count_slot)
+ .map(|slot| slot.name().clone())
+ .collect();
+
+ let publisher_ids: Vec = publisher_id_slots
+ .iter()
+ .take(publisher_count.saturating_sub(1) as usize)
+ .filter_map(|slot_name| storage.get_item(slot_name).ok())
+ .map(|digest| {
+ let words: Word = digest.into();
+ AccountId::new_unchecked([words[3], words[2]])
+ })
+ .collect();
+
+ let mut foreign_accounts = Vec::with_capacity(publisher_ids.len() + 1);
+ let empty_keys: [StorageMapKey; 0] = [];
+
+ for pid in publisher_ids {
+ client.import_account_by_id(pid).await?;
+
+ let publisher_account = client
+ .get_account(pid)
+ .await
+ .expect("RPC failed")
+ .expect("publisher account not found");
+ let map_slot_names: Vec = publisher_account
+ .storage()
+ .slots()
+ .iter()
+ .filter(|slot| slot.slot_type() == StorageSlotType::Map)
+ .map(|slot| slot.name().clone())
+ .collect();
+
+ let storage_requirements = AccountStorageRequirements::new(
+ map_slot_names
+ .iter()
+ .map(|slot_name| (slot_name.clone(), empty_keys.iter())),
+ );
+
+ foreign_accounts.push(ForeignAccount::public(pid, storage_requirements)?);
+ }
+
+ foreign_accounts.push(ForeignAccount::public(
+ oracle_account_id,
+ AccountStorageRequirements::default(),
+ )?);
+
+ Ok(foreign_accounts)
+}
+
+fn create_library(
+ assembler: Assembler,
+ library_path: &str,
+ source_code: &str,
+) -> Result, Box> {
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ source_code,
+ source_manager.clone(),
+ )?;
+ let library = assembler.clone().assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // -------------------------------------------------------------------------
+ // Initialize Client
+ // -------------------------------------------------------------------------
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ println!("Latest block: {}", client.sync_state().await?.block_num);
+
+ // -------------------------------------------------------------------------
+ // Get all foreign accounts for oracle data
+ // -------------------------------------------------------------------------
+ // The oracle account ID must be supplied as a CLI argument.
+ let oracle_bech32 = std::env::args()
+ .nth(1)
+ .expect("Usage: oracle_data_query ");
+ let (_, oracle_account_id) = AccountId::from_bech32(&oracle_bech32).unwrap();
+ let btc_usd_pair_id = 120195681;
+ let foreign_accounts: Vec =
+ get_oracle_foreign_accounts(&mut client, oracle_account_id, btc_usd_pair_id).await?;
+
+ println!(
+ "Oracle accountId prefix: {:?} suffix: {:?}",
+ oracle_account_id.prefix(),
+ oracle_account_id.suffix()
+ );
+
+ // -------------------------------------------------------------------------
+ // Create Oracle Reader contract
+ // -------------------------------------------------------------------------
+ let contract_code =
+ fs::read_to_string(Path::new("../masm/accounts/oracle_reader.masm")).unwrap();
+
+ let contract_slot_name =
+ StorageSlotName::new("miden::tutorials::oracle_reader").expect("valid slot name");
+ let contract_component_code = CodeBuilder::new()
+ .compile_component_code("external_contract::oracle_reader", &contract_code)
+ .unwrap();
+ let contract_component = AccountComponent::new(
+ contract_component_code,
+ vec![StorageSlot::with_value(contract_slot_name.clone(), Word::default())],
+ AccountComponentMetadata::new("external_contract::oracle_reader", AccountType::all()),
+ )
+ .unwrap();
+
+ let mut seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut seed);
+
+ let oracle_reader_contract = miden_client::account::AccountBuilder::new(seed)
+ .account_type(AccountType::RegularAccountImmutableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_component(contract_component.clone())
+ .with_auth_component(NoAuth)
+ .build()
+ .unwrap();
+
+ client
+ .add_account(&oracle_reader_contract, false)
+ .await
+ .unwrap();
+
+ // -------------------------------------------------------------------------
+ // Build the script that calls our `get_price` procedure
+ // -------------------------------------------------------------------------
+ let script_path = Path::new("../masm/scripts/oracle_reader_script.masm");
+ let script_code = fs::read_to_string(script_path).unwrap();
+
+ let assembler = TransactionKernel::assembler();
+ let library_path = "external_contract::oracle_reader";
+ let account_component_lib =
+ create_library(assembler.clone(), library_path, &contract_code).unwrap();
+
+ let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&account_component_lib)
+ .unwrap()
+ .compile_tx_script(&script_code)
+ .unwrap();
+
+ let tx_increment_request = TransactionRequestBuilder::new()
+ .foreign_accounts(foreign_accounts)
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(oracle_reader_contract.id(), tx_increment_request)
+ .await
+ .unwrap();
+
+ println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+
+ client.sync_state().await.unwrap();
+
+ Ok(())
+}
+```
+
+_Don't run this code just yet, we still need to create our smart contract that queries the oracle_
+
+In the code above, we specified the Pragma oracle account id `0x4f67e78643022e00000220d8997e33` and the BTC/USD pair `120195681`. The `get_oracle_foreign_accounts` function returns all of the `ForeignAccounts` that you will need to execute the transaction to get the price data from the oracle. Since Pragma's oracle depends on multiple publishers, this function queries all of the publisher account ids required to make a successful FPI call.
+
+:::note
+The oracle account ID, procedure hash, and trading pair ID used in this tutorial reference Pragma's testnet deployment. These values are maintained by Pragma and may change if they redeploy their oracle. For the latest values, check the [Pragma Miden repository](https://github.com/astraly-labs/pragma-miden).
+:::
+
+## Step 2: Build the price reader smart contract and script
+
+Just like in previous tutorials, for better code organization we will separate the Miden assembly code from our Rust code.
+
+Create a directory named `masm` at the **root** of your `miden-counter-contract` directory. This will contain our contract and script masm code.
+
+Initialize the `masm` directory:
+
+```bash
+mkdir -p masm/accounts masm/scripts
+```
+
+This will create:
+
+```
+masm/
+├── accounts/
+└── scripts/
+```
+
+### Oracle price reader smart contract
+
+Below is our oracle price reader contract. It has a a single exported procedure: `get_price`
+
+The import `miden::tx` contains the `tx::execute_foreign_procedure` which we will use to read the price from the oracle contract.
+
+#### Here's a breakdown of what the `get_price` procedure does:
+
+1. Pushes `0.0.0.120195681` onto the stack, representing the BTC/USD pair in the Pragma oracle.
+2. Pushes `0xb86237a8c9cd35acfef457e47282cc4da43df676df410c988eab93095d8fb3b9` onto the stack which is the procedure root of the `get_median` procedure in the oracle.
+3. Pushes `939716883672832.2172042075194638080` onto the stack which is the oracle id prefix and suffix.
+4. Calls `tx::execute_foreign_procedure` which calls the `get_median` procedure via foreign procedure invocation.
+
+Inside of the `masm/accounts/` directory, create the `oracle_reader.masm` file:
+
+```masm
+use miden::protocol::tx
+
+# Fetches the current price from the `get_median`
+# procedure from the Pragma oracle
+# => []
+pub proc get_price
+ push.0.0.0.120195681
+ # => [PAIR]
+
+ # This is the procedure root of the `get_median` procedure
+ push.0xb86237a8c9cd35acfef457e47282cc4da43df676df410c988eab93095d8fb3b9
+ # => [GET_MEDIAN_HASH, PAIR]
+
+ push.939716883672832.2172042075194638080
+ # => [oracle_id_prefix, oracle_id_suffix, GET_MEDIAN_HASH, PAIR]
+
+ exec.tx::execute_foreign_procedure
+ # => [price]
+
+ debug.stack
+ # => [price]
+
+ dropw dropw
+end
+```
+
+**Note**: _It's a good habit to add comments above each line of MASM code with the expected stack state. This improves readability and helps with debugging._
+
+### Create the script which calls the `get_price` procedure
+
+This is a Miden assembly script that will call the `get_price` procedure during the transaction.
+
+Inside of the `masm/scripts/` directory, create the `oracle_reader_script.masm` file:
+
+```masm
+use external_contract::oracle_reader
+
+begin
+ exec.oracle_reader::get_price
+end
+```
+
+## Step 3: Run the program
+
+Run the following command to execute src/main.rs:
+
+```
+cargo run --release
+```
+
+The output of our program will look something like this:
+
+```
+cleared sqlite store: ./store.sqlite3
+Latest block: 648397
+Oracle accountId prefix: V0(AccountIdPrefixV0 { prefix: 5721796415433354752 }) suffix: 599064613630720
+Stack state before step 8766:
+├── 0: 82655190335
+├── 1: 0
+├── 2: 0
+├── 3: 0
+├── 4: 0
+├── 5: 0
+├── 6: 0
+├── 7: 0
+├── 8: 0
+├── 9: 0
+├── 10: 0
+├── 11: 0
+├── 12: 0
+├── 13: 0
+├── 14: 0
+├── 15: 0
+├── 16: 0
+├── 17: 0
+├── 18: 0
+└── 19: 0
+
+View transaction on MidenScan: https://testnet.midenscan.com/tx/0xc8951190564d5c3ac59fe99d8911f8c17f5b59ba542e2eb860413898902f3722
+```
+
+As you can see, at the top of the stack is the price returned from the Pragma oracle. The price is returned with 6 decimal places. Currently Pragma only publishes the `BTC/USD` price feed on testnet.
+
+### Running the tutorial
+
+To run this tutorial end-to-end, navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run:
+
+```bash
+cd rust-client
+cargo run --release --bin oracle_data_query --
+```
+
+where `` is Pragma's deployed oracle account ID on testnet.
+
+### Continue learning
+
+Next tutorial: [How to Use Unauthenticated Notes](./unauthenticated_note_how_to.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/public_account_interaction_tutorial.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/public_account_interaction_tutorial.md
new file mode 100644
index 00000000..1bdc4b1d
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/public_account_interaction_tutorial.md
@@ -0,0 +1,513 @@
+---
+title: "Interacting with Public Smart Contracts"
+sidebar_position: 5
+---
+
+# Interacting with Public Smart Contracts
+
+_Using the Miden client in Rust to interact with public smart contracts on Miden_
+
+## Overview
+
+In the previous tutorial, we built a simple counter contract and deployed it to the Miden testnet. However, we only covered how the contract’s deployer could interact with it. Now, let’s explore how anyone can interact with a public smart contract on Miden.
+
+We’ll retrieve the counter contract’s state from the chain and rebuild it locally so a local transaction can be executed against it. In the near future, Miden will support network transactions, making the process of submitting transactions to public smart contracts much more like traditional blockchains.
+
+Just like in the previous tutorial, we will use a script to invoke the increment function within the counter contract to update the count. However, this tutorial demonstrates how to call a procedure in a smart contract that was deployed by a different user on Miden.
+
+## What we'll cover
+
+- Reading state from a public smart contract
+- Interacting with public smart contracts on Miden
+
+## Prerequisites
+
+This tutorial assumes you have a basic understanding of Miden assembly and completed the previous tutorial on deploying the counter contract. Although not a requirement, it is recommended to complete the counter contract deployment tutorial before starting this tutorial.
+
+## Step 1: Initialize your repository
+
+Create a new Rust repository for your Miden project and navigate to it with the following command:
+
+```bash
+cargo new miden-counter-contract
+cd miden-counter-contract
+```
+
+Add the following dependencies to your `Cargo.toml` file:
+
+```toml
+[dependencies]
+miden-client = { version = "0.14", features = ["testing", "tonic"] }
+miden-client-sqlite-store = { version = "0.14", package = "miden-client-sqlite-store" }
+miden-protocol = { version = "0.14" }
+rand = { version = "0.9" }
+serde = { version = "1", features = ["derive"] }
+serde_json = { version = "1.0", features = ["raw_value"] }
+tokio = { version = "1.46", features = ["rt-multi-thread", "net", "macros", "fs"] }
+rand_chacha = "0.9.0"
+```
+
+## Step 2: Build the counter contract
+
+For better code organization, we will separate the Miden assembly code from our Rust code.
+
+Create a directory named `masm` at the **root** of your `miden-counter-contract` directory. This will contain our contract and script masm code.
+
+Initialize the `masm` directory:
+
+```bash
+mkdir -p masm/accounts masm/scripts
+```
+
+This will create:
+
+```text
+masm/
+├── accounts/
+└── scripts/
+```
+
+Inside of the `masm/accounts/` directory, create the `counter.masm` file:
+
+```masm
+use miden::protocol::active_account
+use miden::protocol::native_account
+use miden::core::word
+use miden::core::sys
+
+const COUNTER_SLOT = word("miden::tutorials::counter")
+
+#! Inputs: []
+#! Outputs: [count]
+pub proc get_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ # => [count]
+
+ exec.sys::truncate_stack
+ # => [count]
+end
+
+#! Inputs: []
+#! Outputs: []
+pub proc increment_count
+ push.COUNTER_SLOT[0..2] exec.active_account::get_item
+ # => [count]
+
+ add.1
+ # => [count+1]
+
+ push.COUNTER_SLOT[0..2] exec.native_account::set_item
+ # => []
+
+ exec.sys::truncate_stack
+ # => []
+end
+```
+
+Inside of the `masm/scripts/` directory, create the `counter_script.masm` file:
+
+```masm
+use external_contract::counter_contract
+
+begin
+ call.counter_contract::increment_count
+end
+```
+
+**Note**: _We explained in the previous counter contract tutorial what exactly happens at each step in the `increment_count` procedure._
+
+### Step 3: Set up your `src/main.rs` file
+
+Copy and paste the following code into your `src/main.rs` file:
+
+```rust no_run
+use miden_client::transaction::TransactionKernel;
+use std::{fs, path::Path, sync::Arc};
+
+use miden_client::{
+ account::AccountId,
+ assembly::{
+ Assembler,
+ DefaultSourceManager,
+ Module,
+ ModuleKind,
+ Path as AssemblyPath,
+ },
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+
+fn create_library(
+ assembler: Assembler,
+ library_path: &str,
+ source_code: &str,
+) -> Result, Box> {
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ source_code,
+ source_manager.clone(),
+ )?;
+ let library = assembler.clone().assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ Ok(())
+}
+```
+
+## Step 4: Reading public state from a smart contract
+
+To read the public storage state of a smart contract on Miden we either instantiate the `TonicRpcClient` by itself, or use the `test_rpc_api()` method on the `Client` instance. In this example, we will be using the `test_rpc_api()` method.
+
+We will be reading the public storage state of the counter contract deployed on the testnet at address `0x303dd027d27adc0000012b07dbf1b4`.
+
+Add the following code snippet to the end of your `src/main.rs` function:
+
+```rust ignore
+// -------------------------------------------------------------------------
+// STEP 1: Read the Public State of the Counter Contract
+// -------------------------------------------------------------------------
+println!("\n[STEP 1] Reading data from public state");
+
+// Define the Counter Contract account id from counter contract deploy
+let (_, counter_contract_id) =
+ AccountId::from_bech32("mtst1apsd609q5966cqra992t4a00tgstrkfk").unwrap();
+
+client
+ .import_account_by_id(counter_contract_id)
+ .await
+ .unwrap();
+
+let counter_contract = client
+ .get_account(counter_contract_id)
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+println!(
+ "Account details: {:?}",
+ counter_contract.storage().slots().first().unwrap()
+);
+```
+
+Run the following command to execute src/main.rs:
+
+```bash
+cargo run --release
+```
+
+After the program executes, you should see the counter contract count value and nonce printed to the terminal, for example:
+
+```text
+count val: [0, 0, 0, 5]
+counter nonce: 5
+```
+
+## Step 5: Importing a public account
+
+Add the following code snippet to the end of your `src/main.rs` function:
+
+```rust ignore
+// -------------------------------------------------------------------------
+// STEP 2: Call the Counter Contract with a script
+// -------------------------------------------------------------------------
+println!("\n[STEP 2] Call the increment_count procedure in the counter contract");
+
+// Load the MASM script referencing the increment procedure
+let script_path = Path::new("../masm/scripts/counter_script.masm");
+let script_code = fs::read_to_string(script_path).unwrap();
+
+let counter_path = Path::new("../masm/accounts/counter.masm");
+let counter_code = fs::read_to_string(counter_path).unwrap();
+
+let assembler = TransactionKernel::assembler();
+let account_component_lib = create_library(
+ assembler.clone(),
+ "external_contract::counter_contract",
+ &counter_code,
+)
+.unwrap();
+
+let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&account_component_lib)
+ .unwrap()
+ .compile_tx_script(&script_code)
+ .unwrap();
+
+// Build a transaction request with the custom script
+let tx_increment_request = TransactionRequestBuilder::new()
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+// Execute and submit the transaction
+let tx_id = client
+ .submit_new_transaction(counter_contract.id(), tx_increment_request)
+ .await
+ .unwrap();
+
+println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+);
+
+client.sync_state().await.unwrap();
+
+// Retrieve updated contract data to see the incremented counter
+let account = client
+ .get_account(counter_contract.id())
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+let counter_slot_name =
+ miden_client::account::StorageSlotName::new("miden::tutorials::counter")
+ .expect("valid slot name");
+println!(
+ "counter contract storage: {:?}",
+ account.storage().get_item(&counter_slot_name)
+);
+```
+
+## Summary
+
+The final `src/main.rs` file should look like this:
+
+```rust no_run
+use miden_client::transaction::TransactionKernel;
+use std::{fs, path::Path, sync::Arc};
+
+use miden_client::{
+ account::AccountId,
+ assembly::{
+ Assembler,
+ DefaultSourceManager,
+ Module,
+ ModuleKind,
+ Path as AssemblyPath,
+ },
+ builder::ClientBuilder,
+ keystore::FilesystemKeyStore,
+ rpc::{Endpoint, GrpcClient},
+ transaction::TransactionRequestBuilder,
+ ClientError,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+
+fn create_library(
+ assembler: Assembler,
+ library_path: &str,
+ source_code: &str,
+) -> Result, Box> {
+ let source_manager = Arc::new(DefaultSourceManager::default());
+ let module = Module::parser(ModuleKind::Library).parse_str(
+ AssemblyPath::new(library_path),
+ source_code,
+ source_manager.clone(),
+ )?;
+ let library = assembler.clone().assemble_library([module])?;
+ Ok(library)
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ // -------------------------------------------------------------------------
+ // STEP 1: Read the Public State of the Counter Contract
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 1] Reading data from public state");
+
+ // Define the Counter Contract account id from counter contract deploy
+ let (_, counter_contract_id) =
+ AccountId::from_bech32("mtst1apsd609q5966cqra992t4a00tgstrkfk").unwrap();
+
+ client
+ .import_account_by_id(counter_contract_id)
+ .await
+ .unwrap();
+
+ let counter_contract = client
+ .get_account(counter_contract_id)
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+ println!(
+ "Account details: {:?}",
+ counter_contract.storage().slots().first().unwrap()
+ );
+
+ // -------------------------------------------------------------------------
+ // STEP 2: Call the Counter Contract with a script
+ // -------------------------------------------------------------------------
+ println!("\n[STEP 2] Call the increment_count procedure in the counter contract");
+
+ // Load the MASM script referencing the increment procedure
+ let script_path = Path::new("../masm/scripts/counter_script.masm");
+ let script_code = fs::read_to_string(script_path).unwrap();
+
+ let counter_path = Path::new("../masm/accounts/counter.masm");
+ let counter_code = fs::read_to_string(counter_path).unwrap();
+
+ let assembler = TransactionKernel::assembler();
+ let account_component_lib = create_library(
+ assembler.clone(),
+ "external_contract::counter_contract",
+ &counter_code,
+ )
+ .unwrap();
+
+ let tx_script = client
+ .code_builder()
+ .with_dynamically_linked_library(&account_component_lib)
+ .unwrap()
+ .compile_tx_script(&script_code)
+ .unwrap();
+
+ // Build a transaction request with the custom script
+ let tx_increment_request = TransactionRequestBuilder::new()
+ .custom_script(tx_script)
+ .build()
+ .unwrap();
+
+ // Execute and submit the transaction
+ let tx_id = client
+ .submit_new_transaction(counter_contract.id(), tx_increment_request)
+ .await
+ .unwrap();
+
+ println!(
+ "View transaction on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+
+ client.sync_state().await.unwrap();
+
+ // Retrieve updated contract data to see the incremented counter
+ let account = client
+ .get_account(counter_contract.id())
+ .await
+ .unwrap()
+ .expect("counter contract not found");
+ let counter_slot_name =
+ miden_client::account::StorageSlotName::new("miden::tutorials::counter")
+ .expect("valid slot name");
+ println!(
+ "counter contract storage: {:?}",
+ account.storage().get_item(&counter_slot_name)
+ );
+ Ok(())
+}
+```
+
+Run the following command to execute src/main.rs:
+
+```bash
+cargo run --release
+```
+
+The output of our program will look something like this depending on the current count value in the smart contract:
+
+```text
+Client initialized successfully.
+Latest block: 242342
+
+[STEP 1] Building counter contract from public state
+count val: [0, 0, 0, 1]
+counter nonce: 1
+
+[STEP 2] Call the increment_count procedure in the counter contract
+Procedure 1: "0x92495ca54d519eb5e4ba22350f837904d3895e48d74d8079450f19574bb84cb6"
+Procedure 2: "0xecd7eb223a5524af0cc78580d96357b298bb0b3d33fe95aeb175d6dab9de2e54"
+number of procedures: 2
+Final script:
+begin
+ # => []
+ call.0xecd7eb223a5524af0cc78580d96357b298bb0b3d33fe95aeb175d6dab9de2e54
+end
+Stack state before step 1812:
+├── 0: 2
+├── 1: 0
+├── 2: 0
+├── 3: 0
+├── 4: 0
+├── 5: 0
+├── 6: 0
+├── 7: 0
+├── 8: 0
+├── 9: 0
+├── 10: 0
+├── 11: 0
+├── 12: 0
+├── 13: 0
+├── 14: 0
+├── 15: 0
+├── 16: 0
+├── 17: 0
+├── 18: 0
+└── 19: 0
+
+View transaction on MidenScan: https://testnet.midenscan.com/tx/0x8183aed150f20b9c26d4cb7840bfc92571ea45ece31116170b11cdff2649eb5c
+counter contract storage: Ok(RpoDigest([0, 0, 0, 2]))
+```
+
+### Running the example
+
+To run the full example, navigate to the `rust-client` directory in the [miden-tutorials](https://github.com/0xMiden/miden-tutorials/) repository and run this command:
+
+```bash
+cd rust-client
+cargo run --release --bin counter_contract_increment
+```
+
+### Continue learning
+
+Next tutorial: [Network Transactions on Miden](network_transactions_tutorial.md)
diff --git a/versioned_docs/version-0.14/builder/tutorials/recipes/rust/unauthenticated_note_how_to.md b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/unauthenticated_note_how_to.md
new file mode 100644
index 00000000..d9e3560c
--- /dev/null
+++ b/versioned_docs/version-0.14/builder/tutorials/recipes/rust/unauthenticated_note_how_to.md
@@ -0,0 +1,464 @@
+---
+title: "How to Use Unauthenticated Notes"
+sidebar_position: 9
+---
+
+# How to Use Unauthenticated Notes
+
+_Using unauthenticated notes for optimistic note consumption_
+
+## Overview
+
+In this guide, we will explore how to leverage unauthenticated notes on Miden to settle transactions faster than the blocktime. Unauthenticated notes are essentially UTXOs that have not yet been fully committed into a block. This feature allows the notes to be created and consumed within the same block.
+
+We construct a chain of transactions using the unauthenticated notes method on the transaction builder. Unauthenticated notes are also referred to as "unauthenticated notes" or "erasable notes". We also demonstrate how a note can be serialized and deserialized, highlighting the ability to transfer notes between client instances for asset transfers that can be settled between parties faster than the blocktime.
+
+For example, our demo creates a chain of unauthenticated note transactions:
+
+```markdown
+Alice ➡ Bob ➡ Charlie ➡ Dave ➡ Eve ➡ Frank ➡ ...
+```
+
+## What we'll cover
+
+- **Introduction to Unauthenticated Notes:** Understand what unauthenticated notes are and how they differ from standard notes.
+- **Serialization Example:** See how to serialize and deserialize a note to demonstrate how notes can be propagated to client instances faster than the blocktime.
+- **Performance Insights:** Observe how unauthenticated notes can reduce transaction times dramatically.
+
+## Step-by-step process
+
+1. **Client Initialization:**
+ - Set up an RPC client to connect with the Miden testnet.
+ - Initialize a random coin generator and a store for persisting account data.
+
+2. **Deploying a Fungible Faucet:**
+ - Use a random seed to deploy a fungible faucet.
+ - Configure the faucet parameters (symbol, decimals, and max supply) and add it to the client.
+
+3. **Creating Wallet Accounts:**
+ - Build multiple wallet accounts using a secure key generation process.
+ - Add these accounts to the client, making them ready for transactions.
+
+4. **Minting and Transacting with Unauthenticated Notes:**
+ - Mint tokens for one of the accounts (Alice) from the deployed faucet.
+ - Create a note representing the minted tokens.
+ - Build and submit a transaction that uses the unauthenticated note via the "unauthenticated" method.
+ - Serialize the note to demonstrate how it could be transferred to another client instance.
+ - Consume the note in a subsequent transaction, effectively creating a chain of unauthenticated transactions.
+
+5. **Performance Timing and Syncing:**
+ - Measure the time taken for each transaction iteration.
+ - Sync the client state and print account balances to verify the transactions.
+
+## Full Rust code example
+
+```rust no_run
+use miden_client::auth::{AuthSchemeId, AuthSingleSig};
+use rand::RngCore;
+use std::sync::Arc;
+use tokio::time::{sleep, Duration, Instant};
+
+use miden_client::{
+ account::component::{AuthControlled, BasicFungibleFaucet, BasicWallet},
+ address::NetworkId,
+ asset::{FungibleAsset, TokenSymbol},
+ auth::AuthSecretKey,
+ builder::ClientBuilder,
+ keystore::{FilesystemKeyStore, Keystore},
+ note::{Note, NoteAttachment, NoteType, P2idNote},
+ rpc::{Endpoint, GrpcClient},
+ store::TransactionFilter,
+ transaction::{TransactionId, TransactionRequestBuilder, TransactionStatus},
+ utils::{Deserializable, Serializable},
+ Client, ClientError, Felt,
+};
+use miden_client_sqlite_store::ClientBuilderSqliteExt;
+use miden_client::account::{AccountBuilder, AccountStorageMode, AccountType};
+
+/// Waits for a specific transaction to be committed.
+async fn wait_for_tx(
+ client: &mut Client,
+ tx_id: TransactionId,
+) -> Result<(), ClientError> {
+ loop {
+ client.sync_state().await?;
+
+ // Check transaction status
+ let txs = client
+ .get_transactions(TransactionFilter::Ids(vec![tx_id]))
+ .await?;
+ let tx_committed = if !txs.is_empty() {
+ matches!(txs[0].status, TransactionStatus::Committed { .. })
+ } else {
+ false
+ };
+
+ if tx_committed {
+ println!("✅ transaction {} committed", tx_id.to_hex());
+ break;
+ }
+
+ println!(
+ "Transaction {} not yet committed. Waiting...",
+ tx_id.to_hex()
+ );
+ sleep(Duration::from_secs(2)).await;
+ }
+ Ok(())
+}
+
+#[tokio::main]
+async fn main() -> Result<(), ClientError> {
+ // Initialize client
+ let endpoint = Endpoint::testnet();
+ let timeout_ms = 10_000;
+ let rpc_client = Arc::new(GrpcClient::new(&endpoint, timeout_ms));
+
+ // Initialize keystore
+ let keystore_path = std::path::PathBuf::from("./keystore");
+ let keystore = Arc::new(FilesystemKeyStore::new(keystore_path).unwrap());
+
+ let store_path = std::path::PathBuf::from("./store.sqlite3");
+
+ let mut client = ClientBuilder::new()
+ .rpc(rpc_client)
+ .sqlite_store(store_path)
+ .authenticator(keystore.clone())
+ .in_debug_mode(true.into())
+ .build()
+ .await?;
+
+ let sync_summary = client.sync_state().await.unwrap();
+ println!("Latest block: {}", sync_summary.block_num);
+
+ //------------------------------------------------------------
+ // STEP 1: Deploy a fungible faucet
+ //------------------------------------------------------------
+ println!("\n[STEP 1] Deploying a new fungible faucet.");
+
+ // Faucet seed
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ // Generate key pair
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ // Faucet parameters
+ let symbol = TokenSymbol::new("MID").unwrap();
+ let decimals = 8;
+ let max_supply = Felt::new(1_000_000);
+
+ // Build the account
+ let faucet_account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::FungibleFaucet)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicFungibleFaucet::new(symbol, decimals, max_supply).unwrap())
+ .with_component(AuthControlled::allow_all())
+ .build()
+ .unwrap();
+
+ // Add the faucet to the client
+ client.add_account(&faucet_account, false).await?;
+
+ println!(
+ "Faucet account ID: {}",
+ faucet_account.id().to_bech32(NetworkId::Testnet)
+ );
+
+ // Add the key pair to the keystore
+ keystore.add_key(&key_pair, faucet_account.id()).await.unwrap();
+
+ // Resync to show newly deployed faucet
+ tokio::time::sleep(Duration::from_secs(2)).await;
+ client.sync_state().await?;
+
+ //------------------------------------------------------------
+ // STEP 2: Create basic wallet accounts
+ //------------------------------------------------------------
+ println!("\n[STEP 2] Creating new accounts");
+
+ let mut accounts = vec![];
+ let number_of_accounts = 2;
+
+ for i in 0..number_of_accounts {
+ let mut init_seed = [0_u8; 32];
+ client.rng().fill_bytes(&mut init_seed);
+
+ let key_pair = AuthSecretKey::new_falcon512_poseidon2_with_rng(client.rng());
+
+ let account = AccountBuilder::new(init_seed)
+ .account_type(AccountType::RegularAccountUpdatableCode)
+ .storage_mode(AccountStorageMode::Public)
+ .with_auth_component(AuthSingleSig::new(key_pair.public_key().to_commitment(), AuthSchemeId::Falcon512Poseidon2))
+ .with_component(BasicWallet)
+ .build()
+ .unwrap();
+
+ accounts.push(account.clone());
+ println!(
+ "account id {:?}: {}",
+ i,
+ account.id().to_bech32(NetworkId::Testnet)
+ );
+ client.add_account(&account, true).await?;
+
+ // Add the key pair to the keystore
+ keystore.add_key(&key_pair, account.id()).await.unwrap();
+ }
+
+ // For demo purposes, Alice is the first account.
+ let alice = &accounts[0];
+
+ //------------------------------------------------------------
+ // STEP 3: Mint and consume tokens for Alice
+ //------------------------------------------------------------
+ println!("\n[STEP 3] Mint tokens");
+ println!("Minting tokens for Alice...");
+ let amount: u64 = 100;
+ let fungible_asset_mint_amount = FungibleAsset::new(faucet_account.id(), amount).unwrap();
+ let transaction_request = TransactionRequestBuilder::new()
+ .build_mint_fungible_asset(
+ fungible_asset_mint_amount,
+ alice.id(),
+ NoteType::Public,
+ client.rng(),
+ )
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(faucet_account.id(), transaction_request)
+ .await?;
+ println!("Minted tokens. TX: {:?}", tx_id);
+
+ // Wait for mint transaction to be committed
+ wait_for_tx(&mut client, tx_id).await?;
+
+ // Get the minted note and consume it
+ let consumable_notes = client.get_consumable_notes(Some(alice.id())).await?;
+
+ if let Some((note_record, _)) = consumable_notes.first() {
+ let note: Note = note_record.clone().try_into()?;
+ let transaction_request = TransactionRequestBuilder::new()
+ .build_consume_notes(vec![note])
+ .unwrap();
+
+ let consume_tx_id = client
+ .submit_new_transaction(alice.id(), transaction_request)
+ .await?;
+ println!("Consumed minted note. TX: {:?}", consume_tx_id);
+
+ // Wait for consumption to complete
+ wait_for_tx(&mut client, consume_tx_id).await?;
+ }
+
+ //------------------------------------------------------------
+ // STEP 4: Create unauthenticated note tx chain
+ //------------------------------------------------------------
+ println!("\n[STEP 4] Create unauthenticated note tx chain");
+ let start = Instant::now();
+
+ for i in 0..number_of_accounts - 1 {
+ let loop_start = Instant::now();
+ println!("\nunauthenticated tx {:?}", i + 1);
+ println!("sender: {}", accounts[i].id().to_bech32(NetworkId::Testnet));
+ println!(
+ "target: {}",
+ accounts[i + 1].id().to_bech32(NetworkId::Testnet)
+ );
+
+ // Time the creation of the p2id note
+ let send_amount = 20;
+ let fungible_asset_send_amount =
+ FungibleAsset::new(faucet_account.id(), send_amount).unwrap();
+
+ // for demo purposes, unauthenticated notes can be public or private
+ let note_type = if i % 2 == 0 {
+ NoteType::Private
+ } else {
+ NoteType::Public
+ };
+
+ let p2id_note = P2idNote::create(
+ accounts[i].id(),
+ accounts[i + 1].id(),
+ vec![fungible_asset_send_amount.into()],
+ note_type,
+ NoteAttachment::default(),
+ client.rng(),
+ )
+ .unwrap();
+
+ // Time transaction request building
+ let transaction_request = TransactionRequestBuilder::new()
+ .own_output_notes(vec![p2id_note.clone()])
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(accounts[i].id(), transaction_request)
+ .await?;
+ println!("Created note. TX: {:?}", tx_id);
+
+ // Note serialization/deserialization
+ // This demonstrates how you could send the serialized note to another client instance
+ let serialized = p2id_note.to_bytes();
+ let deserialized_p2id_note = Note::read_from_bytes(&serialized).unwrap();
+
+ // Time consume note request building
+ let consume_note_request = TransactionRequestBuilder::new()
+ .input_notes([(deserialized_p2id_note, None)])
+ .build()
+ .unwrap();
+
+ let tx_id = client
+ .submit_new_transaction(accounts[i + 1].id(), consume_note_request)
+ .await?;
+
+ println!(
+ "Consumed Note Tx on MidenScan: https://testnet.midenscan.com/tx/{:?}",
+ tx_id
+ );
+ println!(
+ "Total time for loop iteration {}: {:?}",
+ i,
+ loop_start.elapsed()
+ );
+ }
+
+ println!(
+ "\nTotal execution time for unauthenticated note txs: {:?}",
+ start.elapsed()
+ );
+
+ // Final resync and display account balances
+ tokio::time::sleep(Duration::from_secs(3)).await;
+ client.sync_state().await?;
+ for account in accounts.clone() {
+ let new_account = client.get_account(account.id()).await.unwrap().expect("account not found");
+ let balance = new_account.vault().get_balance(faucet_account.id()).unwrap();
+ println!(
+ "Account: {} balance: {}",
+ account.id().to_bech32(NetworkId::Testnet),
+ balance
+ );
+ }
+
+ Ok(())
+}
+```
+
+The output of our program will look something like this:
+
+```text
+Latest block: 227040
+
+[STEP 1] Deploying a new fungible faucet.
+Faucet account ID:
+
+[STEP 2] Creating new accounts
+account id 0:
+account id 1:
+account id 2:
+account id 3:
+account id 4:
+account id 5:
+account id 6:
+account id 7:
+account id 8:
+account id 9: