0xmiden/compiler/rust-sdk-testing-patterns
Claude Code and agent skill by 0xmiden/compiler
- community
Checking that the SKILL.md file is still in the repository.
Guide to testing Miden smart contracts with MockChain. Covers test setup, contract building, account/note creation, transaction execution, storage verification, faucet setup, output note verification, block numbering, multi-transaction tests, and asset-bearing notes. Use when writing, editing, or debugging Miden integration tests.
Install
npx vskill@latest install 0xmiden/compiler/rust-sdk-testing-patternsSKILL.md
Miden Testing Patterns (MockChain)
These patterns target protocol/standards/testing 0.18.0 and VM/package crates in the 0.36 series. integration/Cargo.toml uses the released miden-client and miden-client-sqlite-store 0.18.0 crates from crates.io. Cargo.lock records the resolved crate versions.
MockChain and its builders live in miden-testing. This project depends on miden-testing directly; other repos may alternatively use the miden_client::testing re-export behind the client's optional testing feature.
The canonical worked examples are in the protocol repo itself: crates/miden-testing/src/kernel_tests/tx/test_note.rs for the end-to-end MockChain flow, and crates/miden-testing/src/mock_chain/{chain,chain_builder}.rs for the full builder surface.
Test File Setup
All tests are async and use MockChain for local execution without a network.
StorageValueName lives under account::component, never under account:: directly.
Step-by-Step Test Pattern
1. Initialize MockChain Builder
let mut builder = MockChain::builder();
2. Create Sender/Wallet Accounts
For a bare wallet, builder.add_existing_wallet(Auth::BasicAuth { auth_scheme: AuthSchemeId::Falcon512Poseidon2 }). For a pre-funded one, builder.add_existing_wallet_with_assets(auth, [FungibleAsset::new(faucet.id(), 100)?.into()]). Both return anyhow::Result<Account>.
Auth also offers Multisig, GuardedMultisig, MultisigSmart, IncrNonce, Noop, Conditional and NetworkAccount variants, plus the shorthands Auth::default(), Auth::basic_falcon() and Auth::basic_ecdsa().
Auth-scheme naming:miden_client::authre-exports the same protocol enum under two names —AuthScheme(the protocol name) andAuthSchemeId(an alias). Both compile; the field isAuth::BasicAuth { auth_scheme }. The enum is#[non_exhaustive]withFalcon512Poseidon2 = 2andEcdsaK256Keccak = 1, and the constantsRPO_FALCON_SCHEME_ID/ECDSA_K256_KECCAK_SCHEME_IDname them.
3. Set Up Faucets (for fungible assets)
The 4th argument is token_supply: Option<u64> (an explicit None is treated as 0).
4. Build Contracts
Build contracts out of process with the midenup / cargo-miden build command and load the resulting .masp package in the test. In this repository, build_project_in_dir(...) uses miden when MIDENUP_HOME is set, then CARGO_MIDEN, then cargo miden.
Do not add cargo-miden as a library dependency of a crate that also depends on miden-client. The build tool is intentionally outside the integration crate's Cargo graph; tests consume the compiled package through the client, standards, and miden-testing dependencies.
Package artefacts: the extension is .masp (Package::EXTENSION), magic MASP\0, package format version [6, 0, 0], MAST wire version [0, 0, 4]. There is no .masl.
5. Create Account with Storage
Storage slot naming convention (CRITICAL):
The slot name is part of the on-chain storage ABI and is derived by the compiler's #[component_storage] macro, not from the Rust struct name:
<[lib].namespace>is the three-segment Miden path declared inmiden-project.toml, e.g.
miden::counter_contract::counter_contract. Because it comes from the declared namespace, renaming the Rust struct cannot change the deployed slot name.
<field_name>is the Rust storage field's identifier (not itsdescription).
Live values from the compiler examples: miden::counter_contract::counter_contract::count_map, miden::auth_component_rpo_falcon512::auth_component::owner_public_key.
The component's storage is declared with the three-part component macro (#[component_storage] struct + #[component] trait + #[component] impl); the storage struct, not the trait, carries the #[storage] fields the slot names derive from. Externally-callable trait methods additionally need #[account_procedure]. See the rust-sdk-patterns skill for the contract side.
Seed any value slot that has no schema default, then register the account:
There is no `AccountBuilder::with_auth_component`. Auth components are installed like any other component, via .with_component(..) / .with_components(..). The builder's surface is new([u8; 32]), version, account_type, with_asset_callbacks, with_component(s), with_assets, nonce, storage_schemas, build, build_existing.
For map slots, seed entries with init_storage_data.insert_map_entry(slot_name, key, value)?.
Storage-seeding footgun:InitStorageData::insert_value(name, value)takesvalue: impl Into<WordValue>. The numericFromimpls (u8/u16/u32/u64) produce aWordValue::Atomic(string)that the slot's schema parses — not a felt-positionedWord. OnlyFrom<Felt>yields[felt, 0, 0, 0], andFrom<Word>/From<[Felt; 4]>/From<[u32; 4]>arefully-typed words. For aStorageValue<Word>slot whose contract reads index[0], seed aWord(Word::default()for zero). Theinsert_valuedoc comment claimingu64becomes[0,0,0,felt]is inaccurate; the code produces an atomic string.
Account model:-AccountTypeis the visibility enum{ Private (default), Public }, withas_u8(),is_public(),is_private().- Set visibility via.account_type(..). There is no.storage_mode(..)and noAccountStorageModeon this builder.- Faucet-ness is determined by the installed components.
MockChainBuilder also ships ready-made account helpers that remove most of the above: create_new_wallet, add_existing_note_creator, add_existing_non_fungible_faucet, add_existing_network_faucet, create_new_faucet, add_existing_mock_account (and its _with_storage / _with_assets / _with_storage_and_assets variants), add_existing_account_from_components.
6. Create Notes
Build a note with NoteBuilder, seeding a rand generator from the note-script root:
NoteBuilder::new(sender: AccountId, rng: T) takes the RNG by value; &mut StdRng works because &mut T: Rng. miden_client::crypto::RandomCoin is no longer re-exported by the client. Other builder methods: package, script, code, note_type, tag, add_assets, note_storage, serial_number, attachment, advice_map, dynamically_linked_packages, source_manager, build.
.tag(..)takes a `u32`, not aNoteTag. The idiom is.tag(NoteTag::with_account_target(account.id()).into()).
NoteScript::from_package(&Package)requires the package to have exactly one@note_scriptexport.NoteScript::root()returns aNoteScriptRootnewtype, and the 32-byte seed comes from aWord, so convert explicitly withWord::from(...root()).into().
Felt::new(u64)is fallible — it returnsResult<Felt, FeltFromIntError>.note_storagetakesimpl IntoIterator<Item = Felt>, so build each felt with the infallibleFelt::from(42_u32)for in-range literals (From<u8>/<u16>/<u32>are infallible); for anarbitraryu64, useFelt::new(n)?. UseFelt::new_unchecked(n)only after provingn < Felt::ORDER.
A note carries at mostMAX_ASSETS_PER_NOTE= 16 assets.
MockChainBuilder also has ready-made note constructors that skip NoteBuilder entirely: add_p2any_note, add_p2id_note, add_p2ide_note, add_swap_note, add_spawn_note, add_tx_fee_note, add_p2id_note_with_fee.
Standard notes built outside the chain builder use typed bon builders — P2idNote::builder().sender(..).target(..).serial_number(..).asset(..).build()?, then Note::from(p2id). There is no XNote::create(..).
7. Add to MockChain and Build
Register accounts (builder.add_account(account)?) and seed notes (builder.add_output_note(RawOutputNote::Full(note.clone()))), then let mut mock_chain = builder.build()?;.
add_output_notereturns(), not aResult— no?.
8. Execute Transaction
build_transaction(input) takes impl Into<MockTransactionInput>, which has From<AccountId> and From<Account>. Pass an Account (rather than an id) when chaining transactions against evolving in-memory state, and for private accounts.
Input notes are not positional arguments. Attach them with .authenticated_input_note(NoteId), .authenticated_input_notes(..), .unauthenticated_input_note(Note) or .unauthenticated_input_notes(..).
build() is sync and returns anyhow::Result<MockTransaction>; execute() is on MockTransaction, is async, and returns Result<ExecutedTransaction, TransactionExecutorError>.
Other MockTransactionBuilder methods: tx_script, tx_script_args, auth_args, extend_note_args, reference_block, foreign_accounts, extend_advice_inputs, add_advice_map_entry, authenticator, add_signature, add_note_script, send_notes_script, expected_output_note(s), with_source_manager.
9. Execute with Transaction Script
TransactionScript::from_package(&package)? handles a kind = "tx-script" package directly: if the package is a program it uses the entrypoint, otherwise it looks for the single procedure carrying the transaction_script attribute, which the compiler emits on tx-script exports.
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingextra/templates/project/.claude/skills/rust-sdk-testing-patterns/SKILL.md
Popularity
- Repository stars
- 116
- Repository forks
- 86
- 7-day trend score
- 0
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/0xmiden/compiler/rust-sdk-testing-patterns)