Skip to main content
Version: 0.13

Cross-Component Calls

Miden components can call each other's methods. Since accounts can have multiple components (e.g., wallet + auth + custom logic), those components need to communicate. Note scripts also need to call methods on the account's components to transfer assets.

How it works

When you build a component with miden build, the compiler generates an interface describing its public methods. Other projects can import this interface to call those methods.

counter-contract (component)
→ generates interface
→ counter-note imports the interface
→ calls counter_contract::get_count()

The simplest way to make cross-component calls from note scripts is through the #[note] macro with an Account parameter:

use crate::bindings::Account;
use miden::{active_note, Asset};

#[note]
struct P2idNote;

#[note]
impl P2idNote {
#[note_script]
pub fn run(self, _arg: Word, account: &mut Account) {
// Iterate over the note's assets and transfer each to the account
for asset in active_note::get_assets() {
account.receive_asset(asset);
}
}
}

The _arg: Word parameter is the note's first input Word, passed automatically when the note is consumed. It's unused in this example (prefixed with _), but note scripts can use it for recipient-specific data like expected account IDs or amounts.

The Account type is auto-generated from the bindings of the dependent component. Its methods correspond to the component's public methods.

Using generate!() directly

For more control (e.g., calling multiple components), use miden::generate!() to manually generate bindings:

cross-ctx-note/src/lib.rs
#![no_std]
#![feature(alloc_error_handler)]

#[global_allocator]
static ALLOC: miden::BumpAlloc = miden::BumpAlloc::new();

use miden::*;

miden::generate!();
bindings::export!(MyNote);

use bindings::{
exports::miden::base::note_script::Guest,
miden::cross_ctx_account::foo::process_felt,
};

struct MyNote;

impl Guest for MyNote {
fn run(_arg: Word) {
let input = Felt::from_u32(11);
let output = process_felt(input);
assert_eq!(output, felt!(53));
}
}

Key points:

  • miden::generate!() generates the bindings module from component interfaces
  • bindings::export!(MyNote) registers MyNote as the implementation
  • process_felt is imported from the cross-ctx-account component
  • The import path follows the package structure: bindings::miden::cross_ctx_account::foo::process_felt

Cargo.toml configuration

Cross-component calls require two dependency declarations:

1. Miden dependencies (for cargo-miden linking)

[package.metadata.miden.dependencies]
"miden:basic-wallet" = { path = "../basic-wallet" }

This tells cargo-miden where to find the component for linking.

2. Component dependencies (for binding generation)

[package.metadata.component.target.dependencies]
"miden:basic-wallet" = { path = "../basic-wallet/target/generated-wit/" }

This points to the generated interface files used to create Rust bindings.

Complete example

counter-note/Cargo.toml
[package]
name = "counter-note"
version = "0.1.0"
edition = "2024"

[lib]
crate-type = ["cdylib"]

[dependencies]
miden = { path = "../../sdk/sdk" }

[package.metadata.miden]
project-kind = "note-script"

[package.metadata.component]
package = "miden:counter-note"

# Miden dependency — the component we want to call
[package.metadata.miden.dependencies]
"miden:counter-contract" = { path = "../counter-contract" }

# Component dependency — generated interface for binding generation
[package.metadata.component.target.dependencies]
"miden:counter-account" = { path = "../counter-contract/target/generated-wit/" }

The dependent component must be built first so its interface files exist. Build counter-contract before building counter-note.

Example: Counter note calling counter contract

counter-note/src/lib.rs
#![no_std]
#![feature(alloc_error_handler)]

use miden::*;

// Import the counter contract's generated bindings
use crate::bindings::miden::counter_contract::counter_contract;

#[note]
struct CounterNote;

#[note]
impl CounterNote {
#[note_script]
pub fn run(self, _arg: Word) {
// Call get_count() on the counter contract component
let initial_value = counter_contract::get_count();

// Call increment_count() — modifies the account's storage
counter_contract::increment_count();

// Verify the count increased
let expected_value = initial_value + Felt::from_u32(1);
let final_value = counter_contract::get_count();
assert_eq!(final_value, expected_value);
}
}

When to use which pattern

PatternUse when
#[note] with &mut AccountNote needs to call a single account component's methods
generate!() + Guest traitNote needs to call multiple components or needs full control
Direct module importsCalling specific functions from a known component interface

Full API docs on docs.rs: miden (generate!() macro)