# 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)`. |