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