In partnership with Miden, Walnut is building Miden Foundry, a set of command-line tools for interacting with, inspecting, testing, and debugging Miden contracts, right from the terminal. The first of those commands miden call is available starting with midenup v1.0.1.
miden call shows developers what a transaction will do before they send it on-chain. It runs a procedure on a Miden account on the developer's own machine and shows the result. Nothing is sent to the network, so developers can run the same call as many times as they want, for free.
In this post, we give a short intro to Miden, explain why developers need these tools, show how miden call works, and share what comes next.
Introduction to Miden and its Unique Transaction Settlement
Miden is a zero-knowledge rollup for building private applications. On Miden, users run transactions on their own devices, and their data stays there unless they choose to make it public.
Every account is a smart contract with its own code, storage, and assets. A call can always read an account’s state, but it can change the state only if the account’s contract allows it. For example, a public counter might let anyone increment it.
Assets never move directly between accounts. They always move through notes: the sender puts the assets in a note, and the receiver accepts them by consuming the note in its own transaction. A note is like a letter: it holds assets and a script that says who can open it. So sending tokens takes two transactions instead of one.
A transaction runs and is proven on the user's device, and only then is it sent to the network. Once it settles, it cannot be reverted. That's where miden call comes in. It allows developers to preview what a transaction will do, without sending it to the chain.
What is Foundry and why Miden needs it
Foundry is a set of command-line tools for building smart contracts on Ethereum, and one of the most popular toolkits among Ethereum developers.
Developers use Foundry to write and run tests, including fuzz tests that try many random inputs. They use it to read data from deployed contracts, simulate a transaction before they send it, and look into past transactions to debug them. Most of this takes a single command.
This helps developers find bugs early, before a contract is live. They can try transactions without deploying anything or paying a fee. When a transaction fails, they can see exactly where and why, instead of sending it again and guessing.
Miden developers need the same tool: as transactions touch accounts, storage, and notes, they can't be reverted once they settle, and today checking what they will do often means writing a Rust script instead of running one command.
Meet miden call
miden call executes a Miden transaction in a dry-run mode. It allows developers to preview the outcome without sending a real transaction to the chain. It works much like cast call on Ethereum.
Example of how it works
Say a developer has a counter contract deployed to an account ${COUNTER} and wants to see the current count:
miden call ${COUNTER}:get-count --package counter-account.maspSignature: get-count() -> felt
Result: 2
The transaction will have the following effects:
No notes will be consumed.
No notes will be created as a result of this transaction.
Account Storage will not be changed.
Account Vault will not be changed.
New account nonce: 2.Now call a procedure that changes storage:
miden call ${COUNTER}:increment-count --package counter-account.maspSignature: increment-count() -> felt
Result: 3
The transaction will have the following effects:
No notes will be consumed.
No notes will be created as a result of this transaction.
Account Storage will not be changed.
Storage map changes:
┌──────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────┐
│ Storage Slot ┆ Map Key ┆ New Value │
╞══════════════════════════════════════════════╪════════════════════════════════════════════════════════════════════╪════════════════════════════════════════════════════════════════════╡
│ counter_account::counter_contract::count_map ┆ 0x0000000000000000000000000000000000000000000000000100000000000000 ┆ 0x0300000000000000000000000000000000000000000000000000000000000000 │
└──────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────┘
Account Vault will not be changed.
New account nonce: 3.Nothing was sent to the network. The count on the chain is still the same. The developer only sees what would change if they sent this transaction.
The command takes four parameters: the account to be called, the procedure name, the contract package, and the arguments. Since miden call knows the procedure's types, developers can also pass arguments like an account ID or an asset in a readable form.
For example, a scoreboard contract deployed to the account ${BOARD} has an award procedure that takes a player's account ID and a score. The developer passes the player's account ID ${PLAYER} as it is, and miden call shows the result as a score-update with the previous and the current score:
miden call ${BOARD}:award ${PLAYER} 23 --package scoreboard-account.maspSignature: award(account-id, felt) -> score-update
Result: score-update { previous: 0, current: 23 }
The transaction will have the following effects:
No notes will be consumed.
No notes will be created as a result of this transaction.
Storage changes:
┌───────────────────────────────────────┬────────────────────────────────────────────────────────────────────┐
│ Storage Slot ┆ New Value │
╞═══════════════════════════════════════╪════════════════════════════════════════════════════════════════════╡
│ scoreboard_account::scoreboard::total ┆ 0x1700000000000000000000000000000000000000000000000000000000000000 │
└───────────────────────────────────────┴────────────────────────────────────────────────────────────────────┘
Storage map changes:
┌────────────────────────────────────────┬────────────────────────────────────────────────────────────────────┬────────────────────────────────────────────────────────────────────┐
│ Storage Slot ┆ Map Key ┆ New Value │
╞════════════════════════════════════════╪════════════════════════════════════════════════════════════════════╪════════════════════════════════════════════════════════════════════╡
│ scoreboard_account::scoreboard::scores ┆ 0x000000000000000000000000000000000013a458b4537752c17daa3b9d3a47ed ┆ 0x1700000000000000000000000000000000000000000000000000000000000000 │
└────────────────────────────────────────┴────────────────────────────────────────────────────────────────────┴────────────────────────────────────────────────────────────────────┘
Account Vault will not be changed.
New account nonce: 2.miden call works both for the developer's own contracts and for public contracts that someone else deployed. For someone else's contract, miden call reads its state from the network and can only read it: it shows the result, but it cannot change the contract's state. The call runs from one of the developer's own accounts, so they need at least one, which they can create with miden new-wallet.
Related commands
miden account --inspectlists the procedures an account exposes, with their names and signatures. Developers use it to find which procedures they can call.miden execruns a Miden Assembly (MASM) script against an account, also without submitting anything. Developers useexecfor MASM scripts andcallfor contract procedures.
What’s next
miden call is just the first step in building Foundry-like tooling for Miden. Next, we plan to add:
miden sendto submit transactions. It runs the same call asmiden call, proves it, and submits it to the network, likecast sendon Ethereum. Commands that work with notes will also get a--dry-runflag, so developers can preview a note transaction before sending it.- Call trace, to see inside a call. With
--trace,miden callshows the hierarchy of procedure calls, with the arguments and result of each call. Developers see exactly where a transaction goes wrong, instead of guessing. - A local package registry to stop passing file paths. It keeps contract packages in one place on the developer's machine, so developers can refer to a package by name and version, like
counter-contract@0.1.0instead of a path to a file. - Fuzz testing to find edge cases developers did not think of. Instead of writing one test for one input, developers describe the kind of inputs a contract accepts, such as note inputs or transaction arguments, and the test runs many times with random values. If one of them breaks the contract, they see which input caused it, just like fuzz tests in Foundry.
- Chain forking, to test against real contracts. Like Anvil's fork mode on Ethereum, it copies the state of the live chain to the developer's machine, so developers can try transactions against contracts that are already deployed. On Miden, this is more challenging because much of the state is private: it lives on users' devices and never reaches the chain. We will cover these challenges in a separate article.
Our goal is to give Miden developers the same fast workflow that Foundry gives Ethereum developers.
