소스 정보
- 저장소
- tomevault-io/skills-registry
- 최근 소스 활동
- 2026년 7월 3일 19:45
- 감지된 SKILL.md 언어
- 영어
- 스타
- 0
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill rust-sdk-patterns명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | rust-sdk-patterns |
| description | Complete guide to writing Miden smart contracts with the Rust SDK. Covers |
| metadata | {"author":"0xMiden"} |
#[component])Defines reusable logic and storage for accounts. Accounts are composed of one or more components.
See counter-account/src/lib.rs for a working example demonstrating #[component], typed StorageMap<Word, Felt>, get()/set(), and felt arithmetic.
Cargo.toml for accounts: See counter-account/Cargo.toml for the required crate-type, miden dependency, component metadata, and project-kind.
#[note])Executes when a note is consumed by an account. Can call component methods on the consuming account.
See increment-note/src/lib.rs for a working example demonstrating #[note], #[note_script], and cross-component calls.
Cargo.toml for notes: See increment-note/Cargo.toml for the required miden deps, cross-component dependencies, wit deps, and project-kind = "note-script".
#[tx_script])One-off logic executed in the context of an account. Used for initialization, admin operations, etc.
#![no_std]
#![feature(alloc_error_handler)]
use miden::*;
use crate::bindings::Account;
#[tx_script]
fn run(_arg: Word, account: &mut Account) {
account.initialize();
}
Cargo.toml: Same as account but with project-kind = "tx-script".
| Type | Usage | Read | Write |
|---|---|---|---|
StorageValue<T> | Single typed slot (flags, counters, IDs) | .get() -> T | .set(T) -> T |
StorageMap<K, V> | Typed key-value mapping (balances, records) | .get(K) -> V | .set(K, V) -> V |
| Module | Key Functions | Purpose |
|---|---|---|
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_assets() -> Vec<Asset>, get_sender() -> AccountId | Query note being consumed (typed note storage arrives as self in the #[note_script] method; see "Cross-Component Note Pattern" below) |
note:: | build_recipient(Word, Word, Vec<Felt>) -> Recipient | Build note recipients from serial number, script root, and note storage |
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 |
| Intrinsics | assert(bool), assertz(Felt), assert_eq(Felt, Felt) | Validation |
Asset is now a two-word value:
Constructor: Asset::new(word) creates an Asset from a Word.
See miden-bank bank-account for complete asset handling patterns including deposit, withdrawal, and balance tracking.
pub struct Asset {
pub key: Word,
pub value: Word,
}
For fungible assets, the amount lives in asset.value[0]. The asset class / vault identity lives in asset.key.
// Access fungible amount
let amount = asset.value[0];
// Keep the asset key if you need to persist or compare the asset class
let asset_key = asset.key;
// Add asset to account vault (only from component methods, not note scripts; see pitfall P11)
native_account::add_asset(asset);
// Remove asset from account vault
native_account::remove_asset(asset.clone());
To send assets to another account, create a P2ID (Pay-to-ID) output note. See miden-bank bank-account create_p2id_note() for a complete working implementation.
To call another component's methods from a note or tx script, two Cargo.toml sections are needed. See increment-note/Cargo.toml for a working example showing both [package.metadata.miden.dependencies] and [package.metadata.component.target.dependencies].
Then import the bindings in your Rust code. See increment-note/src/lib.rs line 13 for the import pattern: use crate::bindings::miden::target_component::target_component;
// Felt from integer
let f = felt!(42); // preferred for literals in contract code
let f = Felt::new(42); // construct a Felt from a u64
let f = Felt::from_u32(42);
let f = Felt::from_canonical_checked(42).unwrap();
// Word from Felts
let w = Word::from([f0, f1, f2, f3]);
let w = Word::new([f0, f1, f2, f3]);
let w = Word::from([0_u32, 0, 0, 1]);
let w = Word::try_from([0_u64, 0, 0, 1]).unwrap();
// Inspect a Word
let limbs: [Felt; 4] = w.into_elements();
let bytes: [u8; 32] = w.as_bytes();
= w.();
: = f.();
Every contract file must start with #![no_std] and #![feature(alloc_error_handler)]. See any contract in contracts/ for the pattern.
If you need heap allocation (Vec, String, etc.):
extern crate alloc;
use alloc::vec::Vec;
A note script reads from active_note::* and forwards work to a public account-component method via generated bindings. This is the canonical pattern for any note that updates account state, because note scripts cannot call native_account::* directly (see rust-sdk-pitfalls skill, P11).
The #[note] macro generates TryFrom<&[Felt]> for the note struct, so the note's serialized storage is deserialized into typed fields before the script runs. The #[note_script] method receives the deserialized note as self (by value) and never indexes a raw Felt slice manually. Alongside the required Word arg, the method may optionally accept a &Account or &mut Account parameter. See compiler/sdk/base-macros/src/lib.rs for the macro contract and compiler/sdk/base-macros/src/note.rs for the generated deserialization (each named field is read via <T as miden::felt_repr::FromFeltRepr>::from_felt_repr(...) and EOF is asserted at the end).
Supported field types include Felt, the unsigned integer scalars (u64, u32, u8), bool, Option<T>, and Vec<T> via the FromFeltRepr trait (compiler/sdk/field-repr/repr/src/lib.rs), plus any user type that opts in with #[derive(FromFeltRepr)] (this is how AccountId supports the macro - see compiler/sdk/base-sys/src/bindings/types.rs). Do not use Asset or Word directly as note struct fields; those types do not currently derive FromFeltRepr. If you need asset-shaped data inside the note, flatten it into supported scalar fields and reconstruct inside the script, or keep it on the side as a separate active_note::get_assets() read.
For Cargo.toml wiring (cross-component dependencies + bindings import), see "Cross-Component Dependencies" above. See increment-note/src/lib.rs for the project-template's local example of the #[note] struct + #[note] impl macro form.
Storage-free case (sender + assets, single component call per asset): declare a unit struct (#[note] struct DepositNote;). The script reads active_note::get_sender() and iterates active_note::get_assets(), calling the component method per asset. The macro still generates the deserialization wrapper; for a unit struct it only asserts the storage Felt slice is empty.
Typed-storage case (note carries scripted data): declare named fields on the note struct. The macro deserializes them in declaration order, and the script accesses them via self.<field>. Illustrative shape:
#[note]
struct DepositNote {
depositor: AccountId,
}
#[note]
impl DepositNote {
#[note_script]
pub fn run(self, _arg: Word) {
let assets = active_note::get_assets();
for asset in assets {
bank_account::deposit(self.depositor, asset);
}
}
}
(use statements and crate attributes elided; see increment-note/src/lib.rs for a complete file.) For a verified working example with an &mut Account parameter, see compiler/examples/p2id-note/src/lib.rs (#[note] struct P2idNote { target_account_id: AccountId }, where the script asserts account.get_id() == self.target_account_id and calls account.receive_asset(asset) for each attached asset).
Component side that absorbs the call: see miden-bank bank-account for deposit(...) and withdraw(...) in a fuller example. The component method validates (felt-arithmetic safety, see rust-sdk-pitfalls P1), updates storage, and (for withdraw) creates a P2ID output note via the existing P2ID pattern. Note: miden-bank currently demonstrates an older raw-indexing variant for its withdraw-request note; treat the typed pattern shown above as the preferred shape for new note scripts.
Test wiring: tests pass the serialized Felt representation of the note struct's fields through NoteCreationConfig.storage, in declaration order. See rust-sdk-testing-patterns skill, "Note Construction" section, for the helper that builds a note from a compiled .masp package and a populated NoteCreationConfig.
Note scripts cannot call native_account::add_asset() directly (see pitfall P11). 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() for the component side: the deposit() method validates the deposit, updates storage, and calls native_account::add_asset().
See miden-bank deposit-note for the note side: the note script calls bank_account::deposit() via generated bindings.
#![no_std] and #![feature(alloc_error_handler)] at top of every contractcrate-type = ["cdylib"] in Cargo.tomlproject-kind in [package.metadata.miden]StorageValue<T> / StorageMap<K, V> with get() / set()[package.metadata.miden.dependencies] and [package.metadata.component.target.dependencies].as_canonical_u64() (see rust-sdk-pitfalls skill)Source: 0xMiden/project-template — distributed by TomeVault.