Part 5: Cross-Component Calls
In this section, you'll learn how note scripts call methods on account components. We'll explore the generated bindings system and the dependency configuration that makes the deposit note work.
What You'll Learn in This Part
By the end of this section, you will have:
- Understood how bindings are generated and imported
- Learned the dependency configuration in
Cargo.toml - Explored the WIT interface files
- Verified cross-component calls work via the deposit flow
Building on Part 4
In Part 4, you wrote bank_account::deposit(depositor, asset) in the deposit note. But how does that call actually work? This part explains the binding system:
┌────────────────────────────────────────────────────────────┐
│ How Bindings Work │
├────────────────────────────────────────────────────────────┤
│ │
│ bank-account/ │
│ └── src/lib.rs miden build │
│ pub fn deposit() ─────────────▶ generated-wit/ │
│ pub fn withdraw() miden_bank-account.wit
│ │
│ ┌───────────────────────────┐ │
│ ▼ │ │
│ deposit-note/ │ │
│ └── src/lib.rs │ │
│ use crate::bindings::miden::bank_account::bank_account;
│ bank_account::deposit(...) ◄───── calls via binding │
│ │
└────────────────────────────────────────────────────────────┘
The Bindings System
When you build an account component with miden build, it generates:
- MASM code - The compiled contract logic
- WIT files - WebAssembly Interface Type definitions
Other contracts (note scripts, transaction scripts) import these WIT files to call the account's methods.
Build Flow:
┌──────────────────┐ miden build ┌─────────────────────────────────┐
│ bank-account/ │ ─────────────────▶│ target/generated-wit/ │
│ src/lib.rs │ │ miden_bank-account.wit │
│ │ │ miden_bank-account_world.wit │
└──────────────────┘ └─────────────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ deposit-note/ │
│ imports generated bindings │
└─────────────────────────────────┘
Importing Bindings
In your note script, import the generated bindings:
// Import the bank account's generated bindings
use crate::bindings::miden::bank_account::bank_account;
The import path follows this pattern:
crate::bindings::{package-prefix}::{component-name}::{interface-name}
For our bank:
miden- The package prefix from[package.metadata.component]bank_account- The component name (derived from package name with underscores)bank_account- The interface name (same as component)
Calling Account Methods
Once imported, call the account methods directly:
#[note_script]
fn run(_arg: Word) {
let depositor = active_note::get_sender();
let assets = active_note::get_assets();
for asset in assets {
// Call the bank account's deposit method
bank_account::deposit(depositor, asset);
}
}
The binding automatically handles:
- Marshalling arguments across the component boundary
- Invoking the correct MASM procedures
- Returning results back to the caller
Configuring Dependencies
Your Cargo.toml needs two dependency sections:
[package.metadata.miden.dependencies]
"miden:bank-account" = { path = "../bank-account" }
[package.metadata.component.target.dependencies]
"miden:bank-account" = { path = "../bank-account/target/generated-wit/" }
miden.dependencies
[package.metadata.miden.dependencies]
"miden:bank-account" = { path = "../bank-account" }
This tells cargo-miden where to find the source package. Used during the build process to:
- Verify interface compatibility
- Link the compiled MASM code
component.target.dependencies
[package.metadata.component.target.dependencies]
"miden:bank-account" = { path = "../bank-account/target/generated-wit/" }
This tells the Rust compiler where to find the WIT interface files. The path points to the generated-wit/ directory created when you built the account component.
If either section is missing, your build will fail with linking or interface errors.
Build Order
Components must be built in dependency order:
# 1. Build the account component first
cd contracts/bank-account
miden build
# 2. Then build note scripts that depend on it
cd ../deposit-note
miden build
If you build out of order, you'll see errors about missing WIT files.
What Methods Are Available?
Only public methods (pub fn) on the #[component] impl block are available through bindings:
#[component]
impl Bank {
// PUBLIC: Available through bindings
pub fn deposit(&mut self, depositor: AccountId, deposit_asset: Asset) { ... }
pub fn withdraw(&mut self, /* ... */) { ... }
pub fn get_balance(&self, depositor: AccountId) -> Felt { ... }
pub fn initialize(&mut self) { ... }
// PRIVATE: NOT available through bindings
fn require_initialized(&self) { ... }
fn create_p2id_note(&mut self, /* ... */) { ... }
}
Understanding the Generated WIT
The WIT files describe the interface. Here's a simplified example:
interface bank-account {
use miden:types/types.{account-id, asset, felt, word};
initialize: func();
deposit: func(depositor: account-id, deposit-asset: asset);
withdraw: func(depositor: account-id, withdraw-asset: asset, ...);
get-balance: func(depositor: account-id) -> felt;
}
This WIT is used to generate the Rust bindings that appear in crate::bindings.
Transaction Script Bindings (Preview)
Transaction scripts use a slightly different import pattern:
use crate::bindings::Account;
#[tx_script]
fn run(_arg: Word, account: &mut Account) {
// The account parameter IS the bound component
account.initialize();
}
The Account binding in transaction scripts wraps the entire component, giving direct method access through the account parameter. We'll implement this in Part 6.
Try It: Verify Bindings Work
If you completed Part 4 and built both contracts, the bindings are already working! Let's verify:
# Check that the WIT files were generated
ls contracts/bank-account/target/generated-wit/
Expected output
miden_bank-account.wit
miden_bank-account_world.wit
These files enable the deposit note to call bank_account::deposit().
Common Issues
"Cannot find module" Error
error: cannot find module `bindings`
Cause: The account component wasn't built, or the WIT path is wrong.
Solution:
- Build the account:
cd contracts/bank-account && miden build - Verify the WIT path in
Cargo.tomlpoints totarget/generated-wit/
"Method not found" Error
error: no method named `deposit` found
Cause: The method isn't marked pub in the account component.
Solution: Ensure the method has pub fn visibility.
"Dependency not found" Error
error: dependency 'miden:bank-account' not found
Cause: One of the dependency sections is missing or has the wrong path.
Solution: Ensure both [package.metadata.miden.dependencies] and [package.metadata.component.target.dependencies] are present with correct paths.
Key Takeaways
- Build accounts first - They generate WIT files that note scripts need
- Two dependency sections - Both
miden.dependenciesandcomponent.target.dependenciesare required - Import path pattern -
crate::bindings::{package}::{component}::{interface} - Only public methods - Private methods aren't exposed in bindings
- Transaction scripts differ - They receive the account as a parameter (Part 6)
See the complete Cargo.toml configurations:
Next Steps
Now that you understand cross-component calls, let's create the transaction script that initializes the bank in Part 6: Transaction Scripts.