-
Notifications
You must be signed in to change notification settings - Fork 26
v0.13 migration guide
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.
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.
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.
Cargo.toml:
- 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:
// 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;- Replace
miden-objectswithmiden-protocolandmiden-libwithmiden-standardsinCargo.tomland imports. - Bump direct Miden VM dependencies to 0.20 and
miden-cryptoto 0.19 if you depend on them directly. - Update error imports to
miden_protocol::errors::*. - Run
cargo checkto catch any remaining import path mismatches.
| 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. |
The MASM standard library was renamed to miden::core, and the Rust wrapper moved from
StdLibrary (miden-stdlib) to CoreLibrary (miden-core-lib).
Rust:
- use miden_stdlib::StdLibrary;
+ use miden_core_lib::CoreLibrary;MASM:
- use std::crypto::hashes::rpo
+ use miden::core::crypto::hashes::rpo256- Replace
StdLibrarywithCoreLibraryand update dependencies tomiden-core-lib. - Replace MASM imports from
std::tomiden::core::.
| 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::.... |
Storage slots are now identified by StorageSlotName instead of numeric indices. Also AccountStorage and AccountStorageDelta APIs are name-based.
Rust Client:
// Before (0.12.x)
use miden_client::account::{StorageMap, StorageSlot};
fn storage_slots(storage_map: StorageMap) -> Vec<StorageSlot> {
vec![StorageSlot::Map(storage_map)]
}// 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:
// 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);// 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):
// 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):
// 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)?;- Define storage slots with
StorageSlotName::newand buildStorageSlotvalues withwith_valueorwith_map. - Replace index-based slots with named slots (
StorageSlotNamein Rust, string names in Web). - Update
AccountStorageDeltausage to passStorageSlotName. - Update any storage accessors (
getItem,getMapItem,getMapEntries) to pass slot names.
| 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")
|
native_account::set_map_item now takes slot IDs and returns only the old value.
MASM (kernel call):
# 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]
- Handle
Resultfromset_itemandset_map_item. - 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.
| stack underflow in MASM around set_map_item | Code still expects OLD_MAP_ROOT | Update stack handling to only consume OLD_VALUE. |
The filesystem keystore is also no longer generic over RNG.
// Before (0.12.x)
use miden_client::builder::ClientBuilder;
use miden_client::keystore::FilesystemKeyStore;
let builder = ClientBuilder::<FilesystemKeyStore<_>>::new();// After (0.13.0)
use miden_client::builder::ClientBuilder;
use miden_client::keystore::FilesystemKeyStore;
let builder = ClientBuilder::<FilesystemKeyStore>::new();- Update account component compilation to use
CodeBuilder.compileAccountComponentCodeand pass the resultingAccountComponentCode. - If you used
FilesystemKeyStore<_>generics, drop the RNG parameter.
| Error Message | Cause | Solution |
|---|---|---|
type annotations needed for FilesystemKeyStore |
Removed RNG generic | Use FilesystemKeyStore without type params |
The web binding for AccountComponent.compile now requires a compiled AccountComponentCode.
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);// 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();- Update account component compilation to use
CodeBuilder.compileAccountComponentCodeand pass the resultingAccountComponentCode.
| AccountComponent.compile takes 2 arguments | Old binding passed CodeBuilder directly | Compile to AccountComponentCode first |
AccountComponentTemplate is removed in favor of metadata-driven component creation via StorageSchema. InitStorageData now supports native types and map entries keyed by StorageSlotName.
Rust (template removal):
// 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):
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):
// 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))?;- Replace
AccountComponentTemplateusage withAccountComponent::from_packageorAccountComponent::from_library. - Define storage layouts with
StorageSchemaand related schema types (StorageSlotSchema,ValueSlotSchema,WordSchema). - Update
InitStorageDatato use native types (e.g.,Felt,Word) and map entries keyed byStorageSlotName. - Handle new validation errors from
InitStorageData::newandInitStorageData::set_value.
| 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. |
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.
Rust (procedure roots):
// 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):
// 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):
// Before (0.13.0 pre-change)
AuthEcdsaK256KeccakAcl::tracked_procedure_roots_slot();
// After (0.13.0)
AuthEcdsaK256KeccakAcl::trigger_procedure_roots_slot();- Replace
AccountProcedureInfowithAccountProcedureRootand remove storage offset/size logic. - Update
AccountProcedureIndexMap::newcall sites to remove error handling and pass the new inputs toget_proc_index. - If you relied on duplicate procedure roots to raise
AccountComponentDuplicateProcedureRoot, add your own validation before composing components. - Update ACL helper method name to
trigger_procedure_roots_slot.
| 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 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.
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);// 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());- Replace numeric auth scheme IDs with
AuthSchemeenum values. - Replace
SecretKeywithAuthSecretKeyand update calls tocreateAuthComponentFromSecretKey. - Replace
getRpoFalcon512PublicKeyAsWordandgetEcdsaK256KeccakPublicKeyAsWordwithgetPublicKeyAsWord. - Pass an account ID to
addAccountSecretKeyToWebStoreand usegetPublicKeyCommitmentsOfAccountwhen you need associated commitments.
| 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
|
Signature encoding helpers were normalized under miden-core-lib::dsa::* with consistent
sign/encode_signature naming.
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");// 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");- Replace
miden_stdlib::falcon_signwithmiden_core_lib::dsa::falcon512_rpo::sign. - For ECDSA and EdDSA, use
miden_core_lib::dsa::ecdsa_k256_keccak::encode_signatureandmiden_core_lib::dsa::eddsa_ed25519::encode_signatureto build advice inputs.
| Error Message | Cause | Solution |
|---|---|---|
error[E0432]: unresolved import miden_stdlib::falcon_sign |
Helper moved and renamed. | Use miden_core_lib::dsa::falcon512_rpo::sign. |
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.
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):
// 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);Rust client:
// Before (0.12.x)
use miden_client::note::{
NoteExecutionHint,
NoteMetadata,
NoteTag,
NoteType,
};
use miden_client::Felt;
let tag = NoteTag::from_account_id(target_account_id);
let metadata = NoteMetadata::new(
sender_account_id,
NoteType::Private,
tag,
NoteExecutionHint::none(),
Felt::default(),
)
.expect("valid metadata");// After (0.13.0)
use miden_client::note::{
NoteAttachment,
NoteAttachmentScheme,
NoteMetadata,
NoteTag,
NoteType,
};
let tag = NoteTag::with_account_target(target_account_id);
let metadata = NoteMetadata::new(sender_account_id, NoteType::Private, tag);
let scheme = NoteAttachmentScheme::new(42);
let attachment = NoteAttachment::new_word(scheme, word);
let metadata_with_attachment = metadata.with_attachment(attachment);WebClient:
// 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);// 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()
);- Drop
auxandNoteExecutionHintparameters fromNoteMetadata::newcalls. - If you need auxiliary data, encode it into a
NoteAttachment(word or array). - Replace
NoteTag::from_account_idwithNoteTag::with_account_targetand useNoteTag::newfor custom tags. - If you used
NoteTag::NetworkAccountIdfor network-account notes, replace it with aNetworkAccountTargetattachment frommiden_standards::note. - If you relied on tag/type validation, add your own checks before constructing metadata.
- Replace
NoteTag.fromAccountIdwithNoteTag.withAccountTargetorwithCustomAccountTarget. - Drop
NoteExecutionModeusages; attach execution context viaNoteAttachmentif needed. - Update
NoteMetadataconstruction tonew NoteMetadata(sender, type, tag)and add attachments withwithAttachment. - Wrap attachment scheme values in
NoteAttachmentSchemeand useasWord()/asArray()to read payloads.
| 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. |
| 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)
|
create_mint_note now takes a MintNoteInputs enum to support private and public output notes, and MAX_INPUTS_PER_NOTE increases to 1024.
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,
)?;- Replace the old
create_mint_noteparameter list with aMintNoteInputsvalue. - Use
MintNoteInputs::new_privatefor private outputs orMintNoteInputs::new_publicfor public outputs. - Update any hard-coded input length limits to use
miden_protocol::MAX_INPUTS_PER_NOTE.
| 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 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.
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)
}// 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:
// 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();// 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();- Replace
authenticated_input_notesandunauthenticated_input_noteswithinput_notes. - Convert
NoteIdvalues intoNoteobjects (Rust:InputNoteRecord->Noteviatry_into; Web:InputNoteRecord.toNote()). - Update
newConsumeTransactionRequestto passNote[], and replaceNoteIdAndArgswithNoteAndArgs.
| 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 |
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.
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);
}
}
}// 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:
// Before (0.12.x)
const fetched = (await rpcClient.getNotesById([noteId]))[0];
if (fetched.inputNote) {
const scriptRoot = fetched.inputNote.note().script().root();
}// 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();- Update pattern matches for
FetchedNote::Privateto useNoteHeader. - In the WebClient, replace
inputNoteaccess withnoteplusinclusionProof, or callasInputNote(). - Use
headerfor shared access tonoteIdandmetadata.
| 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()
|
NoteRelevance was removed; NoteScreener now reports NoteConsumptionStatus values, and the WebClient exposes consumption status objects rather than a single consumableAfterBlock field.
Rust:
// Before (0.12.x)
use miden_client::note::{NoteRelevance, NoteScreener};
let relevances = note_screener.check_relevance(¬e).await?;
for (_, relevance) in relevances {
if relevance == NoteRelevance::Now {
// ...
}
}// After (0.13.0)
use miden_client::note::{NoteConsumptionStatus, NoteScreener};
let relevances = note_screener.check_relevance(¬e).await?;
for (_, status) in relevances {
match status {
NoteConsumptionStatus::Consumable
| NoteConsumptionStatus::ConsumableWithAuthorization => {
// ...
}
NoteConsumptionStatus::ConsumableAfter(_) => {
// ...
}
_ => {}
}
}TypeScript:
// Before (0.12.x)
const records = await client.getConsumableNotes(accountId);
const after = records[0].noteConsumability()[0].consumableAfterBlock();// After (0.13.0)
const records = await client.getConsumableNotes(accountId);
const status = records[0].noteConsumability()[0].consumptionStatus();
const after = status.consumableAfterBlock();- Replace
NoteRelevancewithNoteConsumptionStatusin Rust logic and pattern matching. - Update WebClient consumption checks to call
consumptionStatus()and thenconsumableAfterBlock(). - Remove any reliance on
NoteRelevance::Now/Aftervariants.
| 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()
|
TransactionEvent was renamed to TransactionEventId, and event data extraction is now handled separately inside miden-tx via TransactionEvent::extract.
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
}
_ => {}
}
}- Replace
TransactionEventuses withTransactionEventIdfor ID-level checks. - If you implement a custom host in
miden-tx, useTransactionEvent::extractto get data-rich events. - Remove manual stack parsing for events that now surface structured data.
| 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. |
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.
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
}// 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
}- Replace
get_account_proofswithget_accountand call it perForeignAccount. - Pass the desired state via
AccountStateAt::ChainTiporAccountStateAt::Block. - Update implementations of
NodeRpcClientto match the new signature and return type.
| 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 |
The client RNG must now be Send + Sync via the ClientFeltRng marker and ClientRngBox alias so Client can be Send + Sync.
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);// 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);- Ensure your RNG implements
Send + Sync. - Wrap the RNG in
ClientRngBoxand pass it toClientBuilder::rng.
| 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 |
The CLI swap command no longer accepts a payback_note_type argument; the payback note type is now fixed.
CLI:
# Before (0.12.x)
miden-client swap \
--offered-asset 10::0x... \
--requested-asset 5::0x... \
--note-type public \
--payback-note-type public# After (0.13.0)
miden-client swap \
--offered-asset 10::0x... \
--requested-asset 5::0x... \
--note-type public- Remove
--payback-note-typefrom swap command invocations or scripts.
| Error Message | Cause | Solution |
|---|---|---|
unexpected argument '--payback-note-type' |
Flag removed | Drop the flag from the command |
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.
TypeScript:
// Before (0.12.x)
const client = await WebClient.createClient(rpcUrl, noteTransportUrl, seed);
const clientWithKeystore = await WebClient.createClientWithExternalKeystore(
rpcUrl,
noteTransportUrl,
seed,
getKeyCb,
insertKeyCb,
signCb
);// 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
);- Add a store name to
createClientwhen you need multiple instances in one origin. - Shift external keystore callback arguments one position to the right and pass
storeNamefirst.
| Error Message | Cause | Solution |
|---|---|---|
Expected 7 arguments, but got 6 |
Missing storeName in createClientWithExternalKeystore
|
Insert the store name before callbacks |
WebClient transaction interfaces and IndexedDB storage now use numeric block numbers instead of strings.
TypeScript:
// Before (0.12.x)
const summary = await client.syncState();
const blockNum = parseInt(summary.blockNum(), 10);// After (0.13.0)
const summary = await client.syncState();
const blockNum = summary.blockNum();- Remove
parseIntorNumber(...)wrappers aroundblockNum()results. - If you integrate with
idxdb-storehelpers directly, pass numbers instead of strings.
| Error Message | Cause | Solution |
|---|---|---|
Argument of type 'string' is not assignable to parameter of type 'number' |
Block numbers now numeric | Pass number values directly |
NetworkId is now a class with static constructors and supports custom prefixes. toBech32Custom was removed; use NetworkId.custom(...) with toBech32 instead.
TypeScript:
// Before (0.12.x)
const bech32 = accountId.toBech32Custom("cstm", AccountInterface.BasicWallet);
const network = NetworkId.Testnet;// After (0.13.0)
const network = NetworkId.custom("cstm");
const bech32 = accountId.toBech32(network, AccountInterface.BasicWallet);
const testnet = NetworkId.testnet();- Replace enum-style
NetworkId.Mainnet/Testnet/DevnetwithNetworkId.mainnet()/testnet()/devnet(). - Replace
toBech32Custom(prefix, ...)withtoBech32(NetworkId.custom(prefix), ...).
| 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
|
MASM syntax was modernized. Several legacy forms are no longer accepted by the parser.
MASM:
- 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- Replace dotted keywords (
const.A,export.foo,proc.bar) with spaced forms. - Replace
use.miden::*withuse miden::*. - For re-exports, use
pub useinstead ofexport.<path>.
| Error Message | Cause | Solution |
|---|---|---|
unexpected token '.' |
Legacy dotted syntax. | Use const A, pub proc foo, proc bar, use miden::*. |
The RpoFalcon512 naming is replaced with Falcon512Rpo across types, modules, procedures, and files.
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;- Rename
RpoFalcon512types and modules toFalcon512Rpoin imports and identifiers. - Update any file or procedure names that embed
rpo_falcon_512tofalcon_512_rpo.
| 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 procedures were renamed and moved under the normalized
miden::core::crypto::dsa::ecdsa_k256_keccak module.
MASM:
- 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- Update module paths from
std::crypto::dsa::ecdsa::secp256k1tomiden::core::crypto::dsa::ecdsa_k256_keccak. - Replace
verify_implcalls withverify_prehashwhere used directly. - If you referenced the ECDSA event name in host code, update it to
miden::core::crypto::dsa::ecdsa_k256_keccak::verify.
| 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 memory hashing helpers were renamed for consistency. These are MASM procedure name changes.
MASM:
- 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- Update module path to
miden::core::crypto::hashes::rpo256if you were usingrpo. - Replace the procedure names as shown above.
| Error Message | Cause | Solution |
|---|---|---|
unrecognized token "hash_memory_words" |
Procedure renamed. | Use hash_words. |
LibraryPath was removed; APIs now accept Path (from miden_assembly or std::path).
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)?;// After (0.20.x)
use miden_assembly::Path;
assembler.compile_and_statically_link_from_dir(dir, Path::new("miden::core::foo"))?;- Remove
LibraryPathusage and constructPathdirectly. - If you use both
std::path::Pathandmiden_assembly::Path, qualify imports to avoid ambiguity.
| Error Message | Cause | Solution |
|---|---|---|
error[E0432]: unresolved import miden_assembly::LibraryPath |
Type removed. | Use miden_assembly::Path. |
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.
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")?;// 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"))?;- Remove calls to
with_debug_mode,set_debug_mode, orin_debug_mode; the assembler always emits debug info now. - Update
compile_and_statically_link_from_dirto pass the directory first. - Replace
LibraryNamespaceusage withPath(e.g.,Path::KERNEL,Path::EXEC, or a string).
| 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). |