---
# System prepended metadata

title: Miden Testnet 0.13.0

---

# Miden Testnet 0.13.0

This guide covers all breaking changes you need to migrate an application to Miden 0.13.0. 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 or web client
- interacting with storage, auth, or RPCs

this document is for you.

## At a Glance
Big themes in 0.13:

- Storage is now name-based, not index-based
- Notes use attachments, not overloaded tags. Network-account notes use a standardized attachment
- Input notes are handled as full notes, not IDs
- Authentication APIs are unified and explicit

If you only skim one section, skim Account, Notes, and Transactions.

## Table of Contents
- [Imports](#imports-and-dependencies)
    - [Crate and Dependency Renames](#crate-and-dependency-renames)
    - [Core library rename (StdLibrary → CoreLibrary)](#core-library-rename-stdlibrarry--corelibrary)
- [Account Changes](#accound-changes)
    - [Named Storage Slots](#named-storage-slots)
    - [MASM set_map_item change](#masm-set-map-item-change)
    - [(Rust) No generic filesystem keystore](#rust-no-generic-filesystem-keystore)
    - [(WebClient) Account component compilation requires AccountComponentCode](#webclient-account-component-compilation-requires-accountcomponentcode)
    - [Account Component Templates Replaced by Storage Schemas](#account-component-templates-replaced-by-storage-schemas)
    - [Account Procedure Roots and Index Maps](#account-procedure-roots-and-index-maps)
    - [(WebClient) Authentication and key management updates](#webclient-authentication-and-key-management-updates)
    - [DSA signature encoding helpers](#dsa-signature-encoding-helpers)

- [Note Changes](#note-changes)
    - [Note Metadata Attachments and Tag Semantics](#note-metadata-attachments-and-tag-semantics)
    - [MINT Note Inputs and MAX_INPUTS_PER_NOTE](#mint-note-inputs-and-max-inputs-per-note)
    - [Input notes API unified](#input-notes-api-unified)
    - [FetchedNote and RPC note shapes refactored](#fetchednote-and-rpc-note-shapes-refactored)
    - [NoteScreener relevance replaced by NoteConsumptionStatus](#notescreener-relevance-replaced-by-noteconsumptionstatus)
- [Transaction Changes](#transaction-changes)
    - [Transaction Events: IDs vs Data Extraction](#transaction-events-ids-vs-data-extraction)
- [Client Changes](#transaction-changes)
    - [(Rust) NodeRpcClient account proof API changed](#rust-noderpcclient-account-proof-api-changed)
    - [(Rust) Client RNG must be Send and Sync](#rust-client-rng-must-be-send-and-sync)
    - [(Rust)CLI swap payback_note_type removed](#rust-cli-swap-payback_note_type-removed)
    - [(WebClient) IndexedDB naming for multiple instances](#webclient-indexeddb-naming-for-multiple-instances)
    - [(WebClient) Block numbers are numeric in web APIs and IndexedDB](#block-numbers-are-numeric-in-web-apis-and-indexeddb)
    - [(WebClient) NetworkId custom networks and toBech32Custom removal](#networkid-custom-networks-and-tobech32custom-removal)
- [General MASM Changes](#general-masm-changes)
    - [MASM syntax updates](#masm-syntax-updates)
    - [Falcon512Rpo Renames](#falcon512rpo-renames)
    - [ECDSA precompile procedure renames](#ecdsa-precompile-procedure-renames)
    - [RPO hash helper renames](#rpo-hash-helper-renames)
- [Assembler Changes](#general-masm-changes)
    - [LibraryPath removal (Path-only APIs)](#librarypath-removal-path-only-apis)
    - [Assembler API updates](#assembler-api-updates)


## Imports and Dependencies

### Crate and Dependency Renames
**PRs:** #2184, #2191, #2197, #2255, #2158

#### Summary
The core types moved to `miden-protocol` and standards/scripts to `miden-standards`. Miden VM dependencies are now 0.20 and `miden-crypto` is 0.19, and error types are no longer re-exported at the crate root.

#### Affected Code
**Cargo.toml:**
```diff
- miden-objects = "0.12"
- miden-lib = "0.12"
- miden-assembly = "0.19"
- miden-core = "0.19"
- miden-crypto = "0.18.2"
+ miden-protocol = "0.13"
+ miden-standards = "0.13"
+ miden-assembly = "0.20"
+ miden-core = "0.20"
+ miden-crypto = "0.19"
```

**Rust:**
```rust
// Before (0.12.4)
use miden_objects::account::AccountId;
use miden_objects::AccountError;
use miden_lib::note::create_p2id_note;

// After (0.13.0)
use miden_protocol::account::AccountId;
use miden_protocol::errors::AccountError;
use miden_standards::note::create_p2id_note;
```

#### Migration Steps
1. Replace `miden-objects` with `miden-protocol` and `miden-lib` with `miden-standards` in `Cargo.toml` and imports.
2. Bump direct Miden VM dependencies to 0.20 and `miden-crypto` to 0.19 if you depend on them directly.
3. Update error imports to `miden_protocol::errors::*`.
4. Run `cargo check` to catch any remaining import path mismatches.

#### Common Errors
| Error Message | Cause | Solution |
|--------------|-------|----------|
| `unresolved import miden_objects` | Crate renamed | Replace with `miden_protocol` or `miden_standards` and update `Cargo.toml`. |
| `unresolved import miden_protocol::AccountError` | Errors are no longer re-exported | Import from `miden_protocol::errors::AccountError`. |
| `failed to select a version for miden-assembly` | Direct dependency still pinned to 0.19 | Update `miden-assembly` (and other Miden VM crates) to 0.20. |

### Core library rename (StdLibrary → CoreLibrary)
**PR:** #2260

#### Summary
The MASM standard library was renamed to **miden::core**, and the Rust wrapper moved from
`StdLibrary` (miden-stdlib) to `CoreLibrary` (miden-core-lib).

#### Affected Code

**Rust:**
```diff
- use miden_stdlib::StdLibrary;
+ use miden_core_lib::CoreLibrary;
```

**MASM:**
```diff
- use std::crypto::hashes::rpo
+ use miden::core::crypto::hashes::rpo256
```

#### Migration Steps
1. Replace `StdLibrary` with `CoreLibrary` and update dependencies to `miden-core-lib`.
2. Replace MASM imports from `std::` to `miden::core::`.

#### Common Errors
| Error Message | Cause | Solution |
| --- | --- | --- |
| `error[E0432]: unresolved import miden_stdlib::StdLibrary` | Crate and type renamed. | Use `miden_core_lib::CoreLibrary`. |
| `unknown module std::...` | MASM namespace renamed. | Use `miden::core::...`. |

## Account Changes

### Named Storage Slots
**PRs:** #1987, #2025, #2149, #2150, #2153, #2154, #2160, #2161, #2170, #2182, #2194

#### Summary
Storage slots are now identified by `StorageSlotName` instead of numeric indices. Also `AccountStorage` and `AccountStorageDelta` APIs are name-based.

#### Affected Code
**Rust Client:**
```rust
// Before (0.12.x)
use miden_client::account::{StorageMap, StorageSlot};

fn storage_slots(storage_map: StorageMap) -> Vec<StorageSlot> {
    vec![StorageSlot::Map(storage_map)]
}
```

```rust
// After (0.13.0)
use miden_client::account::{StorageMap, StorageSlot, StorageSlotName};

fn storage_slots(storage_map: StorageMap) -> Vec<StorageSlot> {
    let slot_name = StorageSlotName::new("miden::example::map")
        .expect("slot name must be valid");
    vec![StorageSlot::with_map(slot_name, storage_map)]
}
```

**TypeScript Client:**
```typescript
// Before (0.12.x)
import {
  AccountComponent,
  StorageMap,
  StorageSlot,
  WebClient,
} from "@miden-sdk/miden-sdk";

const client = await WebClient.createClient();
const builder = client.createCodeBuilder();
const storageMap = new StorageMap();
const slot = StorageSlot.map(storageMap);

const component = AccountComponent.compile(accountCode, builder, [slot]);
const value = account.storage().getMapItem(1, key);
```

```typescript
// After (0.13.0)
import {
  AccountComponent,
  StorageMap,
  StorageSlot,
  WebClient,
} from "@miden-sdk/miden-sdk";

const client = await WebClient.createClient();
const builder = client.createCodeBuilder();
const storageMap = new StorageMap();
const slotName = "miden::example::map";
const slot = StorageSlot.map(slotName, storageMap);

const componentCode = builder.compileAccountComponentCode(accountCode);
const component = AccountComponent.compile(componentCode, [slot]);

const value = account.storage().getMapItem(slotName, key);
const slotNames = account.storage().getSlotNames();
```

**Rust (storage access):**
```rust
// Before (0.12.4)
use miden_objects::account::{AccountStorage, StorageMap, StorageSlot};
use miden_objects::Word;

let mut storage = AccountStorage::new(vec![
    StorageSlot::Value(Word::from([1, 2, 3, 4u32])),
    StorageSlot::Map(StorageMap::new()),
])?;
let value = storage.get_item(0)?;
storage.set_map_item(1, key, value)?;

// After (0.13.0)
use miden_protocol::account::{AccountStorage, StorageMap, StorageSlot, StorageSlotName};
use miden_protocol::Word;

let value_slot = StorageSlotName::new("demo::value")?;
let map_slot = StorageSlotName::new("demo::map")?;

let mut storage = AccountStorage::new(vec![
    StorageSlot::with_value(value_slot.clone(), Word::from([1, 2, 3, 4u32])),
    StorageSlot::with_map(map_slot.clone(), StorageMap::new()),
])?;
let value = storage.get_item(&value_slot)?;
storage.set_map_item(&map_slot, key, value)?;
```

**Rust (storage deltas):**
```rust
// Before (0.12.4)
use miden_objects::account::AccountStorageDelta;

let mut delta = AccountStorageDelta::new();
delta.set_item(0, new_value);
delta.set_map_item(1, key, value);

// After (0.13.0)
use miden_protocol::account::{AccountStorageDelta, StorageSlotName};

let mut delta = AccountStorageDelta::new();
let value_slot = StorageSlotName::new("demo::value")?;
let map_slot = StorageSlotName::new("demo::map")?;
delta.set_item(value_slot.clone(), new_value)?;
delta.set_map_item(map_slot.clone(), key, value)?;
```

#### Migration Steps
1. Define storage slots with `StorageSlotName::new` and build `StorageSlot` values with `with_value` or `with_map`.
2. Replace index-based slots with named slots (`StorageSlotName` in Rust, string names in Web).
3. Update `AccountStorageDelta` usage to pass `StorageSlotName`.
4. Update any storage accessors (`getItem`, `getMapItem`, `getMapEntries`) to pass slot names.


#### Common Errors
| Error Message | Cause | Solution |
|--------------|-------|----------|
| `expected &StorageSlotName, found u8` | APIs now require slot names | Replace numeric indices with `StorageSlotName` values. |
| `StorageSlotNameNotFound` | Slot name does not exist in storage | Ensure you build storage with the same `StorageSlotName` used at access time. |
| `expected StorageSlotName` | Creating slots without names | Use `StorageSlotName::new("namespace::slot")` |



### MASM set-map-item change

#### Summary
`native_account::set_map_item` now takes slot IDs and returns only the old value.

#### Affected code

**MASM (kernel call):**
```text
# Before (0.12.4)
# Inputs: [index, KEY, VALUE]
exec.native_account::set_map_item
# => [OLD_MAP_ROOT, OLD_MAP_VALUE]

# After (0.13.0)
# Inputs: [slot_id_prefix, slot_id_suffix, KEY, VALUE]
exec.native_account::set_map_item
# => [OLD_VALUE]
```

#### Migration Steps
1. Handle `Result` from `set_item` and `set_map_item`.
2. If you call `native_account::set_map_item`, pass slot ID prefix/suffix and remove any logic that expects the old map root on the stack.

#### Common Errors
| `stack underflow` in MASM around `set_map_item` | Code still expects `OLD_MAP_ROOT` | Update stack handling to only consume `OLD_VALUE`. |

### (Rust) No generic filesystem keystore 

#### Summary
The filesystem keystore is also no longer generic over RNG.

```rust
// Before (0.12.x)
use miden_client::builder::ClientBuilder;
use miden_client::keystore::FilesystemKeyStore;

let builder = ClientBuilder::<FilesystemKeyStore<_>>::new();
```

```rust
// After (0.13.0)
use miden_client::builder::ClientBuilder;
use miden_client::keystore::FilesystemKeyStore;

let builder = ClientBuilder::<FilesystemKeyStore>::new();
```

#### Migration Steps
1. Update account component compilation to use `CodeBuilder.compileAccountComponentCode` and pass the resulting `AccountComponentCode`.
2. If you used `FilesystemKeyStore<_>` generics, drop the RNG parameter.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `type annotations needed for FilesystemKeyStore` | Removed RNG generic | Use `FilesystemKeyStore` without type params |

### (WebClient) Account component compilation requires AccountComponentCode

#### Summary
The web binding for `AccountComponent.compile` now requires a compiled `AccountComponentCode`.

**TypeScript:**
```typescript
// Before (0.12.x)
import {
  AccountComponent,
  StorageMap,
  StorageSlot,
  WebClient,
} from "@miden-sdk/miden-sdk";

const client = await WebClient.createClient();
const builder = client.createCodeBuilder();
const storageMap = new StorageMap();
const slot = StorageSlot.map(storageMap);

const component = AccountComponent.compile(accountCode, builder, [slot]);
const value = account.storage().getMapItem(1, key);
```

```typescript
// After (0.13.0)
import {
  AccountComponent,
  StorageMap,
  StorageSlot,
  WebClient,
} from "@miden-sdk/miden-sdk";

const client = await WebClient.createClient();
const builder = client.createCodeBuilder();
const storageMap = new StorageMap();
const slotName = "miden::example::map";
const slot = StorageSlot.map(slotName, storageMap);

const componentCode = builder.compileAccountComponentCode(accountCode);
const component = AccountComponent.compile(componentCode, [slot]);

const value = account.storage().getMapItem(slotName, key);
const slotNames = account.storage().getSlotNames();
```

#### Migration Steps
1. Update account component compilation to use `CodeBuilder.compileAccountComponentCode` and pass the resulting `AccountComponentCode`.

#### Common Errors
| `AccountComponent.compile takes 2 arguments` | Old binding passed `CodeBuilder` directly | Compile to `AccountComponentCode` first |

### Account Component Templates Replaced by Storage Schemas
**PRs:** #2127, #2193, #2230

#### Summary
`AccountComponentTemplate` is removed in favor of metadata-driven component creation via `StorageSchema`. `InitStorageData` now supports native types and map entries keyed by `StorageSlotName`.

#### Affected Code
**Rust (template removal):**
```rust
// Before (0.12.4)
use miden_objects::account::{AccountComponent, AccountComponentTemplate, InitStorageData};
use miden_objects::vm::Package;

let template = AccountComponentTemplate::try_from(package.clone())?;
let component = AccountComponent::from_template(&template, &init)?;

// After (0.13.0)
use miden_protocol::account::{AccountComponent, component::InitStorageData};
use miden_protocol::vm::Package;

let init = InitStorageData::default();
let component = AccountComponent::from_package(&package, &init)?;
```

**Rust (storage schema types):**
```rust
use miden_protocol::account::StorageSlotName;
use miden_protocol::account::component::{
    FeltSchema,
    SchemaTypeId,
    StorageSchema,
    StorageSlotSchema,
    ValueSlotSchema,
    WordSchema,
};

let slot_name = StorageSlotName::new("demo::value")?;
let schema = StorageSchema::new([(
    slot_name,
    StorageSlotSchema::Value(ValueSlotSchema::new(
        None,
        WordSchema::new_value([
            FeltSchema::new_void(),
            FeltSchema::new_void(),
            FeltSchema::new_void(),
            FeltSchema::new_typed(SchemaTypeId::native_felt(), "amount"),
        ]),
    )),
)])?;
```

**Rust (storage schema and init data):**
```rust
// Before (0.12.4)
use miden_objects::account::{InitStorageData, StorageValueName};

let init = InitStorageData::new(
    [(StorageValueName::new("demo::value")?, "300".to_string())],
    [],
);

// After (0.13.0)
use miden_protocol::account::StorageSlotName;
use miden_protocol::account::component::{InitStorageData, StorageValueName};
use miden_protocol::Felt;

let mut init = InitStorageData::default();
let slot_name = StorageSlotName::new("demo::value")?;
let value_name = StorageValueName::from_slot_name(&slot_name);
init.set_value(value_name, Felt::new(300))?;
```

#### Migration Steps
1. Replace `AccountComponentTemplate` usage with `AccountComponent::from_package` or `AccountComponent::from_library`.
2. Define storage layouts with `StorageSchema` and related schema types (`StorageSlotSchema`, `ValueSlotSchema`, `WordSchema`).
3. Update `InitStorageData` to use native types (e.g., `Felt`, `Word`) and map entries keyed by `StorageSlotName`.
4. Handle new validation errors from `InitStorageData::new` and `InitStorageData::set_value`.

#### Common Errors
| Error Message | Cause | Solution |
|--------------|-------|----------|
| `no method named from_template` | `AccountComponentTemplate` removed | Use `AccountComponent::from_package` or `from_library`. |
| `expected BTreeMap<StorageSlotName, ...>` | Map init entries now keyed by slot name | Use `StorageSlotName` as the map key. |
| `ConflictingEntries` from `InitStorageData` | Mixing slot-level values and field values | Pick either a full slot value or field-level values for a slot. |

### Account Procedure Roots and Index Maps
**PRs:** #2162, #2163, #2164, #2166

#### Summary
`AccountProcedureInfo` is replaced by `AccountProcedureRoot` with no storage offset/size. `AccountProcedureIndexMap::new` is infallible, and duplicate procedure roots across components are now de-duplicated instead of erroring. ACL components rename `tracked_procedure_roots_slot` to `trigger_procedure_roots_slot`.

#### Affected Code
**Rust (procedure roots):**
```rust
// Before (0.12.4)
use miden_objects::account::AccountProcedureInfo;

let info = AccountProcedureInfo::new(mast_root, 0, 3)?;
let root = *info.mast_root();

// After (0.13.0)
use miden_protocol::account::AccountProcedureRoot;

let root = AccountProcedureRoot::from_raw(mast_root);
let root_word: miden_protocol::Word = root.into();
```

**Rust (procedure index map):**
```rust
// Before (0.12.4)
let index_map = AccountProcedureIndexMap::new([account.code()])?;
let proc_idx = index_map.get_proc_index(process)?;

// After (0.13.0)
let index_map = AccountProcedureIndexMap::new([account.code()]);
let proc_idx = index_map.get_proc_index(code_commitment, procedure_root)?;
```

**Rust (ACL slot rename):**
```rust
// Before (0.13.0 pre-change)
AuthEcdsaK256KeccakAcl::tracked_procedure_roots_slot();

// After (0.13.0)
AuthEcdsaK256KeccakAcl::trigger_procedure_roots_slot();
```

#### Migration Steps
1. Replace `AccountProcedureInfo` with `AccountProcedureRoot` and remove storage offset/size logic.
2. Update `AccountProcedureIndexMap::new` call sites to remove error handling and pass the new inputs to `get_proc_index`.
3. If you relied on duplicate procedure roots to raise `AccountComponentDuplicateProcedureRoot`, add your own validation before composing components.
4. Update ACL helper method name to `trigger_procedure_roots_slot`.

#### Common Errors
| Error Message | Cause | Solution |
|--------------|-------|----------|
| `use of undeclared type AccountProcedureInfo` | Type removed | Use `AccountProcedureRoot` and update code accordingly. |
| `this function takes 2 arguments but 1 argument was supplied` | `get_proc_index` signature changed | Pass `code_commitment` and `procedure_root`. |
| `no method named tracked_procedure_roots_slot` | Method renamed | Use `trigger_procedure_roots_slot`. |

### (WebClient) Authentication and key management updates
**PRs:** #1546, #1578, #1592, #1608

#### Summary
WebClient auth APIs now take the `AuthScheme` enum instead of numeric IDs, `SecretKey` has been removed in favor of `AuthSecretKey`, and `addAccountSecretKeyToWebStore` now requires an account ID. Scheme-specific public key methods on `AuthSecretKey` were removed; use `getPublicKeyAsWord` instead.

#### Affected Code

**TypeScript:**
```typescript
// Before (0.12.x)
import {
  AccountComponent,
  AccountStorageMode,
  SecretKey,
  WebClient,
} from "@miden-sdk/miden-sdk";

const client = await WebClient.createClient();
const wallet = await client.newWallet(AccountStorageMode.public(), true, 0, seed);

const secretKey = SecretKey.rpoFalconWithRNG(seed);
const commitment = secretKey.getRpoFalcon512PublicKeyAsWord();
const authComponent = AccountComponent.createAuthComponentFromCommitment(commitment, 0);

await client.addAccountSecretKeyToWebStore(secretKey);
```

```typescript
// After (0.13.0)
import {
  AccountComponent,
  AccountStorageMode,
  AuthScheme,
  AuthSecretKey,
  WebClient,
} from "@miden-sdk/miden-sdk";

const client = await WebClient.createClient();
const wallet = await client.newWallet(
  AccountStorageMode.public(),
  true,
  AuthScheme.AuthRpoFalcon512,
  seed
);

const secretKey = AuthSecretKey.rpoFalconWithRNG(seed);
const commitment = secretKey.getPublicKeyAsWord();
const authComponent = AccountComponent.createAuthComponentFromCommitment(
  commitment,
  AuthScheme.AuthRpoFalcon512
);
const fromSecret = AccountComponent.createAuthComponentFromSecretKey(secretKey);

await client.addAccountSecretKeyToWebStore(wallet.id(), secretKey);
const commitments = await client.getPublicKeyCommitmentsOfAccount(wallet.id());
```

#### Migration Steps
1. Replace numeric auth scheme IDs with `AuthScheme` enum values.
2. Replace `SecretKey` with `AuthSecretKey` and update calls to `createAuthComponentFromSecretKey`.
3. Replace `getRpoFalcon512PublicKeyAsWord` and `getEcdsaK256KeccakPublicKeyAsWord` with `getPublicKeyAsWord`.
4. Pass an account ID to `addAccountSecretKeyToWebStore` and use `getPublicKeyCommitmentsOfAccount` when you need associated commitments.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `SecretKey is not defined` | Model removed | Use `AuthSecretKey` |
| `Argument of type number is not assignable to AuthScheme` | Numeric scheme IDs removed | Use `AuthScheme.AuthRpoFalcon512` or `AuthScheme.AuthEcdsaK256Keccak` |
| `createAuthComponent is not a function` | Method removed | Use `createAuthComponentFromSecretKey` |

### DSA signature encoding helpers

#### Summary
Signature encoding helpers were normalized under `miden-core-lib::dsa::*` with consistent
`sign`/`encode_signature` naming.

#### Affected Code

**Rust:**
```rust
// Before (0.19.x)
use miden_stdlib::falcon_sign;
use miden_core::{Felt, Word};

let sig: Vec<Felt> = falcon_sign(&secret_key_felts, msg).expect("valid key");
```

```rust
// After (0.20.x)
use miden_core_lib::dsa::falcon512_rpo;
use miden_core::{Felt, Word};

let sig: Vec<Felt> = falcon512_rpo::sign(&secret_key, msg).expect("valid key");
```

#### Migration Steps
1. Replace `miden_stdlib::falcon_sign` with `miden_core_lib::dsa::falcon512_rpo::sign`.
2. For ECDSA and EdDSA, use `miden_core_lib::dsa::ecdsa_k256_keccak::encode_signature` and
   `miden_core_lib::dsa::eddsa_ed25519::encode_signature` to build advice inputs.

#### Common Errors
| Error Message | Cause | Solution |
| --- | --- | --- |
| `error[E0432]: unresolved import miden_stdlib::falcon_sign` | Helper moved and renamed. | Use `miden_core_lib::dsa::falcon512_rpo::sign`. |

## Note Changes

### Note Metadata Attachments and Tag Semantics
**PRs:** #2249, #2252, #2260, #2268, #2279, #2219

#### Summary
`NoteMetadata` no longer stores `aux` or `NoteExecutionHint`. Metadata attachments are now represented by `NoteAttachment`, and `NoteTag` is a plain `u32` without built-in validation. Notes targeting network accounts now use the standardized `NetworkAccountTarget` attachment instead of `NoteTag::NetworkAccountId`.

In the WebClient Note metadata and tagging APIs is therefore simplified. `NoteTag.fromAccountId` is now `withAccountTarget`, `NoteExecutionMode` was removed, `NoteMetadata` no longer accepts execution hints in the constructor, and `NoteAttachment` now uses `NoteAttachmentScheme` with `asWord`/`asArray` accessors.

#### Affected Code
**Rust:**
```rust
// Before (0.12.4)
use miden_objects::note::{NoteExecutionHint, NoteMetadata, NoteTag, NoteType};

let tag = NoteTag::from_account_id(target);
let metadata = NoteMetadata::new(sender, note_type, tag, NoteExecutionHint::always(), aux)?;

// After (0.13.0)
use miden_protocol::note::{NoteAttachment, NoteAttachmentScheme, NoteMetadata, NoteTag, NoteType};
use miden_protocol::{Felt, Word};

let tag = NoteTag::with_account_target(target);
let attachment = NoteAttachment::new_word(
    NoteAttachmentScheme::none(),
    Word::from([aux, Felt::ZERO, Felt::ZERO, Felt::ZERO]),
);
let metadata = NoteMetadata::new(sender, note_type, tag).with_attachment(attachment);
```

**Rust (network account target via attachment):**
```rust
// Before (0.12.4)
use miden_objects::note::{NoteExecutionHint, NoteMetadata, NoteTag, NoteType};

let tag = NoteTag::NetworkAccountId(target);
let metadata = NoteMetadata::new(
    sender,
    NoteType::Public,
    tag,
    NoteExecutionHint::after_block(block_num),
    aux,
)?;

// After (0.13.0)
use miden_protocol::note::{NoteExecutionHint, NoteMetadata, NoteTag, NoteType};
use miden_standards::note::NetworkAccountTarget;

let tag = NoteTag::with_account_target(target);
let attachment = NetworkAccountTarget::new(target, NoteExecutionHint::after_block(block_num))?
    .into();
let metadata = NoteMetadata::new(sender, NoteType::Public, tag).with_attachment(attachment);
```

**TypeScript:**
```typescript
// Before (0.12.x)
import {
  NoteAttachment,
  NoteExecutionHint,
  NoteExecutionMode,
  NoteMetadata,
  NoteTag,
  NoteType,
} from "@miden-sdk/miden-sdk";

const tag = NoteTag.fromAccountId(targetAccountId, NoteExecutionMode.newLocal());
const metadata = new NoteMetadata(
  senderAccountId,
  NoteType.Private,
  tag,
  NoteExecutionHint.none()
);
const attachment = NoteAttachment.newWord(42, word);
```

```typescript
// After (0.13.0)
import {
  NoteAttachment,
  NoteAttachmentScheme,
  NoteExecutionHint,
  NoteMetadata,
  NoteTag,
  NoteType,
} from "@miden-sdk/miden-sdk";

const tag = NoteTag.withAccountTarget(targetAccountId);
const metadata = new NoteMetadata(senderAccountId, NoteType.Private, tag);

const scheme = new NoteAttachmentScheme(42);
const attachment = NoteAttachment.newWord(scheme, word);
const metadataWithAttachment = metadata.withAttachment(attachment);

// Optional: target a network account via attachment.
const networkAttachment = NoteAttachment.newNetworkAccountTarget(
  targetAccountId,
  NoteExecutionHint.none()
);
```

#### Migration Steps
1. Drop `aux` and `NoteExecutionHint` parameters from `NoteMetadata::new` calls.
2. If you need auxiliary data, encode it into a `NoteAttachment` (word or array).
3. Replace `NoteTag::from_account_id` with `NoteTag::with_account_target` and use `NoteTag::new` for custom tags.
4. If you used `NoteTag::NetworkAccountId` for network-account notes, replace it with a `NetworkAccountTarget` attachment from `miden_standards::note`.
5. If you relied on tag/type validation, add your own checks before constructing metadata.

#### Migration Steps (WebClient)
1. Replace `NoteTag.fromAccountId` with `NoteTag.withAccountTarget` or `withCustomAccountTarget`.
2. Drop `NoteExecutionMode` usages; attach execution context via `NoteAttachment` if needed.
3. Update `NoteMetadata` construction to `new NoteMetadata(sender, type, tag)` and add attachments with `withAttachment`.
4. Wrap attachment scheme values in `NoteAttachmentScheme` and use `asWord()` / `asArray()` to read payloads.

#### Common Errors
| Error Message | Cause | Solution |
|--------------|-------|----------|
| `this function takes 3 arguments but 5 arguments were supplied` | `NoteMetadata::new` signature changed | Remove `execution_hint` and `aux`, and use `with_attachment`. |
| `no variant or associated item named from_account_id` | `NoteTag` API changed | Use `NoteTag::with_account_target` or `NoteTag::new`. |
| `type mismatch: expected NoteMetadata, found Result` | `NoteMetadata::new` is no longer fallible | Remove `?` and handle validation manually. |

#### Common Errors (WebClient)
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `NoteExecutionMode is not defined` | Class removed | Remove it and use attachments if needed |
| `NoteTag.fromAccountId is not a function` | API renamed | Use `NoteTag.withAccountTarget` |
| `Argument of type number is not assignable to NoteAttachmentScheme` | Scheme wrapper added | Construct `new NoteAttachmentScheme(value)` |

### MINT Note Inputs and MAX_INPUTS_PER_NOTE
**PRs:** #2123, #2139

#### Summary
`create_mint_note` now takes a `MintNoteInputs` enum to support private and public output notes, and `MAX_INPUTS_PER_NOTE` increases to 1024.

#### Affected Code
**Rust:**
```rust
// Before (0.12.4)
use miden_lib::note::create_mint_note;

let note = create_mint_note(
    faucet_id,
    sender,
    recipient_digest,
    output_note_tag,
    amount,
    aux,
    output_note_aux,
    rng,
)?;

// After (0.13.0)
use miden_protocol::note::NoteAttachment;
use miden_standards::note::{create_mint_note, MintNoteInputs};

let mint_inputs = MintNoteInputs::new_private(recipient_digest, amount, output_note_tag);
let note = create_mint_note(
    faucet_id,
    sender,
    mint_inputs,
    NoteAttachment::default(),
    rng,
)?;
```

#### Migration Steps
1. Replace the old `create_mint_note` parameter list with a `MintNoteInputs` value.
2. Use `MintNoteInputs::new_private` for private outputs or `MintNoteInputs::new_public` for public outputs.
3. Update any hard-coded input length limits to use `miden_protocol::MAX_INPUTS_PER_NOTE`.

#### Common Errors
| Error Message | Cause | Solution |
|--------------|-------|----------|
| `this function takes 5 arguments but 8 arguments were supplied` | `create_mint_note` signature changed | Build a `MintNoteInputs` value and pass it instead. |
| `NoteError::TooManyInputs` | Public output note inputs exceed the limit | Ensure total inputs do not exceed `MAX_INPUTS_PER_NOTE` (now 1024). |
| `cannot find type MintNoteInputs` | Missing `miden-standards` import | Add `miden-standards` dependency and import from `miden_standards::note`. |

### Input notes API unified
**PR:** #1624

#### Summary
Input notes are no longer split into authenticated and unauthenticated lists. Builders now accept full `Note` objects and the client determines authentication internally, and the WebClient consume request now accepts `Note[]` instead of note ID strings.

#### Affected Code

**Rust:**
```rust
// Before (0.12.x)
use miden_client::auth::TransactionAuthenticator;
use miden_client::note::NoteId;
use miden_client::transaction::TransactionRequestBuilder;
use miden_client::{Client, ClientError};

async fn build_request<AUTH: TransactionAuthenticator + Sync>(
    client: &Client<AUTH>,
    note_id: NoteId,
) -> Result<miden_client::transaction::TransactionRequest, ClientError> {
    let tx_request = TransactionRequestBuilder::new()
        .authenticated_input_notes(vec![(note_id, None)])
        .build()?;
    Ok(tx_request)
}
```

```rust
// After (0.13.0)
use miden_client::auth::TransactionAuthenticator;
use miden_client::note::{Note, NoteId};
use miden_client::transaction::TransactionRequestBuilder;
use miden_client::{Client, ClientError};

async fn build_request<AUTH: TransactionAuthenticator + Sync>(
    client: &Client<AUTH>,
    note_id: NoteId,
) -> Result<miden_client::transaction::TransactionRequest, ClientError> {
    let record = client.get_input_note(note_id).await?.expect("note not found");
    let note: Note = record.try_into().expect("failed to convert note record");

    let tx_request = TransactionRequestBuilder::new()
        .input_notes(vec![(note, None)])
        .build()?;
    Ok(tx_request)
}
```

**TypeScript:**
```typescript
// Before (0.12.x)
import {
  NoteIdAndArgs,
  NoteIdAndArgsArray,
  TransactionRequestBuilder,
  WebClient,
} from "@miden-sdk/miden-sdk";

const client = await WebClient.createClient();
const consumeRequest = client.newConsumeTransactionRequest([noteId]);

const noteIdAndArgs = new NoteIdAndArgs(noteId, null);
const txRequest = new TransactionRequestBuilder()
  .withAuthenticatedInputNotes(new NoteIdAndArgsArray([noteIdAndArgs]))
  .build();
```

```typescript
// After (0.13.0)
import {
  NoteAndArgs,
  NoteAndArgsArray,
  TransactionRequestBuilder,
  WebClient,
} from "@miden-sdk/miden-sdk";

const client = await WebClient.createClient();
const record = await client.getInputNote(noteId);
if (!record) {
  throw new Error(`Note with ID ${noteId} not found`);
}
const note = record.toNote();

const consumeRequest = client.newConsumeTransactionRequest([note]);

const noteAndArgs = new NoteAndArgs(note, null);
const txRequest = new TransactionRequestBuilder()
  .withInputNotes(new NoteAndArgsArray([noteAndArgs]))
  .build();
```

#### Migration Steps
1. Replace `authenticated_input_notes` and `unauthenticated_input_notes` with `input_notes`.
2. Convert `NoteId` values into `Note` objects (Rust: `InputNoteRecord` -> `Note` via `try_into`; Web: `InputNoteRecord.toNote()`).
3. Update `newConsumeTransactionRequest` to pass `Note[]`, and replace `NoteIdAndArgs` with `NoteAndArgs`.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `method not found: authenticated_input_notes` | Deprecated builder methods removed | Use `input_notes` with `Note` values |
| `expected Note, found NoteId` | Passing IDs where full notes are required | Fetch the note record and convert to `Note` |
| `newConsumeTransactionRequest expects Note[]` | API now requires notes, not strings | Call `getInputNote(...).toNote()` first |

### FetchedNote and RPC note shapes refactored
**PRs:** #1536, #1606

#### Summary
Fetched notes now carry a `NoteHeader` for private notes and always expose the inclusion proof in the WebClient. Web `FetchedNote` exposes `header`, `note`, and `inclusionProof`, with `asInputNote()` for public notes.

#### Affected Code

**Rust:**
```rust
// Before (0.12.x)
use miden_client::rpc::domain::note::FetchedNote;

fn handle_note(note: FetchedNote) {
    match note {
        FetchedNote::Private(note_id, metadata, proof) => {
            let _ = (note_id, metadata, proof);
        }
        FetchedNote::Public(note, proof) => {
            let _ = (note, proof);
        }
    }
}
```

```rust
// After (0.13.0)
use miden_client::rpc::domain::note::FetchedNote;

fn handle_note(note: FetchedNote) {
    match note {
        FetchedNote::Private(header, proof) => {
            let note_id = header.id();
            let metadata = header.metadata();
            let _ = (note_id, metadata, proof);
        }
        FetchedNote::Public(note, proof) => {
            let _ = (note, proof);
        }
    }
}
```

**TypeScript:**
```typescript
// Before (0.12.x)
const fetched = (await rpcClient.getNotesById([noteId]))[0];
if (fetched.inputNote) {
  const scriptRoot = fetched.inputNote.note().script().root();
}
```

```typescript
// After (0.13.0)
const fetched = (await rpcClient.getNotesById([noteId]))[0];
const proof = fetched.inclusionProof;
const note = fetched.note;
if (note) {
  const scriptRoot = note.script().root();
}
const inputNote = fetched.asInputNote();
```

#### Migration Steps
1. Update pattern matches for `FetchedNote::Private` to use `NoteHeader`.
2. In the WebClient, replace `inputNote` access with `note` plus `inclusionProof`, or call `asInputNote()`.
3. Use `header` for shared access to `noteId` and `metadata`.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `pattern has 3 fields, but the corresponding tuple variant has 2 fields` | `FetchedNote::Private` shape changed | Use `FetchedNote::Private(header, proof)` |
| `Property 'inputNote' does not exist on type 'FetchedNote'` | Web shape updated | Use `note`, `inclusionProof`, or `asInputNote()` |

### NoteScreener relevance replaced by NoteConsumptionStatus
**PR:** #1630

#### Summary
`NoteRelevance` was removed; `NoteScreener` now reports `NoteConsumptionStatus` values, and the WebClient exposes consumption status objects rather than a single `consumableAfterBlock` field.

#### Affected Code

**Rust:**
```rust
// Before (0.12.x)
use miden_client::note::{NoteRelevance, NoteScreener};

let relevances = note_screener.check_relevance(&note).await?;
for (_, relevance) in relevances {
    if relevance == NoteRelevance::Now {
        // ...
    }
}
```

```rust
// After (0.13.0)
use miden_client::note::{NoteConsumptionStatus, NoteScreener};

let relevances = note_screener.check_relevance(&note).await?;
for (_, status) in relevances {
    match status {
        NoteConsumptionStatus::Consumable
        | NoteConsumptionStatus::ConsumableWithAuthorization => {
            // ...
        }
        NoteConsumptionStatus::ConsumableAfter(_) => {
            // ...
        }
        _ => {}
    }
}
```

**TypeScript:**
```typescript
// Before (0.12.x)
const records = await client.getConsumableNotes(accountId);
const after = records[0].noteConsumability()[0].consumableAfterBlock();
```

```typescript
// After (0.13.0)
const records = await client.getConsumableNotes(accountId);
const status = records[0].noteConsumability()[0].consumptionStatus();
const after = status.consumableAfterBlock();
```

#### Migration Steps
1. Replace `NoteRelevance` with `NoteConsumptionStatus` in Rust logic and pattern matching.
2. Update WebClient consumption checks to call `consumptionStatus()` and then `consumableAfterBlock()`.
3. Remove any reliance on `NoteRelevance::Now` / `After` variants.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `use of undeclared type NoteRelevance` | Type removed | Use `NoteConsumptionStatus` |
| `consumableAfterBlock is not a function` | API moved under `consumptionStatus()` | Call `consumptionStatus().consumableAfterBlock()` |

## Transaction Changes

### Transaction Events: IDs vs Data Extraction
**PR:** #2071

#### Summary
`TransactionEvent` was renamed to `TransactionEventId`, and event data extraction is now handled separately inside `miden-tx` via `TransactionEvent::extract`.

#### Affected Code
**Rust:**
```rust
// Before (0.12.4)
use miden_lib::transaction::{EventId, TransactionEvent};

let event = TransactionEvent::try_from(event_id)?;
match event {
    TransactionEvent::AccountStorageAfterSetItem => {
        // read stack/process data manually
    }
    _ => {}
}

// After (0.13.0)
use miden_protocol::transaction::TransactionEventId;
use crate::host::TransactionEvent; // inside miden-tx host implementation

let _event_id = TransactionEventId::try_from(event_id)?;
if let Some(event) = TransactionEvent::extract(base_host, process)? {
    match event {
        TransactionEvent::AccountStorageAfterSetItem { slot_name, new_value } => {
            // use extracted data directly
        }
        _ => {}
    }
}
```

#### Migration Steps
1. Replace `TransactionEvent` uses with `TransactionEventId` for ID-level checks.
2. If you implement a custom host in `miden-tx`, use `TransactionEvent::extract` to get data-rich events.
3. Remove manual stack parsing for events that now surface structured data.

#### Common Errors
| Error Message | Cause | Solution |
|--------------|-------|----------|
| `unresolved import miden_lib::transaction::TransactionEvent` | Enum renamed and moved | Use `miden_protocol::transaction::TransactionEventId`. |
| `no function or associated item named extract` | Using old event API | Use `TransactionEvent::extract` from `miden-tx` host code. |
| `pattern requires 0 fields but 2 fields were supplied` | Event variants now carry data | Match on the new data-carrying variants. |

## Client Changes

### (Rust) NodeRpcClient account proof API changed
**PR:** #1616

#### Summary
The batch `get_account_proofs` API is replaced with a single-account call that requires `AccountStateAt`, and the known code parameter is now optional per account.

#### Affected Code

**Rust:**
```rust
// Before (0.12.x)
use std::collections::{BTreeMap, BTreeSet};

use miden_client::account::{AccountCode, AccountId};
use miden_client::block::BlockNumber;
use miden_client::rpc::domain::account::AccountProof;
use miden_client::rpc::NodeRpcClient;
use miden_client::transaction::ForeignAccount;

async fn fetch_proofs(
    rpc: &dyn NodeRpcClient,
    accounts: BTreeSet<ForeignAccount>,
    known_codes: BTreeMap<AccountId, AccountCode>,
) -> Result<(BlockNumber, Vec<AccountProof>), miden_client::rpc::RpcError> {
    rpc.get_account_proofs(&accounts, known_codes).await
}
```

```rust
// After (0.13.0)
use miden_client::account::AccountCode;
use miden_client::block::BlockNumber;
use miden_client::rpc::domain::account::AccountProof;
use miden_client::rpc::{AccountStateAt, NodeRpcClient};
use miden_client::transaction::ForeignAccount;

async fn fetch_proof(
    rpc: &dyn NodeRpcClient,
    account: ForeignAccount,
    known_code: Option<AccountCode>,
) -> Result<(BlockNumber, AccountProof), miden_client::rpc::RpcError> {
    rpc.get_account(account, AccountStateAt::ChainTip, known_code).await
}
```

#### Migration Steps
1. Replace `get_account_proofs` with `get_account` and call it per `ForeignAccount`.
2. Pass the desired state via `AccountStateAt::ChainTip` or `AccountStateAt::Block`.
3. Update implementations of `NodeRpcClient` to match the new signature and return type.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `method not found: get_account_proofs` | Old trait method removed | Use `get_account` and loop |
| `missing argument: account_state` | New `AccountStateAt` required | Pass `AccountStateAt::ChainTip` or `AccountStateAt::Block` |
| `expected AccountProof, found Vec<AccountProof>` | Return type changed | Handle single proof per call |

### (Rust) Client RNG must be Send and Sync
**PR:** #1677

#### Summary
The client RNG must now be `Send + Sync` via the `ClientFeltRng` marker and `ClientRngBox` alias so `Client` can be `Send + Sync`.

#### Affected Code

**Rust:**
```rust
// Before (0.12.x)
use miden_client::builder::ClientBuilder;
use miden_client::crypto::{FeltRng, RpoRandomCoin};

let rng: Box<dyn FeltRng> = Box::new(RpoRandomCoin::new([0u8; 32]));
let builder = ClientBuilder::new().rng(rng);
```

```rust
// After (0.13.0)
use miden_client::builder::ClientBuilder;
use miden_client::crypto::RpoRandomCoin;
use miden_client::ClientRngBox;

let rng: ClientRngBox = Box::new(RpoRandomCoin::new([0u8; 32]));
let builder = ClientBuilder::new().rng(rng);
```

#### Migration Steps
1. Ensure your RNG implements `Send + Sync`.
2. Wrap the RNG in `ClientRngBox` and pass it to `ClientBuilder::rng`.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `the trait bound ...: Send + Sync is not satisfied` | RNG type not thread-safe | Use a `Send + Sync` RNG or wrap it safely |

### (Rust) CLI swap payback_note_type removed
**PR:** #1700

#### Summary
The CLI swap command no longer accepts a `payback_note_type` argument; the payback note type is now fixed.

#### Affected Code

**CLI:**
```bash
# Before (0.12.x)
miden-client swap \
  --offered-asset 10::0x... \
  --requested-asset 5::0x... \
  --note-type public \
  --payback-note-type public
```

```bash
# After (0.13.0)
miden-client swap \
  --offered-asset 10::0x... \
  --requested-asset 5::0x... \
  --note-type public
```

#### Migration Steps
1. Remove `--payback-note-type` from swap command invocations or scripts.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `unexpected argument '--payback-note-type'` | Flag removed | Drop the flag from the command |

### (WebClient) IndexedDB naming for multiple instances
**PR:** #1645

#### Summary
The WebClient store is now named, so multiple clients can coexist in the same browser. `WebClient.createClient` and `createClientWithExternalKeystore` accept an optional store name before callback arguments.

#### Affected Code

**TypeScript:**
```typescript
// Before (0.12.x)
const client = await WebClient.createClient(rpcUrl, noteTransportUrl, seed);

const clientWithKeystore = await WebClient.createClientWithExternalKeystore(
  rpcUrl,
  noteTransportUrl,
  seed,
  getKeyCb,
  insertKeyCb,
  signCb
);
```

```typescript
// After (0.13.0)
const client = await WebClient.createClient(
  rpcUrl,
  noteTransportUrl,
  seed,
  "app-db"
);

const clientWithKeystore = await WebClient.createClientWithExternalKeystore(
  rpcUrl,
  noteTransportUrl,
  seed,
  "app-db",
  getKeyCb,
  insertKeyCb,
  signCb
);
```

#### Migration Steps
1. Add a store name to `createClient` when you need multiple instances in one origin.
2. Shift external keystore callback arguments one position to the right and pass `storeName` first.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `Expected 7 arguments, but got 6` | Missing `storeName` in `createClientWithExternalKeystore` | Insert the store name before callbacks |

### (WebClient) Block numbers are numeric in web APIs and IndexedDB
**PRs:** #1528, #1684

#### Summary
WebClient transaction interfaces and IndexedDB storage now use numeric block numbers instead of strings.

#### Affected Code

**TypeScript:**
```typescript
// Before (0.12.x)
const summary = await client.syncState();
const blockNum = parseInt(summary.blockNum(), 10);
```

```typescript
// After (0.13.0)
const summary = await client.syncState();
const blockNum = summary.blockNum();
```

#### Migration Steps
1. Remove `parseInt` or `Number(...)` wrappers around `blockNum()` results.
2. If you integrate with `idxdb-store` helpers directly, pass numbers instead of strings.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `Argument of type 'string' is not assignable to parameter of type 'number'` | Block numbers now numeric | Pass `number` values directly |

### (WebClient) NetworkId custom networks and toBech32Custom removal
**PR:** #1612

#### Summary
`NetworkId` is now a class with static constructors and supports custom prefixes. `toBech32Custom` was removed; use `NetworkId.custom(...)` with `toBech32` instead.

#### Affected Code

**TypeScript:**
```typescript
// Before (0.12.x)
const bech32 = accountId.toBech32Custom("cstm", AccountInterface.BasicWallet);
const network = NetworkId.Testnet;
```

```typescript
// After (0.13.0)
const network = NetworkId.custom("cstm");
const bech32 = accountId.toBech32(network, AccountInterface.BasicWallet);
const testnet = NetworkId.testnet();
```

#### Migration Steps
1. Replace enum-style `NetworkId.Mainnet/Testnet/Devnet` with `NetworkId.mainnet()/testnet()/devnet()`.
2. Replace `toBech32Custom(prefix, ...)` with `toBech32(NetworkId.custom(prefix), ...)`.

#### Common Errors
| Error Message | Cause | Solution |
|---------------|-------|----------|
| `Property 'Mainnet' does not exist on type 'typeof NetworkId'` | Enum replaced by class | Use `NetworkId.mainnet()` and friends |
| `toBech32Custom is not a function` | Method removed | Use `NetworkId.custom(...)` + `toBech32` |

## General MASM Changes

### MASM syntax updates
**PR:** (not listed in changelog)

#### Summary
MASM syntax was modernized. Several legacy forms are no longer accepted by the parser.

#### Affected Code

**MASM:**
```diff
- const.A
+ const A

- export.foo
+ pub proc foo

- proc.bar
+ proc bar

- use.miden::*
+ use miden::*

- export.miden::foo::bar
+ pub use miden::foo::bar
```

#### Migration Steps
1. Replace dotted keywords (`const.A`, `export.foo`, `proc.bar`) with spaced forms.
2. Replace `use.miden::*` with `use miden::*`.
3. For re-exports, use `pub use` instead of `export.<path>`.

#### Common Errors
| Error Message | Cause | Solution |
| --- | --- | --- |
| `unexpected token '.'` | Legacy dotted syntax. | Use `const A`, `pub proc foo`, `proc bar`, `use miden::*`. |

### Falcon512Rpo Renames
**PR:** #2264

#### Summary
The `RpoFalcon512` naming is replaced with `Falcon512Rpo` across types, modules, procedures, and files.

#### Affected Code
**Rust:**
```rust
// Before (0.12.4)
use miden_lib::account::auth::AuthRpoFalcon512Acl;
use miden_objects::crypto::dsa::rpo_falcon_512;

// After (0.13.0)
use miden_standards::account::auth::AuthFalcon512RpoAcl;
use miden_protocol::crypto::dsa::falcon512_rpo;
```

#### Migration Steps
1. Rename `RpoFalcon512` types and modules to `Falcon512Rpo` in imports and identifiers.
2. Update any file or procedure names that embed `rpo_falcon_512` to `falcon_512_rpo`.

#### Common Errors
| Error Message | Cause | Solution |
|--------------|-------|----------|
| `unresolved import miden_lib::account::auth::AuthRpoFalcon512Acl` | Type renamed and crate moved | Use `miden_standards::account::auth::AuthFalcon512RpoAcl`. |
| `cannot find module rpo_falcon_512` | Module renamed | Import from `falcon512_rpo`. |

### ECDSA precompile procedure renames
**PR:** #2413

#### Summary
ECDSA precompile procedures were renamed and moved under the normalized
`miden::core::crypto::dsa::ecdsa_k256_keccak` module.

#### Affected Code

**MASM:**
```diff
- use std::crypto::dsa::ecdsa::secp256k1
+ use miden::core::crypto::dsa::ecdsa_k256_keccak

- exec.secp256k1::verify
- exec.secp256k1::verify_impl
+ exec.ecdsa_k256_keccak::verify
+ exec.ecdsa_k256_keccak::verify_prehash
```

#### Migration Steps
1. Update module paths from `std::crypto::dsa::ecdsa::secp256k1` to
   `miden::core::crypto::dsa::ecdsa_k256_keccak`.
2. Replace `verify_impl` calls with `verify_prehash` where used directly.
3. If you referenced the ECDSA event name in host code, update it to
   `miden::core::crypto::dsa::ecdsa_k256_keccak::verify`.

#### Common Errors
| Error Message | Cause | Solution |
| --- | --- | --- |
| `unrecognized token "secp256k1::verify"` | Procedure renamed and moved. | Use `ecdsa_k256_keccak::verify`. |
| `unrecognized token "verify_impl"` | Internal precompile wrapper renamed. | Use `verify_prehash`. |

### RPO hash helper renames
**PR:** #2395

#### Summary
RPO memory hashing helpers were renamed for consistency. These are MASM procedure name changes.

#### Affected Code

**MASM:**
```diff
- exec.rpo::hash_memory_with_state
- exec.rpo::hash_memory_words
- exec.rpo::hash_memory_double_words
+ exec.rpo256::hash_elements_with_state
+ exec.rpo256::hash_words
+ exec.rpo256::hash_double_words
```

#### Migration Steps
1. Update module path to `miden::core::crypto::hashes::rpo256` if you were using `rpo`.
2. Replace the procedure names as shown above.

#### Common Errors
| Error Message | Cause | Solution |
| --- | --- | --- |
| `unrecognized token "hash_memory_words"` | Procedure renamed. | Use `hash_words`. |

## Assembler Changes

### LibraryPath removal (Path-only APIs)
**PR:** (not listed in changelog)

#### Summary
`LibraryPath` was removed; APIs now accept `Path` (from `miden_assembly` or `std::path`).

#### Affected Code

**Rust:**
```rust
// Before (0.19.x)
use miden_assembly::{LibraryPath, Path};

let namespace = LibraryPath::from_str("miden::core::foo")?;
assembler.compile_and_statically_link_from_dir(namespace, dir)?;
```

```rust
// After (0.20.x)
use miden_assembly::Path;

assembler.compile_and_statically_link_from_dir(dir, Path::new("miden::core::foo"))?;
```

#### Migration Steps
1. Remove `LibraryPath` usage and construct `Path` directly.
2. If you use both `std::path::Path` and `miden_assembly::Path`, qualify imports to avoid
   ambiguity.

#### Common Errors
| Error Message | Cause | Solution |
| --- | --- | --- |
| `error[E0432]: unresolved import miden_assembly::LibraryPath` | Type removed. | Use `miden_assembly::Path`. |

### Assembler API updates
**PR:** #2396, (not listed in changelog)

#### Summary
Assembler debug mode is now always enabled and the debug-mode toggles were removed. The argument
order of `compile_and_statically_link_from_dir` changed to `(dir, namespace)` and namespaces are
now `Path`-based.

#### Affected Code

**Rust:**
```rust
// Before (0.19.x)
use miden_assembly::{Assembler, LibraryNamespace};

let mut assembler = Assembler::default().with_debug_mode(true);
assembler.compile_and_statically_link_from_dir(LibraryNamespace::Kernel, "./masm")?;
```

```rust
// After (0.20.x)
use miden_assembly::{Assembler, Path};

let mut assembler = Assembler::default();
assembler.compile_and_statically_link_from_dir("./masm", Path::new("miden::core"))?;
```

#### Migration Steps
1. Remove calls to `with_debug_mode`, `set_debug_mode`, or `in_debug_mode`; the assembler always
   emits debug info now.
2. Update `compile_and_statically_link_from_dir` to pass the directory first.
3. Replace `LibraryNamespace` usage with `Path` (e.g., `Path::KERNEL`, `Path::EXEC`, or a string).

#### Common Errors
| Error Message | Cause | Solution |
| --- | --- | --- |
| `error[E0599]: no method named with_debug_mode found for struct Assembler` | Debug mode toggles were removed. | Drop the call and rely on default behavior. |
| `error[E0308]: mismatched types: expected Path, found LibraryNamespace` | The API now uses `Path`. | Use `Path::new("miden::core")` or a string. |
| `error[E0061]: this method takes 2 arguments but 2 arguments were supplied in the wrong order` | Argument order was flipped. | Pass `(dir, namespace)` instead of `(namespace, dir)`. |