# Migration Guide
This guide explains how to update Rust code and project configuration written against the Miden
SDK (the `miden` crate) and compiler when upgrading between released versions. Each section covers
one release step and shows the concrete *before*/*after* edits a breaking change requires.
The most recent migration is at the top. When cutting a new release, add its migration section
directly below this paragraph, above the previous one (newest first, like the
[CHANGELOG](./CHANGELOG.md)).
## Unreleased
### Transaction summaries are six words (protocol 0.16)
Custom authentication components sign a commitment to the transaction summary. Protocol 0.16
extends the summary from four words to six: it now also binds the reference block commitment,
the transaction's expiration block delta, and seven user-defined parameters. The host rebuilds
the summary from the advice-map preimage and rejects the signing request unless its commitment
matches, so a component that still hashes the old four-word layout fails at runtime with
`TransactionSummaryConstructionFailed` even though it compiles unchanged.
Build the six words in this order and place the final nonce in the first user parameter, as the
standards components do:
Before:
```rust
let salt = Word::from([felt!(0), felt!(0), ref_block_num.into(), final_nonce.into()]);
let tx_summary = [acct_delta_commit, input_notes_commit, output_notes_commit, salt];
let msg: Word = hash_words(&tx_summary).into();
adv_insert(msg, &tx_summary);
```
After:
```rust
let block_commit = tx::get_block_commitment();
let expiration_delta = tx::get_expiration_block_delta();
// [expiration_delta, user_param0..2] and [user_param3..6]; the first user parameter carries
// the final nonce for replay protection.
let params_head = Word::from([expiration_delta.into(), final_nonce.into(), felt!(0), felt!(0)]);
let params_tail = Word::from([felt!(0), felt!(0), felt!(0), felt!(0)]);
let tx_summary = [
acct_delta_commit,
input_notes_commit,
output_notes_commit,
block_commit,
params_head,
params_tail,
];
let msg: Word = hash_words(&tx_summary).into();
adv_insert(msg, &tx_summary);
```
See `examples/auth-component-rpo-falcon512` for the complete component.
### Attachment setter aliases are removed
`output_note::set_word_attachment` and `output_note::set_array_attachment` are removed. They
were aliases from the protocol 0.15 rename, and their names promised replace semantics the
transaction kernel does not have: attachments are append-only.
Before:
```rust
output_note::set_word_attachment(note_idx, scheme, word);
output_note::set_array_attachment(note_idx, scheme, attachment_commitment);
```
After:
```rust
output_note::add_word_attachment(note_idx, scheme, word);
output_note::add_attachment(note_idx, scheme, attachment_commitment);
```
Both functions take a `Word`: `add_word_attachment` takes the attachment value itself, and
`add_attachment` takes a commitment to attachment elements that the advice map holds. If your
code holds the attachment contents as words in memory, call
`output_note::add_attachment_from_memory(note_idx, scheme, &words)` instead.
### `#[note]` reserves `get_entrypoint_root`
The `#[note]` macro now generates a `get_entrypoint_root()` associated method on the note type so
constructors can commit to the note script's MAST root. Rename any inherent item with that name,
including methods, `#[note_constructor]` methods, associated constants, and items declared in a
separate impl block.
### Contract crates gain a `build.rs` for IDE and plain-cargo builds
New projects created by `cargo miden new` include a small `build.rs` in each contract crate and a
build dependency on `miden-sdk-build-script-support` at the same version as the SDK. The script
delegates to [`prepare_package_cache`](https://docs.rs/miden-sdk-build-script-support/latest/miden_sdk_build_script_support/fn.prepare_package_cache.html),
which makes plain `cargo check`, `cargo build`, and IDE analysis (rust-analyzer) resolve compiled
dependency packages: outside a `cargo miden build`, it stages package-cache
generations under its `OUT_DIR`, fills a private generation with a nested
`cargo miden build --release` that adopts it through `MIDENC_PACKAGE_CACHE`, and exports the
same variable to the crate's macro expansion. Inside a midenc-driven build the script does
nothing: the compiler exchanges packages with its nested builds through a directory of its
own, which it deletes when the build ends.
The nested build runs whenever Cargo re-runs the script. The compiler writes a versioned input
record next to the staged packages. Inputs a frontend can enumerate completely — local MASM
source trees and preassembled packages, for example — become selective Cargo change directives
when the cargo-miden launcher and Miden workspace boundary are also explicit. Rust/Cargo source
dependencies, launchers resolved through `PATH`, and packages whose future parent workspace
cannot be watched completely are marked opaque; those graphs deliberately re-stage dependencies
on every relevant Cargo invocation instead of risking stale metadata. Set `CARGO_MIDEN` to an
explicit binary path and build from an established Miden workspace to enable selective behavior
for an otherwise-complete dependency graph.
The build script is now required for plain cargo builds of crates with Miden source
dependencies. The SDK macros read dependency packages only from the `MIDENC_PACKAGE_CACHE`
directory (or from a manifest path naming a `.masp` file directly); they no longer search
`target/miden/<profile>` output directories, so a plain `cargo check` without the script fails
with instructions instead of finding previously built artifacts. Add the support crate and the
following wrapper to every contract crate (using the same version requirement as `miden`):
```toml
[build-dependencies]
miden-sdk-build-script-support = "0.14.0-rc.1"
```
```rust
fn main() {
miden_sdk_build_script_support::prepare_package_cache();
}
```
The helper needs `cargo miden` on `PATH`; set the `CARGO_MIDEN` environment variable to use a
specific `cargo-miden` binary instead. A missing tool fails the build script with an install
hint.
Each successful staging run publishes an immutable content-addressed generation. Changed
generations remain until Cargo removes `OUT_DIR`, so an IDE expansion that still references an
older path continues to see one coherent dependency set; byte-identical staging reuses the
existing generation. If the nested `cargo miden build` fails, the outer check fails with its error
rather than compiling against stale dependency metadata.
One sharing caveat: cargo keys build-script output by crate name and version, not by project
path. Two different projects that contain a contract crate with the same package name and
version and share one `CARGO_TARGET_DIR` reuse each other's build-script output — including
the staged package cache. Use per-checkout target directories for such layouts.
### Kernel scalars are typed instead of `Felt` (counts, block heights, nonces, attachments)
Binding surfaces whose values are counts now return `u32`: `tx::get_num_input_notes`,
`tx::get_num_output_notes`, `active_account::get_num_procedures`, and the
`num_assets` / `num_storage_items` fields of the note info structs. Matching this,
`active_account::get_procedure_root` takes the procedure index as `u32` (previously `u8`), so
count-driven loops index directly. Code that compared or computed with these as felts now works
with plain integers:
```rust
// before
let all_consumed: Felt = tx::get_num_input_notes();
assert_eq!(all_consumed, felt!(2));
// after
let all_consumed: u32 = tx::get_num_input_notes();
assert_eq!(all_consumed, 2);
```
Block heights are wrapped in the new `BlockNumber` type (comparable as integers; heights read
from note storage convert with the validated `BlockNumber::try_from(felt)`), block timestamps
are `u32` seconds, and expiration deltas are `u16`:
```rust
// before
let timelock_height: Felt = inputs[3];
assert!(tx::get_block_number() >= timelock_height);
tx::update_expiration_block_delta(Felt::new(42).unwrap());
// after
let timelock_height = BlockNumber::try_from(inputs[3]).unwrap();
assert!(tx::get_block_number() >= timelock_height);
tx::update_expiration_block_delta(42);
```
Account nonces are wrapped in the new `Nonce` type (comparable as integers; use
`as_felt()`/`as_u64()` or `Felt::from(nonce)` where the raw value is needed, e.g. when packing a
nonce into a `Word` — `ref_block_num` below is a `BlockNumber` from `tx::get_block_number()` and
converts the same way):
```rust
// before
let final_nonce: Felt = self.incr_nonce();
let params = Word::from([felt!(0), felt!(0), ref_block_num, final_nonce]);
// after
let final_nonce: Nonce = self.incr_nonce();
let params = Word::from([felt!(0), felt!(0), ref_block_num.into(), final_nonce.into()]);
```
This example shows only the type conversions. Do not reuse the word layout: protocol 0.16
replaces the four-word transaction summary that packed the nonce this way — see
[Transaction summaries are six words (protocol 0.16)](#transaction-summaries-are-six-words-protocol-016)
at the top of this guide for the required six-word layout.
Attachment lookups return `Option<u32>` instead of the removed `AttachmentLocation` struct, and
attachment indexes are passed as `u32`:
```rust
// before
let location = active_note::find_attachment(scheme);
if location.found() {
let attachment = active_note::write_attachment_to_memory(location.index);
}
// after
if let Some(index) = active_note::find_attachment(scheme) {
let attachment = active_note::write_attachment_to_memory(index);
}
```
### Typed transaction-script arguments
`#[tx_script]` entrypoints now receive typed script arguments decoded from the `TX_SCRIPT_ARGS`
word. Existing `fn run(arg: Word, ...)` entrypoints keep compiling and behave unchanged (`Word`
decodes as itself), so no edit is required — but scripts that manually fetch their arguments from
the advice provider can delete that plumbing: declare a struct deriving
`FromFeltRepr`/`ToFeltRepr` and take it by value. The entrypoint may have any name and accept
the script-args and account parameters in any order (the account reference is optional);
encodings of at most 4 felts
travel in the args word directly, longer or variable-length ones travel through the advice
provider, hash-verified against the args word. The derives' generated code refers to the
`miden_field_repr` crate, which the `miden` crate re-exports under its own name: `use miden::*;`
makes it resolve. A module that does not glob-import `miden` needs `use miden::miden_field_repr;`
(or a direct `miden-field-repr` dependency) alongside the struct.
Before:
```rust
use miden::{intrinsics::advice::adv_push_mapvaln, *};
#[tx_script]
fn run(arg: Word, account: &mut Wallet) {
let num_felts = adv_push_mapvaln(arg);
let num_felts_u64 = num_felts.as_canonical_u64();
assert_eq(Felt::from_u32((num_felts_u64 % 4) as u32), felt!(0));
let num_words = Felt::new(num_felts_u64 / 4).unwrap();
let input = adv_load_preimage(num_words, arg);
let tag = input[0];
let note_type = input[1];
let recipient: [Felt; 4] = input[2..6].try_into().unwrap();
let note_idx = account.create_note(tag.into(), note_type.into(), recipient.into());
// ...
}
```
After:
```rust
use miden::*;
/// Arguments of the transaction script, transported via the `TX_SCRIPT_ARGS` word.
#[derive(FromFeltRepr, ToFeltRepr)]
pub struct TxScriptArgs {
pub tag: Tag,
pub note_type: NoteType,
pub recipient: Recipient,
}
#[tx_script]
fn run(args: TxScriptArgs, account: &mut Wallet) {
let note_idx = account.create_note(args.tag, args.note_type, args.recipient);
// ...
}
```
On the host, build the transaction with `ScriptArgs::encode` on a mirror struct that reproduces
the guest type's *felt-repr wire sequence* — the felts, in order; the Rust fields may differ, e.g.
a guest `Asset` field can be mirrored as its key and value `Word`s (sharing the schema through
the Miden package is planned). Pin the mirror against drift: assert its
`<Mirror as ScriptArgs>::FIXED_LEN` equals the guest type's, and ideally assert a golden encoding
with distinct sentinel values, which also catches same-length field reorders. This matters
because one drift direction fails silently: if the guest type is word mode and the mirror grows
past 4 felts, the host switches to commitment mode and the guest decodes the hash limbs as
argument values. The trait lives in the `miden-tx-script-args` crate, whose off-chain
dependencies are only `miden-field` and `miden-field-repr` — host code does not need the
on-chain SDK.
`EncodedScriptArgs` carries `miden-field` felts, while the protocol crates use their own felt
type — convert between them via `as_canonical_u64`:
```rust
/// Converts `miden-field` felts into the protocol crate's felt type.
fn to_protocol_felts(felts: &[miden_field::Felt]) -> Vec<Felt> {
felts.iter().map(|felt| Felt::new_unchecked(felt.as_canonical_u64())).collect()
}
match script_args.encode() {
// Word mode: the encoding is the script-args word; no advice map involved.
EncodedScriptArgs::Word(word) => builder.tx_script_args(Word::new(
[0, 1, 2, 3].map(|i| Felt::new_unchecked(word[i].as_canonical_u64())),
)),
// Commitment mode: hash the preimage into the script-args word and register the
// advice-map entry (Poseidon2 is the Miden VM's native hash).
EncodedScriptArgs::Preimage(felts) => {
let preimage = to_protocol_felts(&felts);
let args_word = Poseidon2::hash_elements(&preimage);
builder.tx_script_args(args_word).extend_advice_map([(args_word, preimage)])
}
}
```
### Mark account interface procedures with `#[account_procedure]`
A component's methods are no longer implicitly part of the account interface. Mark every method that
must be callable as an account procedure — from transaction scripts, notes, foreign procedure
invocation (FPI), or sibling components — with `#[account_procedure]` on the `#[component]` **trait**
declaration (not the `impl`). Any number of methods may be marked. Unmarked methods still compile
and are still exported, but are not account procedures.
`#[auth_script]` and `#[account_procedure]` belong to different component kinds — an authentication
component versus a regular account component — and cannot be combined in one component. An
authentication component keeps using `#[auth_script]` alone (its method is the account interface
implicitly).
The `#[account_procedure]` marker is recognized by the surrounding `#[component]` macro, so it needs
no import (just like `#[auth_script]`).
Before:
```rust
use miden::{Asset, component, component_storage};
#[component]
trait BasicWallet {
fn receive_asset(&mut self, asset: Asset);
}
```
After:
```rust
use miden::{Asset, component, component_storage};
#[component]
trait BasicWallet {
#[account_procedure]
fn receive_asset(&mut self, asset: Asset);
}
```
### `#[account(...)]` generates one trait per component
In 0.13 the `#[account(...)]` macro generated each component's methods as inherent methods on the
wrapper struct. In 0.14 it generates **one trait per referenced interface** (named after the
interface, with the wrapper's visibility) and implements it for the wrapper, so two components that
export the same method name can coexist on one wrapper. Single-component accounts keep calling
`account.method(..)` unchanged **when the generated trait is in scope** — a same-module
`#[note]`/`#[tx_script]` entrypoint sees it automatically, but a call site in a different module
than the wrapper needs `use` of the generated trait (e.g. `use crate::BasicWallet;`).
Because the methods are now on a generated trait, a referenced interface must export at least one
method: `#[account(...)]` now errors if a selected interface has no callable exports, where 0.13
silently generated nothing for it.
**The wrapper struct must be named differently from every generated trait.**
`#[account(counter_contract::CounterContract)] struct CounterContract;` no longer compiles; rename
the struct (e.g. `Counter`):
```rust
#[account(counter_contract::CounterContract)]
struct Counter;
let counter = Counter::new(counter_account_id);
let count = counter.get_count();
```
**Shared method names are disambiguated with UFCS.** When an account derives two components that
export the same method name — or a component method shares a name with an `ActiveAccount` built-in
such as `get_id` — the bare call is ambiguous:
```rust
#[account(basic_wallet::BasicWallet, vault::Vault)]
struct Wallet;
// both BasicWallet and Vault export `deposit`:
<Wallet as BasicWallet>::deposit(account, asset);
<Wallet as Vault>::deposit(account, asset);
```
Generated component traits are same-module, so `<Wallet as Interface>::…` needs no import. The
`ActiveAccount` built-in trait, however, is not in the `miden::*` prelude, so disambiguating a
component method against a built-in needs an explicit import:
```rust
use miden::active_account::ActiveAccount;
// a component method named `get_id` shares the `ActiveAccount::get_id` name:
<Wallet as CounterContract>::get_id(account); // the component method
<Wallet as ActiveAccount>::get_id(account); // the built-in
```
For the same reason, a component method named `new` is now permitted (it was previously rejected):
it lives on the generated trait and coexists with the inherent `Wallet::new(account_id)`
constructor — `Wallet::new(id)` resolves to the constructor, `wallet.new()` to the component method.
**Name clashes between generated traits are resolved with `as`.** When the generated trait *name*
would clash — the struct shares the interface name, two packages export the same interface name,
two separate `#[account]` wrappers in one module select the same interface, or the crate already
uses the interface as a sibling `#[component(...)]` — rename the generated trait with `as` (the path
still selects the interface):
```rust
// a component that both calls a sibling counter and reaches a remote counter through FPI:
#[account(counter_contract::CounterContract as RemoteCounter)] // FPI trait `RemoteCounter`
struct Remote;
#[component(counter_contract::CounterContract)] // sibling trait `CounterContract`
trait Caller: NativeAccount + CounterContract { /* ... */ }
```
### Tx-kernel bindings: protocol 0.16.0-beta.1
The SDK bindings were aligned with VM v0.25 / protocol 0.16.0-beta.1. Several tx-kernel bindings
were renamed, moved, or removed to match the protocol procedures.
**The "initial asset" getters are renamed.** They read the note's assets at creation time,
unaffected by any in-transaction removal, so the names now say so:
```rust
// before // after
active_note::get_assets(); active_note::get_initial_assets();
input_note::get_assets(idx); input_note::get_initial_assets(idx);
input_note::get_assets_info(idx); input_note::get_initial_assets_info(idx);
```
**`active_account::has_non_fungible_asset` became `has_asset`** and now takes an asset id `Word`
instead of an `Asset`, testing membership of any asset (fungible or non-fungible) by id:
```rust
// before
if account.has_non_fungible_asset(asset) { /* ... */ }
// after
if account.has_asset(asset.key) { /* ... */ }
```
**The initial-state getters moved from `active_account` to `native_account`.**
`get_initial_commitment`, `get_initial_storage_commitment`, `get_initial_vault_root`, and
`get_initial_asset` are now free functions on `native_account` (they are no longer `ActiveAccount`
trait methods). `storage::{get_initial_item, get_initial_map_item}` keep their API but now read the
native account's initial state.
```rust
// before (ActiveAccount trait method)
let init = self.get_initial_commitment();
let asset = self.get_initial_asset(asset_key);
// after (native_account free function)
let init = miden::native_account::get_initial_commitment();
let asset = miden::native_account::get_initial_asset(asset_key);
```
**In-transaction asset construction and balance getters were removed.**
- `active_account::{get_balance, get_initial_balance}` are gone. Read the asset value word with
`active_account::get_asset` (or `native_account::get_initial_asset`) and extract the fungible
amount from the returned `AssetValue` word (see the protocol `fungible_value_into_amount`
helper).
- `faucet::{create_fungible_asset, create_non_fungible_asset, has_callbacks}` and the whole
`asset` module (`asset::{create_fungible_asset, create_non_fungible_asset}`) are gone. The kernel
no longer exposes in-transaction asset construction; `faucet::{mint, burn}` take a pre-built
`Asset`.
**`output_note::create` is account-context only.** It can now only be called from account-component
context (runtime-enforced). Tx/note scripts must create notes through an account component wrapper
method (see the `basic-wallet` example's `create_note`).
### `#[note]` structs now also implement `ToFeltRepr`
`#[note]` on a struct now generates a `felt_repr::ToFeltRepr` impl encoding the note inputs in
field order (mirroring the generated `TryFrom<&[Felt]>` decoding), so note constructors can
serialize the inputs when computing the note recipient.
Both directions are generated by the macro — do not add `FromFeltRepr`/`ToFeltRepr` derives to
the note struct itself. For the typical note struct, whose fields are SDK-provided types
(`Felt`, `AccountId`, primitives, ...), upgrading requires no changes.
Changes are only required if:
1. The note type has a manual `ToFeltRepr` impl: remove it — it now conflicts with the
generated impl. If its encoding differed from the generated field-order encoding, move the
logic to a differently named helper method instead.
2. A field has a custom type that only implements `FromFeltRepr`: that field type (not the note
struct) must now also implement `ToFeltRepr`, e.g. via `#[derive(ToFeltRepr)]` next to its
existing `FromFeltRepr`.
### Component WIT is embedded in the compiled package
The component WIT generated by `#[component]` is now embedded in the compiled Miden package (a
`wit` section of the `.masp`) instead of being written to `target/generated-wit/`. The
`#[account(...)]`, sibling `#[component(pkg::Interface)]`, `#[note]`, and `#[tx_script]` macros
read dependency WIT from the dependency's compiled `.masp`, and every Miden path dependency must
be a built package (cargo-miden builds path dependencies automatically; a dependency that points
directly at a prebuilt `.masp` file is self-contained).
Remove the `wit = "..."` entries from `[package.metadata.miden.dependencies]` in
`miden-project.toml` for dependencies whose packages embed WIT — a leftover key is now an error
("remove the `wit` key"). The key remains available as an escape hatch for dependency packages
*without* embedded WIT (e.g. produced by another toolchain); it must point at a single
self-contained `.wit` file, or a directory containing exactly one top-level `.wit` file.
Before:
```toml
[dependencies]
basic-wallet = { path = "../basic-wallet" }
[package.metadata.miden.dependencies]
basic-wallet = { wit = "../basic-wallet/target/generated-wit/" }
```
After:
```toml
[dependencies]
basic-wallet = { path = "../basic-wallet" }
```
A `.masp` built by an older SDK has no embedded WIT: the macros skip it, and a macro that
references it reports the missing interface at the reference site; rebuild each dependency with
the current toolchain (`cargo miden build`), or supply the WIT manually via the `wit` key as
above. Components written
without the `#[component]` macro — a hand-written `wit/` directory and a bare
`miden::generate!()` — embed their WIT automatically when the `wit/` directory contains a single
`.wit` file, so no changes are needed there. WIT split across multiple files, or referencing
packages under `wit/deps/` other than the bundled SDK WIT, cannot be embedded verbatim yet;
consolidate it into one self-contained file if the component is consumed as a Miden dependency.
Note for the editor workflow: previously, `cargo check` (or rust-analyzer) of the dependency crate
regenerated its WIT under `target/generated-wit` as a macro side effect. Dependency WIT now comes
from the compiled package. A consumer crate carrying the contract `build.rs` (see "Contract
crates gain a `build.rs`" above) rebuilds its dependencies automatically; without the script,
run `cargo miden build` for the dependency once (and again after changing its interface) before
checking a dependent crate.
## 0.13.0 -> 0.13.1
### `*_note::get_metadata` returns a single-word `NoteMetadata`
`get_metadata` no longer includes the note attachment word — it returns only the metadata header,
and `NoteMetadata` is now a single-field struct `{ header: Word }`. Retrieve attachments through the
dedicated attachment procedures instead.
```rust
// before — NoteMetadata { attachment: Word, header: Word }
let meta = active_note::get_metadata();
let attachment = meta.attachment;
let header = meta.header;
// after — NoteMetadata { header: Word }
let meta = active_note::get_metadata();
let header = meta.header;
// attachments are now retrieved separately:
let attachments_commitment = active_note::get_attachments_commitment();
```
## 0.12.0 -> 0.13.0
This release reworks how account components, accounts, and authentication are declared, introduces
a required `miden-project.toml` project manifest, and aligns the tx-kernel bindings with
VM v0.23 / protocol v0.15. The macro changes touch every account component, every authentication
component, and every note/tx-script that references an account. Work through the sections in order:
rewrite the component (1), add the project manifest (2), update account references (3), then the
bindings (4).
### 1. Account and authentication components: `#[component]` is now a trait + a storage struct
`#[component]` no longer applies to a `struct` or an inherent `impl`. An account component is now
three pieces:
1. a `#[component_storage]` struct holding the `#[storage(...)]` fields,
2. a `#[component]` **trait** declaring the API (the trait name yields the WIT interface), and
3. a `#[component] impl Trait for Storage` block providing the behavior.
Method receivers (`&self` / `&mut self`) and method bodies are unchanged.
Before (0.12.0):
```rust
use miden::{component, felt, Felt, StorageMap, Word};
#[component]
struct CounterContract {
#[storage(description = "counter contract storage map")]
count_map: StorageMap<Word, Felt>,
}
#[component]
impl CounterContract {
pub fn get_count(&self) -> Felt {
let key = Word::new([felt!(0), felt!(0), felt!(0), felt!(1)]);
self.count_map.get(key)
}
pub fn increment_count(&mut self) -> Felt {
let key = Word::new([felt!(0), felt!(0), felt!(0), felt!(1)]);
let new_value = self.count_map.get(key) + felt!(1);
self.count_map.set(key, new_value);
new_value
}
}
```
After (0.13.0):
```rust
use miden::{component, component_storage, felt, Felt, StorageMap, Word};
// 1. storage fields move to a `#[component_storage]` struct
#[component_storage]
struct CounterContractStorage {
#[storage(description = "counter contract storage map")]
count_map: StorageMap<Word, Felt>,
}
// 2. the API becomes a `#[component]` trait (its name is the WIT interface)
#[component]
trait CounterContract {
fn get_count(&self) -> Felt;
fn increment_count(&mut self) -> Felt;
}
// 3. the behavior is a `#[component] impl Trait for Storage` block
#[component]
impl CounterContract for CounterContractStorage {
fn get_count(&self) -> Felt {
let key = Word::new([felt!(0), felt!(0), felt!(0), felt!(1)]);
self.count_map.get(key)
}
fn increment_count(&mut self) -> Felt {
let key = Word::new([felt!(0), felt!(0), felt!(0), felt!(1)]);
let new_value = self.count_map.get(key) + felt!(1);
self.count_map.set(key, new_value);
new_value
}
}
```
**Authentication components migrate the same way.** `#[auth_script]` was already required in
0.12.0; in 0.13.0 it simply moves onto the trait method declaration (the `impl` method no longer
repeats it):
```rust
// before (0.12.0): inherent impl
#[component]
struct AuthComponent;
#[component]
impl AuthComponent {
#[auth_script]
pub fn auth_procedure(&mut self, _arg: Word) { /* ... */ }
}
// after (0.13.0): trait + storage, `#[auth_script]` on the trait method
#[component_storage]
struct AuthComponentStorage;
#[component]
trait AuthComponent {
#[auth_script]
fn auth_procedure(&mut self, _arg: Word);
}
#[component]
impl AuthComponent for AuthComponentStorage {
fn auth_procedure(&mut self, _arg: Word) { /* ... */ }
}
```
### 2. `miden-project.toml` is now a required file
0.13.0 introduces a dedicated project manifest, `miden-project.toml`, placed next to `Cargo.toml`
at the crate root. The Miden-specific configuration that previously lived in `Cargo.toml`
`[package.metadata.*]` now lives here, and the proc-macros read it to resolve the WIT interface
name, the project kind, and any FPI/sibling dependencies. **Building a 0.13.0 project without it
fails** (for components, with an undefined `::init` link error).
Create `miden-project.toml` like this (account component without dependencies):
```toml
[package]
name = "counter-contract" # crate name; kebab-case
version = "0.1.0" # project version; supplies the WIT `@version`
[lib]
# <package> = the kebab-cased [package].name
# <interface> = the kebab-cased `#[component]` trait name (here: `CounterContract`)
# <version> = the [package].version above
namespace = "miden:counter-contract/counter-contract@0.1.0"
[dependencies]
miden-core = "*"
miden-protocol = "*"
# account components only: which account types may host this component
[package.metadata.miden]
supported-types = ["RegularAccountUpdatableCode"]
```
Walking through the fields:
- **`[package]`** — `name` and `version`. The version feeds the `@version` suffix of the WIT id,
so bumping it changes the component's interface id.
- **`[lib].kind`** — the project kind: `account-component`, `note`, or `tx-script`.
- **`[lib].namespace`** — the full `miden:<package>/<interface>@<version>` WIT id. The
**interface segment must equal the kebab-cased `#[component]` trait name**; a mismatch fails to
link with an undefined `::init`. (For `note`/`tx-script` projects, the interface segment is the
project's own name rather than a component trait.)
- **`[dependencies]`** — the Miden crates the project links against (`miden-core`,
`miden-protocol`), plus any FPI/sibling dependency packages by `path` (see section 3).
- **`[package.metadata.miden].supported-types`** — account components only.
> **Storage-slot caution.** Storage slot names derive from the `[lib].namespace` interface
> segment (which mirrors the component trait name), and slot names feed `StorageSlotId` derivation.
> Renaming the component trait (and updating `[lib].namespace` to match) **re-keys the storage
> slot ids of an already-deployed component**. Keep the trait name stable across upgrades of a
> live component.
For a project that calls another account/component (FPI or sibling), add the dependency in both
`[dependencies]` (the package) and `[package.metadata.miden.dependencies]` (its generated WIT):
```toml
[dependencies]
miden-core = "*"
miden-protocol = "*"
basic-wallet = { path = "../basic-wallet" }
# the dependency's generated WIT, used to generate the call bindings
[package.metadata.miden.dependencies]
basic-wallet = { wit = "../basic-wallet/target/generated-wit/" }
```
The `[package.metadata.miden.dependencies].<name>.wit` entry is what the macros read to generate
the typed call bindings, and it is the **same entry used by note scripts, by account components
doing FPI, and by sibling component calls**.
### 3. Accounts: declare `#[account(...)]` explicitly with an interface
The auto-generated `crate::bindings::Account` struct is gone. Declare the account explicitly with
`#[account(...)]` and use that type as the note/tx-script entrypoint account parameter. The
dependency reference now **requires the exported WIT interface** (kebab-cased and validated):
write `#[account(basic_wallet::BasicWallet)]`, not `#[account(basic_wallet)]`.
Before (0.12.0):
```rust
use miden::{active_note, note, AccountId, Word};
use crate::bindings::Account; // auto-generated
#[note]
struct P2idNote {
target_account_id: AccountId,
}
#[note]
impl P2idNote {
#[note_script]
pub fn script(self, _arg: Word, account: &mut Account) {
for asset in active_note::get_assets() {
account.receive_asset(asset);
}
}
}
```
After (0.13.0):
```rust
use miden::{account, active_note, note, AccountId, Word};
// declare the native account explicitly; pick the package's WIT interface
#[account(basic_wallet::BasicWallet)]
pub struct Wallet;
#[note]
struct P2idNote {
target_account_id: AccountId,
}
#[note]
impl P2idNote {
#[note_script]
pub fn script(self, _arg: Word, account: &mut Wallet) {
for asset in active_note::get_assets() {
account.receive_asset(asset);
}
}
}
```
The same `#[account(...)]` type serves two roles. Passed to a `#[note]`/`#[tx_script]` entrypoint
it is the transaction's native (active) account. Constructed with `new(account_id)` it is a
**foreign account caller**, whose method calls are routed through `execute_foreign_procedure`
(FPI):
```rust
let counter = CounterContract::new(counter_account_id);
let count = counter.get_count();
```
**FPI is not limited to note/tx scripts — an account component can call another account through
FPI too.** Declare the `#[account(...)]` wrapper in the component crate (and the dependency in
`miden-project.toml`, section 2) and use it from inside the `#[component] impl`:
```rust
#[account(callee_account::CounterContract)]
struct CalleeAccount;
#[component]
impl CallerAccount for CallerAccountStorage {
fn read_foreign_count(&self, callee_account_id: AccountId) -> Felt {
let callee = CalleeAccount::new(callee_account_id);
callee.get_count(key)
}
}
```
### 4. Tx-kernel bindings: protocol v0.15
The SDK bindings were aligned with VM v0.23 / protocol v0.15 (`miden-field` bumped to `^0.25`).
- **`Felt::new` is now fallible** — it returns `Result<Felt, _>` instead of `Felt`. Replace
`Felt::new(x)` with `Felt::new(x).unwrap()` (or handle the error). The `felt!(x)` macro is
unchanged and remains the preferred constructor for literals.
- **`asset::{create_fungible_asset, create_non_fungible_asset}`** now take a trailing
`enable_callbacks: bool` argument.
- **`active_account::{get_balance, get_initial_balance}`** (and the corresponding `ActiveAccount`
trait methods) now take an asset key `Word` instead of a faucet `AccountId`.
- **`faucet::{mint, burn}`** no longer return an `Asset`; the `faucet::{mint_value, burn_value}`
helpers were removed. Use the returned value-free API to match the tx kernel.
- **`output_note::set_attachment` was removed.** The attachment shape is selected by function
instead of a runtime `attachment_kind` argument:
```rust
output_note::add_word_attachment(note_idx, scheme, attachment);
```
> **Storage encoding note.** Scalar `Felt` values stored in `StorageValue<Felt>` /
> `StorageMap<_, Felt>` are now packed into the low word limb (`[v, 0, 0, 0]`) instead of the high
> limb (`[0, 0, 0, v]`), matching protocol v0.15. This is transparent when you recompile and
> redeploy, but state written by 0.12.0 code is read back differently by 0.13.0 code.
### New in 0.13.0 (no migration required)
- **Sibling component calls**: `#[component(package::Interface, ...)]` on the component trait lets
one component call another component deployed on the same account. See the
[CHANGELOG](./CHANGELOG.md) for details.
- **`println!`** macro (and `debug::println`) for emitting a debug message during execution.