From e5fcb482d54e1c0c5ed2f613fc5760ceab658971 Mon Sep 17 00:00:00 2001 From: keinberger Date: Thu, 26 Mar 2026 12:45:40 +0200 Subject: [PATCH 1/3] fix(skills): update Rust SDK skills with Miden Day feedback --- .claude/skills/rust-sdk-patterns/SKILL.md | 52 +++++++------ .claude/skills/rust-sdk-pitfalls/SKILL.md | 75 ++++++++++++++++++- .../skills/rust-sdk-testing-patterns/SKILL.md | 58 +++++--------- 3 files changed, 122 insertions(+), 63 deletions(-) diff --git a/.claude/skills/rust-sdk-patterns/SKILL.md b/.claude/skills/rust-sdk-patterns/SKILL.md index faa8b10..696c22a 100644 --- a/.claude/skills/rust-sdk-patterns/SKILL.md +++ b/.claude/skills/rust-sdk-patterns/SKILL.md @@ -1,6 +1,6 @@ --- name: rust-sdk-patterns -description: Complete guide to writing Miden smart contracts with the Rust SDK. Covers #[component], #[note], #[tx_script] macros, storage patterns, native functions, asset handling, cross-component calls, and P2ID note creation. Use when writing, editing, or reviewing Miden Rust contract code. +description: Complete guide to writing Miden smart contracts with the Rust SDK. Covers #[component], #[note], #[tx_script] macros, storage patterns, native functions, asset handling, cross-component calls, P2ID note creation, and asset receiving via component methods. Use when writing, editing, or reviewing Miden Rust contract code. --- # Miden Rust SDK Patterns @@ -63,11 +63,15 @@ fn run(_arg: Word, account: &mut Account) { Fungible asset Word layout: `[amount, 0, faucet_suffix, faucet_prefix]` +**Constructor**: `Asset::new(word)` creates an Asset from a Word. + +See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) for complete asset handling patterns including deposit, withdrawal, and balance tracking. + ```rust // Access asset amount let amount = asset.inner[0]; -// Add asset to account vault +// Add asset to account vault (only from component methods, not note scripts — see pitfall P13) native_account::add_asset(asset); // Remove asset from account vault @@ -76,28 +80,22 @@ native_account::remove_asset(asset.clone()); ## P2ID Output Note Creation -To send assets to another account, create a P2ID (Pay-to-ID) output note: +To send assets to another account, create a P2ID (Pay-to-ID) output note. See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) `create_p2id_note()` for a complete working implementation. -```rust -fn create_p2id_note(&mut self, serial_num: Word, asset: &Asset, - recipient_id: AccountId, tag: Felt, note_type: Felt) { - let tag = Tag::from(tag); - let note_type = NoteType::from(note_type); - let script_root = Self::p2id_note_root(); // Hardcoded P2ID script digest - - // P2ID inputs: [suffix, prefix] of recipient - let recipient = Recipient::compute(serial_num, script_root, - vec![recipient_id.suffix, recipient_id.prefix]); - - let note_idx = output_note::create(tag, note_type, recipient); - native_account::remove_asset(asset.clone()); - output_note::add_asset(asset.clone(), note_idx); -} -``` +**Key details:** + +- `Recipient::compute(serial_num: Word, script_digest: Digest, inputs: Vec)` — the second parameter is `Digest`, not `Word`. +- `Digest` does not implement `Copy`. Use `.clone()` when reusing a digest in loops or across calls. +- P2ID inputs must be padded to 8 elements: `[suffix, prefix, 0, 0, 0, 0, 0, 0]`. +- In host/test code, use `NoteRecipient` (from miden-client) instead of `Recipient` for constructing notes. ## Note Inputs -Notes receive data via inputs (Vec), accessed with `active_note::get_inputs()`: +Notes receive data via inputs (Vec), accessed with `active_note::get_inputs()`. + +**Requires alloc**: Since `get_inputs()` returns `Vec`, you must have `extern crate alloc;` and `use alloc::vec::Vec;` in your `#![no_std]` contract. See the [no-std setup in any contract](../../../contracts/counter-account/src/lib.rs). + +**Usage:** ```rust let inputs = active_note::get_inputs(); @@ -116,10 +114,10 @@ Then import the bindings in your Rust code. See [increment-note/src/lib.rs](../. ```rust // Felt from integer -let f = felt!(42); -let f = Felt::new(42); +let f = felt!(42); // preferred for literals in contract code +let f = Felt::new(42); // returns Result in contracts, Felt in host code (see pitfall P8) let f = Felt::from_u32(42); -let f = Felt::from_u64_unchecked(42); +let f = Felt::from_u64_unchecked(42); // when value is known < field modulus // Word from Felts let w = Word::from([f0, f1, f2, f3]); @@ -140,6 +138,14 @@ extern crate alloc; use alloc::vec::Vec; ``` +## Asset Receiving via Component Methods + +Note scripts cannot call `native_account::add_asset()` directly (see pitfall P13). The canonical pattern is for an account component to expose a public method that wraps `native_account::add_asset()`, and note scripts call that method via cross-component bindings. + +See [miden-bank bank-account deposit()](../../../../miden-bank/contracts/bank-account/src/lib.rs) for the component side: the `deposit()` method validates the deposit, updates storage, and calls `native_account::add_asset()`. + +See [miden-bank deposit-note](../../../../miden-bank/contracts/deposit-note/src/lib.rs) for the note side: the note script calls `bank_account::deposit()` via generated bindings. + ## Validation Checklist - [ ] `#![no_std]` and `#![feature(alloc_error_handler)]` at top of every contract diff --git a/.claude/skills/rust-sdk-pitfalls/SKILL.md b/.claude/skills/rust-sdk-pitfalls/SKILL.md index 2a3dd4a..01ad092 100644 --- a/.claude/skills/rust-sdk-pitfalls/SKILL.md +++ b/.claude/skills/rust-sdk-pitfalls/SKILL.md @@ -1,6 +1,6 @@ --- name: rust-sdk-pitfalls -description: Critical pitfalls and safety rules for Miden Rust SDK development. Covers felt arithmetic security, comparison operators, argument limits, storage naming, no-std setup, asset layout, and P2ID roots. Use when reviewing, debugging, or writing Miden contract code. +description: Critical pitfalls and safety rules for Miden Rust SDK development. Covers felt arithmetic security, comparison operators, argument limits, storage naming, no-std setup, asset layout, P2ID roots, Felt::new() Result type, Value read annotations, NoteType construction, AccountId.prefix() types, Felt conversion limits, note-to-component call boundaries, and note input immutability. Use when reviewing, debugging, or writing Miden contract code. --- # Miden SDK Pitfalls @@ -24,6 +24,8 @@ let new_balance = current_balance - withdraw_amount; **Rule**: ALWAYS check `.as_u64()` values before any Felt subtraction. +**Max Felt value**: The maximum valid Felt is `p - 1 = 18446744069414584320`, not `u64::MAX` (`18446744073709551615`). Using `u64::MAX` as a sentinel or boundary value causes silent wraparound. + ## P2: Felt Comparison Operators Are Misleading for Quantity Logic **Severity**: High — silently produces incorrect results @@ -131,6 +133,68 @@ fn p2id_note_root() -> Digest { **Mitigation**: Use `P2idNote::script_root()` from miden-standards if available, or verify the hardcoded root matches the current version after dependency updates. +**NoteType for P2ID**: P2ID output notes created in contract code should use `NoteType::Private` (value 2, see P10). Using `NoteType::Public` triggers an opaque "missing details in advice provider" error at execution time. See [miden-bank withdraw](../../../../miden-bank/contracts/bank-account/src/lib.rs) for the working pattern. + +## P8: Felt::new() Returns Result in Contract Code + +**Severity**: Medium -- causes compilation errors + +In contract code (compiler SDK), `Felt::new(x)` returns `Result`, not `Felt` directly. In host/test code (miden-base/miden-client), `Felt::new(x)` returns `Felt` directly. + +**Preferred alternatives in contract code**: Use `felt!(x)` for literals, or `Felt::from_u64_unchecked(x)` when the value is known to be less than the field modulus. See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) for `felt!()` usage throughout. + +## P9: Value Storage Read Requires Type Annotation + +**Severity**: Medium -- causes compilation errors + +`Value::read()` is generic over `V: From`, so an explicit type annotation is mandatory. Omitting it causes a type inference error. + +See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) for the pattern: `let current: Word = self.initialized.read()`. + +## P10: NoteType Variants Unavailable in Compiler SDK + +**Severity**: Medium -- causes compilation errors + +Named enum variants (`NoteType::Private`, `NoteType::Public`, `NoteType::Encrypted`) don't exist in contract code. Construct via `NoteType::from()`: + +| NoteType | Value | +|----------|-------| +| Public | `NoteType::from(felt!(1))` | +| Private | `NoteType::from(felt!(2))` | +| Encrypted | `NoteType::from(felt!(3))` | + +See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) for `NoteType::from(note_type)` usage. + +## P11: AccountId.prefix() Returns AccountIdPrefix + +**Severity**: Low-Medium -- causes type mismatch errors + +`AccountId.prefix()` returns `AccountIdPrefix`, not `Felt`. Use `.as_felt()` or `.into()` to convert when a `Felt` is needed: + +```rust +let prefix_felt: Felt = account_id.prefix().as_felt(); +``` + +## P12: Felt Conversion Limitations in Contract Code + +**Severity**: Medium -- causes compilation errors + +In contract code (compiler SDK), only `as_u64()` exists for converting Felt to integer. `as_int()` is available in host/test code only. `as_u32()` does not exist. For construction, `Felt::from_u32()` is available. + +## P13: Note Scripts Cannot Call Native Account Functions + +**Severity**: High -- causes runtime failures + +Note scripts cannot call `native_account::add_asset()` or other `native_account::` functions directly. The kernel's `authenticate_account_origin` check rejects these calls from a note context. Instead, note scripts must call an account component method, which then calls `native_account::add_asset()` internally. + +See [miden-bank deposit-note](../../../../miden-bank/contracts/deposit-note/src/lib.rs) for the correct pattern: the note script calls `bank_account::deposit()`, which internally calls `native_account::add_asset()`. + +## P14: Note Inputs Are Immutable After Creation + +**Severity**: Low -- causes incorrect architecture + +Note inputs (`active_note::get_inputs()`) are baked at note creation time and cannot be modified after creation. Design note input layouts carefully before deployment. + ## Quick Reference | Pitfall | One-Line Rule | @@ -141,4 +205,11 @@ fn p2id_note_root() -> Digest { | P4 Storage names | `miden::component::pkg_name::field` (underscores) | | P5 No-std | `#![no_std]` + `#![feature(alloc_error_handler)]` | | P6 Asset layout | `[amount, 0, suffix, prefix]` | -| P7 P2ID root | Verify digest after dependency updates | +| P7 P2ID root | Verify digest after dependency updates; use Private NoteType | +| P8 Felt::new() | Returns `Result` in contracts — use `felt!()` or `from_u64_unchecked()` | +| P9 Value read | Explicit type annotation required: `let w: Word = val.read()` | +| P10 NoteType | No named variants in contracts — use `NoteType::from(felt!(n))` | +| P11 AccountId.prefix() | Returns `AccountIdPrefix`, not `Felt` — use `.as_felt()` | +| P12 Felt conversions | Only `as_u64()` in contracts; no `as_int()` / `as_u32()` | +| P13 Note ↛ native_account | Note scripts must call component methods, not `native_account::` | +| P14 Note inputs | Immutable after creation — design layouts upfront | diff --git a/.claude/skills/rust-sdk-testing-patterns/SKILL.md b/.claude/skills/rust-sdk-testing-patterns/SKILL.md index 7f32aff..1a6a6cb 100644 --- a/.claude/skills/rust-sdk-testing-patterns/SKILL.md +++ b/.claude/skills/rust-sdk-testing-patterns/SKILL.md @@ -1,6 +1,6 @@ --- name: rust-sdk-testing-patterns -description: Guide to testing Miden smart contracts with MockChain. Covers test setup, contract building, account/note creation, transaction execution, storage verification, faucet setup, and output note verification. Use when writing, editing, or debugging Miden integration tests. +description: 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. --- # Miden Testing Patterns (MockChain) @@ -27,7 +27,7 @@ let faucet = builder.add_existing_basic_faucet( Auth::BasicAuth, "TOKEN", // token symbol 1000, // max supply - Some(10), // decimals (None for 0) + Some(10), // total_issuance (None for 0) )?; ``` @@ -66,7 +66,7 @@ See [counter_test.rs](../../../integration/tests/counter_test.rs) lines 54-58 fo For notes with assets and inputs: ```rust use miden_client::note::NoteAssets; -use miden_standards::notes::FungibleAsset; +use miden_client::asset::FungibleAsset; let note_assets = NoteAssets::new(vec![FungibleAsset::new(faucet.id(), 50)?.into()])?; let note = create_testing_note_from_package( @@ -115,6 +115,9 @@ mock_chain.prove_next_block()?; See [counter_test.rs](../../../integration/tests/counter_test.rs) lines 82-92 for reading a StorageMap value and asserting on the result. ### 11. Verify Output Notes + +**Important**: `add_output_note()` is only available on `MockChainBuilder` (before `build()`) — use it to seed the chain with existing notes. To verify output notes from a transaction, use `extend_expected_output_notes()` on `TxContextBuilder`: + ```rust use miden_client::note::{Note, NoteAssets, NoteMetadata, NoteRecipient}; @@ -129,42 +132,21 @@ let tx_context = mock_chain let executed = tx_context.execute().await?; ``` -## Multi-Step Test Pattern +## Multi-Transaction Test Pattern -For contracts requiring initialization before use: +For contracts requiring initialization before use, each step needs its own execute → `apply_delta()` → `add_pending_executed_transaction()` → `prove_next_block()` cycle. -```rust -#[tokio::test] -async fn multi_step_test() -> anyhow::Result<()> { - let mut builder = MockChain::builder(); - // ... setup ... - let mut mock_chain = builder.build()?; - - // Step 1: Initialize (via tx script) - let init_tx_context = mock_chain - .build_tx_context(account.id(), &[], &[])? - .tx_script(init_script) - .build()?; - let executed_init = init_tx_context.execute().await?; - account.apply_delta(executed_init.account_delta())?; - mock_chain.add_pending_executed_transaction(&executed_init)?; - mock_chain.prove_next_block()?; - - // Step 2: Main operation (via note consumption) - let tx_context = mock_chain - .build_tx_context(account.id(), &[note.id()], &[])? - .build()?; - let executed = tx_context.execute().await?; - account.apply_delta(executed.account_delta())?; - mock_chain.add_pending_executed_transaction(&executed)?; - mock_chain.prove_next_block()?; - - // Step 3: Verify state - // ... - - Ok(()) -} -``` +See [miden-bank withdraw_test.rs](../../../../miden-bank/integration/tests/withdraw_test.rs) for a complete multi-transaction test demonstrating: initialize bank → deposit assets → withdraw assets (3 sequential transactions with state verification between each step). + +See [miden-bank deposit_test.rs](../../../../miden-bank/integration/tests/deposit_test.rs) for asset-bearing note construction using `NoteAssets::new()` with `FungibleAsset`. + +## MockChain Block Numbering + +Genesis is block 0. Each `prove_next_block()` advances the block number by 1. In contract code, `tx::get_block_number()` returns the **reference block** — the last proven block at the time the transaction started, not the block the transaction will be included in. + +## Note Construction + +Always use `create_testing_note_from_package` (or mirror its logic with `.masp` package files) for creating notes in tests. Manually constructed notes may fail with a "private notes cannot be converted" error. See [counter_test.rs](../../../integration/tests/counter_test.rs) for the working pattern. ## Key Dependencies @@ -177,5 +159,5 @@ See [integration/Cargo.toml](../../../integration/Cargo.toml) for the current de - [ ] All contracts built before account/note creation - [ ] `apply_delta()` called after each `execute()` - [ ] `prove_next_block()` called after `add_pending_executed_transaction()` -- [ ] Notes added to builder via `add_output_note(OutputNote::Full(...))` +- [ ] Notes added to `MockChainBuilder` via `add_output_note(OutputNote::Full(...))` (before `build()`) - [ ] Faucet set up before creating assets From acbd40a3ee727123fbaebc9d8b3c73f3a26f7880 Mon Sep 17 00:00:00 2001 From: keinberger Date: Thu, 26 Mar 2026 14:46:44 +0200 Subject: [PATCH 2/3] fix(skills): add dedicated asset-bearing note example section --- .claude/skills/rust-sdk-testing-patterns/SKILL.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.claude/skills/rust-sdk-testing-patterns/SKILL.md b/.claude/skills/rust-sdk-testing-patterns/SKILL.md index 1a6a6cb..6ad28ce 100644 --- a/.claude/skills/rust-sdk-testing-patterns/SKILL.md +++ b/.claude/skills/rust-sdk-testing-patterns/SKILL.md @@ -148,6 +148,19 @@ Genesis is block 0. Each `prove_next_block()` advances the block number by 1. In Always use `create_testing_note_from_package` (or mirror its logic with `.masp` package files) for creating notes in tests. Manually constructed notes may fail with a "private notes cannot be converted" error. See [counter_test.rs](../../../integration/tests/counter_test.rs) for the working pattern. +## Asset-Bearing Note Example + +To create a note that carries fungible assets in tests: + +1. Create a `FungibleAsset` from a faucet ID and amount. +2. Wrap it in `NoteAssets::new(vec![Asset::Fungible(fungible_asset)])`. +3. Pass the `NoteAssets` into `NoteCreationConfig { assets: note_assets, ..Default::default() }`. +4. Use `create_testing_note_from_package` as usual. + +The faucet must be set up first (see Step 3) and the sender wallet must hold sufficient assets (see Step 2). + +See [miden-bank deposit_test.rs](../../../../miden-bank/integration/tests/deposit_test.rs) lines 56-70 for the complete working pattern, including `FungibleAsset::new()`, `NoteAssets::new()`, and `NoteCreationConfig` usage. + ## Key Dependencies See [integration/Cargo.toml](../../../integration/Cargo.toml) for the current dependency versions used in this project. From 47f079c3cf76958448a0feb465fb80f4dfad41bc Mon Sep 17 00:00:00 2001 From: keinberger Date: Tue, 14 Apr 2026 16:21:36 +0300 Subject: [PATCH 3/3] fix(skills): address review feedback, remove v0.14-outdated content --- .claude/skills/rust-sdk-patterns/SKILL.md | 38 +++--------- .claude/skills/rust-sdk-pitfalls/SKILL.md | 60 ++++--------------- .../skills/rust-sdk-testing-patterns/SKILL.md | 6 +- 3 files changed, 23 insertions(+), 81 deletions(-) diff --git a/.claude/skills/rust-sdk-patterns/SKILL.md b/.claude/skills/rust-sdk-patterns/SKILL.md index 696c22a..8a28503 100644 --- a/.claude/skills/rust-sdk-patterns/SKILL.md +++ b/.claude/skills/rust-sdk-patterns/SKILL.md @@ -53,7 +53,7 @@ fn run(_arg: Word, account: &mut Account) { |--------|--------------|---------| | `native_account::` | `add_asset(Asset)`, `remove_asset(Asset)`, `incr_nonce()` | Modify account vault/nonce | | `active_account::` | `get_id() -> AccountId`, `get_balance(AccountId) -> Felt` | Query current account | -| `active_note::` | `get_inputs() -> Vec`, `get_assets() -> Vec`, `get_sender() -> AccountId` | Query note being consumed | +| `active_note::` | `get_storage() -> Vec`, `get_assets() -> Vec`, `get_sender() -> AccountId` | Query note being consumed | | `output_note::` | `create(Tag, NoteType, Recipient) -> NoteIdx`, `add_asset(Asset, NoteIdx)` | Create output notes | | `faucet::` | `create_fungible_asset(Felt) -> Asset`, `mint(Asset)`, `burn(Asset)` | Asset minting | | `tx::` | `get_block_number() -> Felt`, `get_block_timestamp() -> Felt` | Transaction context | @@ -65,13 +65,13 @@ Fungible asset Word layout: `[amount, 0, faucet_suffix, faucet_prefix]` **Constructor**: `Asset::new(word)` creates an Asset from a Word. -See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) for complete asset handling patterns including deposit, withdrawal, and balance tracking. +See [miden-bank bank-account](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/contracts/bank-account/src/lib.rs) for complete asset handling patterns including deposit, withdrawal, and balance tracking. ```rust // Access asset amount let amount = asset.inner[0]; -// Add asset to account vault (only from component methods, not note scripts — see pitfall P13) +// Add asset to account vault (only from component methods, not note scripts — see pitfall P9) native_account::add_asset(asset); // Remove asset from account vault @@ -80,29 +80,7 @@ native_account::remove_asset(asset.clone()); ## P2ID Output Note Creation -To send assets to another account, create a P2ID (Pay-to-ID) output note. See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) `create_p2id_note()` for a complete working implementation. - -**Key details:** - -- `Recipient::compute(serial_num: Word, script_digest: Digest, inputs: Vec)` — the second parameter is `Digest`, not `Word`. -- `Digest` does not implement `Copy`. Use `.clone()` when reusing a digest in loops or across calls. -- P2ID inputs must be padded to 8 elements: `[suffix, prefix, 0, 0, 0, 0, 0, 0]`. -- In host/test code, use `NoteRecipient` (from miden-client) instead of `Recipient` for constructing notes. - -## Note Inputs - -Notes receive data via inputs (Vec), accessed with `active_note::get_inputs()`. - -**Requires alloc**: Since `get_inputs()` returns `Vec`, you must have `extern crate alloc;` and `use alloc::vec::Vec;` in your `#![no_std]` contract. See the [no-std setup in any contract](../../../contracts/counter-account/src/lib.rs). - -**Usage:** - -```rust -let inputs = active_note::get_inputs(); -// Parse: Asset = inputs[0..4], serial_num = inputs[4..8], tag = inputs[8], type = inputs[9] -let asset = Asset::new(Word::from([inputs[0], inputs[1], inputs[2], inputs[3]])); -let serial_num = Word::from([inputs[4], inputs[5], inputs[6], inputs[7]]); -``` +To send assets to another account, create a P2ID (Pay-to-ID) output note. See [miden-bank bank-account](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/contracts/bank-account/src/lib.rs) `create_p2id_note()` for a complete working implementation. ## Cross-Component Dependencies @@ -115,7 +93,7 @@ Then import the bindings in your Rust code. See [increment-note/src/lib.rs](../. ```rust // Felt from integer let f = felt!(42); // preferred for literals in contract code -let f = Felt::new(42); // returns Result in contracts, Felt in host code (see pitfall P8) +let f = Felt::new(42); // construct a Felt from a u64 let f = Felt::from_u32(42); let f = Felt::from_u64_unchecked(42); // when value is known < field modulus @@ -140,11 +118,11 @@ use alloc::vec::Vec; ## Asset Receiving via Component Methods -Note scripts cannot call `native_account::add_asset()` directly (see pitfall P13). The canonical pattern is for an account component to expose a public method that wraps `native_account::add_asset()`, and note scripts call that method via cross-component bindings. +Note scripts cannot call `native_account::add_asset()` directly (see pitfall P9). The canonical pattern is for an account component to expose a public method that wraps `native_account::add_asset()`, and note scripts call that method via cross-component bindings. -See [miden-bank bank-account deposit()](../../../../miden-bank/contracts/bank-account/src/lib.rs) for the component side: the `deposit()` method validates the deposit, updates storage, and calls `native_account::add_asset()`. +See [miden-bank bank-account deposit()](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/contracts/bank-account/src/lib.rs) for the component side: the `deposit()` method validates the deposit, updates storage, and calls `native_account::add_asset()`. -See [miden-bank deposit-note](../../../../miden-bank/contracts/deposit-note/src/lib.rs) for the note side: the note script calls `bank_account::deposit()` via generated bindings. +See [miden-bank deposit-note](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/contracts/deposit-note/src/lib.rs) for the note side: the note script calls `bank_account::deposit()` via generated bindings. ## Validation Checklist diff --git a/.claude/skills/rust-sdk-pitfalls/SKILL.md b/.claude/skills/rust-sdk-pitfalls/SKILL.md index 01ad092..89e73e4 100644 --- a/.claude/skills/rust-sdk-pitfalls/SKILL.md +++ b/.claude/skills/rust-sdk-pitfalls/SKILL.md @@ -1,6 +1,6 @@ --- name: rust-sdk-pitfalls -description: Critical pitfalls and safety rules for Miden Rust SDK development. Covers felt arithmetic security, comparison operators, argument limits, storage naming, no-std setup, asset layout, P2ID roots, Felt::new() Result type, Value read annotations, NoteType construction, AccountId.prefix() types, Felt conversion limits, note-to-component call boundaries, and note input immutability. Use when reviewing, debugging, or writing Miden contract code. +description: Critical pitfalls and safety rules for Miden Rust SDK development. Covers felt arithmetic security, comparison operators, argument limits, storage naming, no-std setup, asset layout, P2ID roots, NoteType construction, note-to-component call boundaries, and note input immutability. Use when reviewing, debugging, or writing Miden contract code. --- # Miden SDK Pitfalls @@ -133,25 +133,9 @@ fn p2id_note_root() -> Digest { **Mitigation**: Use `P2idNote::script_root()` from miden-standards if available, or verify the hardcoded root matches the current version after dependency updates. -**NoteType for P2ID**: P2ID output notes created in contract code should use `NoteType::Private` (value 2, see P10). Using `NoteType::Public` triggers an opaque "missing details in advice provider" error at execution time. See [miden-bank withdraw](../../../../miden-bank/contracts/bank-account/src/lib.rs) for the working pattern. +**NoteType for P2ID**: P2ID output notes created in contract code should use the private note type value via `NoteType::from(felt!(2))` (see P8). Using the public note type triggers an opaque "missing details in advice provider" error at execution time. See [miden-bank withdraw](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/contracts/bank-account/src/lib.rs) for the working pattern. -## P8: Felt::new() Returns Result in Contract Code - -**Severity**: Medium -- causes compilation errors - -In contract code (compiler SDK), `Felt::new(x)` returns `Result`, not `Felt` directly. In host/test code (miden-base/miden-client), `Felt::new(x)` returns `Felt` directly. - -**Preferred alternatives in contract code**: Use `felt!(x)` for literals, or `Felt::from_u64_unchecked(x)` when the value is known to be less than the field modulus. See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) for `felt!()` usage throughout. - -## P9: Value Storage Read Requires Type Annotation - -**Severity**: Medium -- causes compilation errors - -`Value::read()` is generic over `V: From`, so an explicit type annotation is mandatory. Omitting it causes a type inference error. - -See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) for the pattern: `let current: Word = self.initialized.read()`. - -## P10: NoteType Variants Unavailable in Compiler SDK +## P8: NoteType Variants Unavailable in Compiler SDK **Severity**: Medium -- causes compilation errors @@ -163,37 +147,21 @@ Named enum variants (`NoteType::Private`, `NoteType::Public`, `NoteType::Encrypt | Private | `NoteType::from(felt!(2))` | | Encrypted | `NoteType::from(felt!(3))` | -See [miden-bank bank-account](../../../../miden-bank/contracts/bank-account/src/lib.rs) for `NoteType::from(note_type)` usage. - -## P11: AccountId.prefix() Returns AccountIdPrefix - -**Severity**: Low-Medium -- causes type mismatch errors - -`AccountId.prefix()` returns `AccountIdPrefix`, not `Felt`. Use `.as_felt()` or `.into()` to convert when a `Felt` is needed: - -```rust -let prefix_felt: Felt = account_id.prefix().as_felt(); -``` - -## P12: Felt Conversion Limitations in Contract Code - -**Severity**: Medium -- causes compilation errors - -In contract code (compiler SDK), only `as_u64()` exists for converting Felt to integer. `as_int()` is available in host/test code only. `as_u32()` does not exist. For construction, `Felt::from_u32()` is available. +See [miden-bank bank-account](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/contracts/bank-account/src/lib.rs) for `NoteType::from(note_type)` usage. -## P13: Note Scripts Cannot Call Native Account Functions +## P9: Note Scripts Cannot Call Native Account Functions **Severity**: High -- causes runtime failures Note scripts cannot call `native_account::add_asset()` or other `native_account::` functions directly. The kernel's `authenticate_account_origin` check rejects these calls from a note context. Instead, note scripts must call an account component method, which then calls `native_account::add_asset()` internally. -See [miden-bank deposit-note](../../../../miden-bank/contracts/deposit-note/src/lib.rs) for the correct pattern: the note script calls `bank_account::deposit()`, which internally calls `native_account::add_asset()`. +See [miden-bank deposit-note](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/contracts/deposit-note/src/lib.rs) for the correct pattern: the note script calls `bank_account::deposit()`, which internally calls `native_account::add_asset()`. -## P14: Note Inputs Are Immutable After Creation +## P10: Note Inputs Are Immutable After Creation **Severity**: Low -- causes incorrect architecture -Note inputs (`active_note::get_inputs()`) are baked at note creation time and cannot be modified after creation. Design note input layouts carefully before deployment. +Note inputs (`active_note::get_storage()`) are baked at note creation time and cannot be modified after creation. Design note input layouts carefully before deployment. ## Quick Reference @@ -205,11 +173,7 @@ Note inputs (`active_note::get_inputs()`) are baked at note creation time and ca | P4 Storage names | `miden::component::pkg_name::field` (underscores) | | P5 No-std | `#![no_std]` + `#![feature(alloc_error_handler)]` | | P6 Asset layout | `[amount, 0, suffix, prefix]` | -| P7 P2ID root | Verify digest after dependency updates; use Private NoteType | -| P8 Felt::new() | Returns `Result` in contracts — use `felt!()` or `from_u64_unchecked()` | -| P9 Value read | Explicit type annotation required: `let w: Word = val.read()` | -| P10 NoteType | No named variants in contracts — use `NoteType::from(felt!(n))` | -| P11 AccountId.prefix() | Returns `AccountIdPrefix`, not `Felt` — use `.as_felt()` | -| P12 Felt conversions | Only `as_u64()` in contracts; no `as_int()` / `as_u32()` | -| P13 Note ↛ native_account | Note scripts must call component methods, not `native_account::` | -| P14 Note inputs | Immutable after creation — design layouts upfront | +| P7 P2ID root | Verify digest after dependency updates; use `NoteType::from(felt!(2))` for private | +| P8 NoteType | No named variants in contracts — use `NoteType::from(felt!(n))` | +| P9 Note ↛ native_account | Note scripts must call component methods, not `native_account::` | +| P10 Note inputs | Immutable after creation — design layouts upfront | diff --git a/.claude/skills/rust-sdk-testing-patterns/SKILL.md b/.claude/skills/rust-sdk-testing-patterns/SKILL.md index 6ad28ce..fe68271 100644 --- a/.claude/skills/rust-sdk-testing-patterns/SKILL.md +++ b/.claude/skills/rust-sdk-testing-patterns/SKILL.md @@ -136,9 +136,9 @@ let executed = tx_context.execute().await?; For contracts requiring initialization before use, each step needs its own execute → `apply_delta()` → `add_pending_executed_transaction()` → `prove_next_block()` cycle. -See [miden-bank withdraw_test.rs](../../../../miden-bank/integration/tests/withdraw_test.rs) for a complete multi-transaction test demonstrating: initialize bank → deposit assets → withdraw assets (3 sequential transactions with state verification between each step). +See [miden-bank withdraw_test.rs](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/integration/tests/withdraw_test.rs) for a complete multi-transaction test demonstrating: initialize bank → deposit assets → withdraw assets (3 sequential transactions with state verification between each step). -See [miden-bank deposit_test.rs](../../../../miden-bank/integration/tests/deposit_test.rs) for asset-bearing note construction using `NoteAssets::new()` with `FungibleAsset`. +See [miden-bank deposit_test.rs](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/integration/tests/deposit_test.rs) for asset-bearing note construction using `NoteAssets::new()` with `FungibleAsset`. ## MockChain Block Numbering @@ -159,7 +159,7 @@ To create a note that carries fungible assets in tests: The faucet must be set up first (see Step 3) and the sender wallet must hold sufficient assets (see Step 2). -See [miden-bank deposit_test.rs](../../../../miden-bank/integration/tests/deposit_test.rs) lines 56-70 for the complete working pattern, including `FungibleAsset::new()`, `NoteAssets::new()`, and `NoteCreationConfig` usage. +See [miden-bank deposit_test.rs](https://github.com/0xMiden/tutorials/blob/main/examples/miden-bank/integration/tests/deposit_test.rs) lines 56-70 for the complete working pattern, including `FungibleAsset::new()`, `NoteAssets::new()`, and `NoteCreationConfig` usage. ## Key Dependencies