create-miden-app 1.0.4 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/cli.js +6 -6
  2. package/package.json +1 -1
  3. package/template/.claude/commands/review-security.md +67 -0
  4. package/template/.claude/hooks/check-artifacts.sh +45 -0
  5. package/template/.claude/hooks/run-affected-tests.sh +31 -0
  6. package/template/.claude/hooks/typecheck.sh +27 -0
  7. package/template/.claude/settings.json +29 -0
  8. package/template/.claude/settings.local.json +24 -0
  9. package/template/.claude/skills/frontend-pitfalls/SKILL.md +186 -0
  10. package/template/.claude/skills/frontend-source-guide/SKILL.md +163 -0
  11. package/template/.claude/skills/miden-concepts/SKILL.md +110 -0
  12. package/template/.claude/skills/react-sdk-patterns/SKILL.md +562 -0
  13. package/template/.claude/skills/signer-integration/SKILL.md +177 -0
  14. package/template/.claude/skills/testing-patterns/SKILL.md +338 -0
  15. package/template/.claude/skills/vite-wasm-setup/SKILL.md +134 -0
  16. package/template/.claude/skills/web-client-usage/SKILL.md +454 -0
  17. package/template/.env.example +18 -0
  18. package/template/.mcp.json +9 -0
  19. package/template/CLAUDE.md +243 -0
  20. package/template/README.md +119 -14
  21. package/template/index.html +1 -1
  22. package/template/package.json +18 -8
  23. package/template/public/packages/counter_account.masp +0 -0
  24. package/template/public/packages/increment_note.masp +0 -0
  25. package/template/src/App.tsx +6 -59
  26. package/template/src/__tests__/fixtures/accounts.ts +68 -0
  27. package/template/src/__tests__/fixtures/index.ts +22 -0
  28. package/template/src/__tests__/fixtures/notes.ts +33 -0
  29. package/template/src/__tests__/mocks/miden-sdk-react.ts +261 -0
  30. package/template/src/__tests__/patterns/README.md +44 -0
  31. package/template/src/__tests__/patterns/mutation-hook.test.tsx +146 -0
  32. package/template/src/__tests__/patterns/provider-setup.test.tsx +77 -0
  33. package/template/src/__tests__/patterns/query-hook.test.tsx +143 -0
  34. package/template/src/{App.css → components/AppContent.css} +9 -9
  35. package/template/src/components/AppContent.tsx +80 -0
  36. package/template/src/components/ConfiguredCounter.tsx +48 -0
  37. package/template/src/components/Counter.css +27 -0
  38. package/template/src/components/Counter.tsx +16 -0
  39. package/template/src/components/__tests__/AppContent.test.tsx +274 -0
  40. package/template/src/components/__tests__/ConfiguredCounter.test.tsx +116 -0
  41. package/template/src/components/__tests__/Counter.test.tsx +44 -0
  42. package/template/src/config.ts +41 -0
  43. package/template/src/hooks/__tests__/useIncrementCounter.test.tsx +257 -0
  44. package/template/src/hooks/useIncrementCounter.ts +195 -0
  45. package/template/src/index.css +7 -0
  46. package/template/src/lib/miden.ts +9 -0
  47. package/template/src/main.tsx +6 -6
  48. package/template/src/providers.tsx +27 -0
  49. package/template/src/vite-env.d.ts +1 -0
  50. package/template/tsconfig.app.json +8 -4
  51. package/template/tsconfig.node.json +1 -3
  52. package/template/vite.config.ts +5 -17
  53. package/template/vitest.config.ts +25 -0
  54. package/template/vitest.setup.ts +1 -0
  55. package/template/yarn.lock +1687 -815
  56. package/template/src/miden/lib/demo.ts +0 -106
@@ -0,0 +1,110 @@
1
+ ---
2
+ name: miden-concepts
3
+ description: Miden architecture and core concepts from a developer perspective. Covers the actor model, accounts, notes, transactions, assets, privacy model, and standard patterns. Use when designing Miden applications or understanding how Miden differs from traditional blockchains.
4
+ ---
5
+
6
+ # Miden Architecture for Developers
7
+
8
+ ## What is Miden?
9
+
10
+ Miden is a zero-knowledge rollup that uses an **actor model** where each account is an independent smart contract. It settles on Ethereum via validity proofs through Agglayer.
11
+
12
+ Key properties:
13
+ - **Privacy by default** — accounts, notes, and transactions are private; the network stores only cryptographic commitments
14
+ - **Client-side execution** — transactions are executed and proven locally by the user's device
15
+ - **Programmable everything** — accounts hold code and storage; notes carry scripts and assets
16
+
17
+ ## Mental Model Shifts from Traditional Blockchains
18
+
19
+ | Traditional (Ethereum) | Miden |
20
+ |------------------------|-------|
21
+ | Transactions involve sender + receiver | Transactions involve **one account only** |
22
+ | Public state by default | **Private by default** |
23
+ | Validators execute transactions | **Client executes and proves** locally |
24
+ | Gas metering | No gas (computational bounds exist) |
25
+ | Synchronous contract calls | **Asynchronous** communication via notes |
26
+ | Accounts are balances + storage | Accounts are **full smart contracts** with code, storage, and vault |
27
+
28
+ ## Core Concepts
29
+
30
+ ### Accounts
31
+ Each account is an independent smart contract containing:
32
+ - **Code** — Immutable logic compiled from Rust components
33
+ - **Storage** — Up to 255 slots (Value or StorageMap)
34
+ - **Vault** — Holds fungible and non-fungible assets
35
+ - **Nonce** — Incremented with each state change
36
+ - **ID** — Unique identifier (prefix + suffix, 2 Felts)
37
+
38
+ Accounts are composed from **components** — reusable Rust modules annotated with `#[component]`.
39
+
40
+ ### Notes
41
+ Notes are **UTXO-like messages** for asynchronous inter-account communication. A note contains:
42
+ - **Script** — Logic that executes when the note is consumed
43
+ - **Storage** — Data accessible to the script during execution (`NoteStorage`, Vec<Felt>)
44
+ - **Assets** — Fungible/non-fungible tokens attached to the note
45
+ - **Metadata** — Sender, tag, note type (public/private)
46
+
47
+ Notes are created as **output notes** by one transaction and consumed as **input notes** by another.
48
+
49
+ ### Transactions
50
+ A transaction is a **single-account state transition** with 4 phases:
51
+ 1. Consume input notes (execute their scripts against the account)
52
+ 2. Execute transaction script (optional, for one-off logic)
53
+ 3. Update account state (storage, vault, nonce)
54
+ 4. Produce output notes (for other accounts to consume later)
55
+
56
+ **Important**: A two-party transfer (Alice sends Bob tokens) requires TWO transactions:
57
+ 1. Alice's transaction creates a P2ID note with tokens attached
58
+ 2. Bob's transaction consumes that note, receiving the tokens
59
+
60
+ ### Assets
61
+ - **Fungible**: `[amount, 0, faucet_suffix, faucet_prefix]` (1 Word)
62
+ - **Non-fungible**: Unique token tied to a faucet account
63
+ - Assets live in account **vaults** and move between accounts via notes
64
+ - Created by **faucet accounts** using `faucet::create_fungible_asset()` or `faucet::mint()`
65
+
66
+ ### Felt and Word
67
+ - **Felt**: Field element in the Goldilocks prime field (p = 2^64 - 2^32 + 1). The fundamental data unit.
68
+ - **Word**: Array of 4 Felts (32 bytes). Used for cryptographic hashes, storage keys, account IDs.
69
+
70
+ **WARNING**: Felt arithmetic is **modular**. Subtraction wraps around the prime. Always validate with `.as_u64()` before subtracting. See the miden-pitfalls skill for details.
71
+
72
+ ## Standard Note Patterns
73
+
74
+ | Pattern | Purpose | How It Works |
75
+ |---------|---------|-------------|
76
+ | **P2ID** | Send assets to a specific account | Note script checks consumer's ID matches target |
77
+ | **P2IDE** | P2ID with expiration | Adds block-height timelock; sender can reclaim after expiry |
78
+ | **SWAP** | Atomic asset exchange | Note offers asset A, requests asset B; consumer provides B |
79
+
80
+ ## Standard Components (miden-standards)
81
+
82
+ | Component | Purpose |
83
+ |-----------|---------|
84
+ | `BasicWallet` | Standard wallet: `receive_asset()`, `move_asset_to_note()` |
85
+ | `BasicFungibleFaucet` | Mint/burn fungible tokens |
86
+ | `NoAuth` | No authentication (for testing) |
87
+ | `AuthSingleSig` | Production signature authentication — unified auth component covering both Falcon-512 and ECDSA-K256 key types |
88
+
89
+ **v14 note**: `AuthSingleSig` unifies what were previously per-scheme auth components (one for Falcon-512, one for ECDSA-K256) into a single component that dispatches on the key type ([miden-client#1798](https://github.com/0xMiden/miden-client/pull/1798)). In the same release the underlying Falcon-512 signature scheme adopted Poseidon2 as its hash function, and is now named `Falcon512Poseidon2`. If you see the older per-scheme component names or the old signature-scheme name in examples or docs, the source predates 0.14 and needs updating.
90
+
91
+ ## Development Model
92
+
93
+ ```
94
+ Developer writes Rust → Compiler produces MASM → VM executes and proves
95
+ ```
96
+
97
+ Three contract types:
98
+ - `#[component]` — Account logic and storage (can have multiple per account)
99
+ - `#[note]` — Note script (executes when consumed)
100
+ - `#[tx_script]` — One-off transaction logic
101
+
102
+ Contracts are tested locally with **MockChain** (no network needed) and deployed via **miden-client**.
103
+
104
+ ## Key Design Decisions for App Architects
105
+
106
+ 1. **One account per service** — Each bank, vault, or DEX pool is a separate account
107
+ 2. **Notes for communication** — Use deposit/withdraw/request notes instead of direct calls
108
+ 3. **Storage for state** — Use `Value` for flags, `StorageMap` for mappings
109
+ 4. **Privacy by default** — Choose `NoteType::Public` only when discoverability is needed
110
+ 5. **Components for reuse** — Standard wallet, auth, and faucet components compose into accounts
@@ -0,0 +1,562 @@
1
+ ---
2
+ name: react-sdk-patterns
3
+ description: Complete guide to building Miden frontends with @miden-sdk/react hooks. Covers MidenProvider setup, all query hooks (useAccounts, useAccount, useNotes, useSyncState, useAssetMetadata), all mutation hooks (useCreateWallet, useSend, useMultiSend, useMint, useConsume, useSwap, useTransaction, useCreateFaucet), transaction stages, signer integration, and utility functions. Use when writing, editing, or reviewing Miden React frontend code.
4
+ ---
5
+
6
+ # Miden React SDK Patterns
7
+
8
+ ## SDK Choice
9
+
10
+ ALWAYS use `@miden-sdk/react` hooks. Only fall back to the raw `WasmWebClient` (exported as `WebClient`) via `useMidenClient()` for operations not covered by hooks. The React SDK handles WASM safety (runExclusive), state management (Zustand), auto-sync, and transaction stage tracking automatically.
11
+
12
+ ## MidenProvider Configuration
13
+
14
+ ```tsx
15
+ import { MidenProvider } from "@miden-sdk/react";
16
+
17
+ <MidenProvider
18
+ config={{
19
+ rpcUrl: "testnet", // "devnet" | "testnet" | "localhost" | custom URL
20
+ prover: "testnet", // "local" | "devnet" | "testnet" | custom URL
21
+ autoSyncInterval: 15000, // ms, set to 0 to disable. Default: 15000
22
+ noteTransportUrl: "...", // optional: for private note delivery
23
+ }}
24
+ loadingComponent={<Loading />} // shown during WASM init
25
+ errorComponent={<Error />} // shown on init failure (receives Error prop)
26
+ >
27
+ <App />
28
+ </MidenProvider>
29
+ ```
30
+
31
+ | Network | rpcUrl | Use When |
32
+ |---------|--------|----------|
33
+ | Testnet | `"testnet"` | Recommended for new projects - primary development network |
34
+ | Devnet | `"devnet"` | Early-access testing (may lag feature parity with testnet) |
35
+ | Localhost | `"localhost"` | Local node at `http://localhost:57291` |
36
+
37
+ ## Query Hooks
38
+
39
+ Each returns its own result shape plus `isLoading`, `error`, `refetch`.
40
+
41
+ ### useAccounts()
42
+ ```tsx
43
+ const { accounts, wallets, faucets, isLoading, error, refetch } = useAccounts();
44
+ // wallets - AccountHeader[] (regular wallet accounts)
45
+ // faucets - AccountHeader[] (token faucet accounts)
46
+ // accounts - AccountHeader[] (everything)
47
+ ```
48
+
49
+ ### useAccount(accountId: string)
50
+ ```tsx
51
+ const { account, assets, getBalance, isLoading, error, refetch } = useAccount(accountId);
52
+ // account - Account object (.id, .nonce, .bech32id())
53
+ // assets - AssetBalance[] (assetId, amount, symbol?, decimals?)
54
+ // getBalance(faucetId) - bigint balance for specific token
55
+ ```
56
+
57
+ ### useNotes(filter?)
58
+ ```tsx
59
+ const { notes, consumableNotes, noteSummaries, consumableNoteSummaries, isLoading, error, refetch } = useNotes();
60
+ // notes - InputNoteRecord[]
61
+ // consumableNotes - ConsumableNoteRecord[]
62
+ // noteSummaries - NoteSummary[] (id, assets, sender)
63
+ // consumableNoteSummaries - NoteSummary[]
64
+
65
+ // Filter by account:
66
+ const { notes } = useNotes({ accountId: "0x..." });
67
+ // Filter by status:
68
+ const { notes } = useNotes({ status: "committed" });
69
+ // Filter by sender:
70
+ const { notes } = useNotes({ sender: "0x..." });
71
+ // Exclude specific notes:
72
+ const { notes } = useNotes({ excludeIds: ["0xnote1", "0xnote2"] });
73
+ ```
74
+
75
+ ### useNoteStream(options?)
76
+ ```tsx
77
+ const { notes, latest, markHandled, markAllHandled, snapshot, isLoading, error } = useNoteStream();
78
+ // notes - StreamedNote[] (matching filter criteria)
79
+ // latest - most recent StreamedNote (convenience)
80
+ // markHandled(noteId) - exclude a note from future renders
81
+ // markAllHandled() - exclude all current notes
82
+ // snapshot() - capture { ids, timestamp } for cross-phase filtering
83
+
84
+ // Options:
85
+ const { notes } = useNoteStream({ status: "committed", sender: "0x..." });
86
+ const { notes } = useNoteStream({ since: Date.now() - 60000 }); // last 60s
87
+ const { notes } = useNoteStream({ excludeIds: new Set(["0xnote1"]) });
88
+ const { notes } = useNoteStream({ amountFilter: (amount) => amount > 100n });
89
+ ```
90
+
91
+ ### useSyncState()
92
+ ```tsx
93
+ const { syncHeight, isSyncing, lastSyncTime, sync, error } = useSyncState();
94
+ await sync(); // Manual sync
95
+ ```
96
+
97
+ ### useAssetMetadata(faucetId: string | string[])
98
+ ```tsx
99
+ const { assetMetadata } = useAssetMetadata(faucetId);
100
+ // assetMetadata - Map<string, AssetMetadata>
101
+ // Each entry: { assetId, symbol?, decimals? }
102
+ const meta = assetMetadata.get(faucetId);
103
+ // meta.symbol - "TEST"
104
+ // meta.decimals - 8
105
+ ```
106
+
107
+ ### useTransactionHistory(options?)
108
+ ```tsx
109
+ const { records, record, status, isLoading, error, refetch } = useTransactionHistory({ id: txId });
110
+ // status: "pending" | "committed" | "discarded" | null
111
+ ```
112
+
113
+ ## Mutation Hooks
114
+
115
+ Each returns its own action function plus `isLoading`, `stage`, `error`, `reset`.
116
+
117
+ **Transaction stages**: `"idle"` → `"executing"` → `"proving"` → `"submitting"` → `"complete"`
118
+
119
+ ### useCreateWallet()
120
+ ```tsx
121
+ const { createWallet, wallet, isCreating, error, reset } = useCreateWallet();
122
+ const account = await createWallet({
123
+ storageMode: "private", // "private" | "public" | "network". Default: "private"
124
+ mutable: true, // Default: true
125
+ authScheme: 0, // 0 = RpoFalcon512, 1 = EcdsaK256Keccak. Default: 0
126
+ });
127
+ ```
128
+
129
+ ### useCreateFaucet()
130
+ ```tsx
131
+ const { createFaucet, faucet, isCreating, error, reset } = useCreateFaucet();
132
+ const account = await createFaucet({
133
+ tokenSymbol: "TEST",
134
+ decimals: 8, // Default: 8
135
+ maxSupply: 1000000n, // bigint!
136
+ storageMode: "public", // Default: "private"
137
+ });
138
+ ```
139
+
140
+ ### useImportAccount()
141
+ ```tsx
142
+ const { importAccount, account, isImporting, error, reset } = useImportAccount();
143
+
144
+ // Import by account ID (network lookup):
145
+ const account = await importAccount({ type: "id", accountId: "0x..." });
146
+
147
+ // Import from file:
148
+ const account = await importAccount({ type: "file", file: accountFileOrBytes });
149
+
150
+ // Import from seed:
151
+ const account = await importAccount({ type: "seed", seed: seedBytes, mutable: true });
152
+ ```
153
+
154
+ ### useSend()
155
+ ```tsx
156
+ const { send, result, isLoading, stage, error, reset } = useSend();
157
+ await send({
158
+ from: senderAccountId,
159
+ to: recipientAccountId,
160
+ assetId: faucetId, // token faucet ID
161
+ amount: 1000n, // bigint!
162
+ noteType: "private", // "private" | "public". Default: "private"
163
+ recallHeight: 100, // optional: sender can reclaim after this block
164
+ timelockHeight: 50, // optional: recipient can consume after this block
165
+ sendAll: true, // optional: send entire balance (ignores amount)
166
+ attachment: [1n, 2n], // optional: arbitrary data attached to the note
167
+ });
168
+ ```
169
+
170
+ ### useMultiSend()
171
+ ```tsx
172
+ const { sendMany, result, isLoading, stage, error, reset } = useMultiSend();
173
+ await sendMany({
174
+ from: senderAccountId,
175
+ assetId: faucetId,
176
+ recipients: [
177
+ { to: recipient1, amount: 500n },
178
+ { to: recipient2, amount: 300n, noteType: "public" }, // per-recipient override
179
+ { to: recipient3, amount: 200n, attachment: [1n, 2n, 3n] }, // per-recipient attachment
180
+ ],
181
+ noteType: "private", // default for all recipients
182
+ });
183
+ ```
184
+
185
+ ### useMint()
186
+ ```tsx
187
+ const { mint, result, isLoading, stage, error, reset } = useMint();
188
+ await mint({
189
+ targetAccountId: recipientId,
190
+ faucetId: myFaucetId,
191
+ amount: 10000n, // bigint!
192
+ noteType: "public",
193
+ });
194
+ ```
195
+
196
+ ### useConsume()
197
+ ```tsx
198
+ const { consume, result, isLoading, stage, error, reset } = useConsume();
199
+ await consume({
200
+ accountId: myAccountId,
201
+ notes: [noteId1, noteId2], // accepts: hex string IDs, NoteId, InputNoteRecord, or Note
202
+ });
203
+ ```
204
+
205
+ ### useSwap()
206
+ ```tsx
207
+ const { swap, result, isLoading, stage, error, reset } = useSwap();
208
+ await swap({
209
+ accountId: myAccountId,
210
+ offeredFaucetId: tokenA,
211
+ offeredAmount: 100n,
212
+ requestedFaucetId: tokenB,
213
+ requestedAmount: 50n,
214
+ noteType: "private",
215
+ paybackNoteType: "private",
216
+ });
217
+ ```
218
+
219
+ ### useTransaction() - Escape Hatch
220
+ ```tsx
221
+ const { execute, result, isLoading, stage, error, reset } = useTransaction();
222
+
223
+ // With pre-built TransactionRequest:
224
+ await execute({ accountId, request: txRequest });
225
+
226
+ // With factory function (gets access to client):
227
+ await execute({
228
+ accountId,
229
+ request: (client) => client.newSwapTransactionRequest(/* ... */),
230
+ });
231
+ ```
232
+
233
+ ### useWaitForCommit()
234
+ ```tsx
235
+ const { waitForCommit } = useWaitForCommit();
236
+ await waitForCommit(result.txId, { // useSend returns { txId, note }; other hooks use { transactionId }
237
+ timeoutMs: 10000, // Default: 10000
238
+ intervalMs: 1000, // Default: 1000
239
+ });
240
+ ```
241
+
242
+ ### useWaitForNotes()
243
+ ```tsx
244
+ const { waitForConsumableNotes } = useWaitForNotes();
245
+ await waitForConsumableNotes({
246
+ accountId: myAccountId,
247
+ minCount: 1, // Default: 1
248
+ timeoutMs: 10000,
249
+ });
250
+ ```
251
+
252
+ ### useSessionAccount(options)
253
+ ```tsx
254
+ const { initialize, sessionAccountId, isReady, step, error, reset } = useSessionAccount({
255
+ fund: async (sessionId) => {
256
+ // Called after session wallet is created - fund it here
257
+ await send({ from: mainWallet, to: sessionId, assetId: faucetId, amount: 100n });
258
+ },
259
+ assetId: faucetId, // optional: for note filtering
260
+ walletOptions: { // optional: session wallet creation options
261
+ storageMode: "private",
262
+ mutable: true,
263
+ authScheme: 0,
264
+ },
265
+ pollIntervalMs: 3000, // optional: funding detection interval. Default: 3000
266
+ });
267
+ // Steps: "idle" → "creating" → "funding" → "consuming" → "ready"
268
+ // Call initialize() to start the flow. isReady becomes true when fully funded.
269
+ ```
270
+
271
+ ## Transaction Progress UI
272
+
273
+ ```tsx
274
+ function SendButton({ from, to, assetId, amount }) {
275
+ const { send, stage, isLoading, error } = useSend();
276
+
277
+ return (
278
+ <div>
279
+ <button onClick={() => send({ from, to, assetId, amount })} disabled={isLoading}>
280
+ {isLoading ? `${stage}...` : "Send"}
281
+ </button>
282
+ {error && <p>Error: {error.message}</p>}
283
+ </div>
284
+ );
285
+ }
286
+ ```
287
+
288
+ ## Signer Integration
289
+
290
+ ### Local Keystore (Default)
291
+ No signer provider needed. Keys are managed in the browser via IndexedDB.
292
+
293
+ ### External Signers
294
+ Wrap MidenProvider with a signer provider. Three pre-built options:
295
+ - `ParaSignerProvider` from `@miden-sdk/para` - EVM wallets
296
+ - `TurnkeySignerProvider` from `@miden-sdk/miden-turnkey-react` - passkey auth
297
+ - `MidenFiSignerProvider` from `@miden-sdk/miden-wallet-adapter-react` - MidenFi wallet
298
+
299
+ ```tsx
300
+ // Example: Para signer wrapping MidenProvider
301
+ import { ParaSignerProvider } from "@miden-sdk/para";
302
+ <ParaSignerProvider apiKey="..." environment="PRODUCTION">
303
+ <MidenProvider config={...}><App /></MidenProvider>
304
+ </ParaSignerProvider>
305
+ ```
306
+
307
+ ### useSigner() - Unified Interface
308
+ ```tsx
309
+ const { isConnected, connect, disconnect, name } = useSigner();
310
+ ```
311
+
312
+ ### Custom Signer
313
+ Implement `SignerContextValue` interface via `SignerContext.Provider`. Requires: `name`, `storeName` (unique per user for DB isolation), `accountConfig`, `signCb`, `connect`, `disconnect`. See `frontend-source-guide` skill for source references.
314
+
315
+ ## Utility Functions
316
+
317
+ ```tsx
318
+ import { formatAssetAmount, parseAssetAmount, getNoteSummary, formatNoteSummary, toBech32AccountId } from "@miden-sdk/react";
319
+
320
+ formatAssetAmount(1000000n, 8) // "0.01"
321
+ parseAssetAmount("0.01", 8) // 1000000n
322
+ const summary = getNoteSummary(note); // { id, assets, sender }
323
+ formatNoteSummary(summary); // "1.5 TEST"
324
+ toBech32AccountId("0x1234..."); // "miden1qy35..."
325
+ ```
326
+
327
+ ## Direct Client Access
328
+
329
+ ```tsx
330
+ const client = useMidenClient(); // throws if not ready
331
+ const { runExclusive } = useMiden();
332
+
333
+ // For operations not covered by hooks:
334
+ await runExclusive(async () => {
335
+ const header = await client.getBlockHeaderByNumber(100);
336
+ });
337
+ ```
338
+
339
+ ## Type Imports
340
+
341
+ ```tsx
342
+ import type {
343
+ MidenConfig, QueryResult, MutationResult, TransactionStage,
344
+ AccountsResult, AccountResult, AssetBalance, NotesResult, NoteSummary,
345
+ SendOptions, MultiSendOptions, MintOptions, ConsumeOptions, SwapOptions,
346
+ CreateWalletOptions, CreateFaucetOptions, ExecuteTransactionOptions,
347
+ TransactionResult, SyncState, WaitForCommitOptions, WaitForNotesOptions,
348
+ Account, AccountId, InputNoteRecord, ConsumableNoteRecord,
349
+ TransactionRecord, TransactionRequest, NoteType, AccountStorageMode,
350
+ SignerContextValue, SignCallback, SignerAccountConfig,
351
+ } from "@miden-sdk/react";
352
+ ```
353
+
354
+ ## Reading Account Storage
355
+
356
+ For the high-level vault summary on a tracked account, the existing query hook is enough:
357
+
358
+ ```tsx
359
+ const { account, assets, getBalance } = useAccount(accountId);
360
+ // account.id, account.nonce, account.bech32id()
361
+ // assets: AssetBalance[]; getBalance(faucetId): bigint
362
+ ```
363
+
364
+ For lower-level reads (custom contract storage slots, map items), use `Account.storage()`. The React SDK serializes WASM access via `runExclusive`:
365
+
366
+ ```tsx
367
+ const client = useMidenClient();
368
+ const { runExclusive } = useMiden();
369
+
370
+ await runExclusive(async () => {
371
+ const id = AccountId.fromBech32(addressBech32);
372
+ if (!(await client.getAccount(id))) {
373
+ await client.importAccountById(id);
374
+ }
375
+ await client.syncState();
376
+ const account = await client.getAccount(id);
377
+ if (!account) return;
378
+ // AccountStorage (miden_client_web.d.ts:639-660):
379
+ const value = account.storage().getItem("my_slot_name");
380
+ // For storage maps:
381
+ const mapValue = account.storage().getMapItem("my_map_slot", keyWord);
382
+ });
383
+ ```
384
+
385
+ `Account.storage()` returns an `AccountStorage`. Both `getItem(slot_name: string)` and `getMapItem(slot_name: string, key: Word)` return `Word | undefined`. Use slot-name strings (e.g. `COUNTER_SLOT_NAME` in `src/config.ts`), not numeric indices. See `src/hooks/useIncrementCounter.ts:73-83` for the live in-template `getMapItem` example.
386
+
387
+ `useMidenClient()` returns the raw `WasmWebClient`. Its direct methods include `getAccount(accountId)`, `getAccountStorage(accountId)`, `importAccountById(accountId)`, `syncState()`, and the transaction-request factories (`newSendTransactionRequest`, `newConsumeTransactionRequest`, `newMintTransactionRequest`, `newSwapTransactionRequest`). For compile-from-source, call `client.createCodeBuilder()` and use the returned `CodeBuilder`'s `compileNoteScript(program: string)` / `compileTxScript(tx_script: string)` (`miden_client_web.d.ts`:1058-1115, factory at :4237). The higher-level `MidenClient.accounts.getOrImport` resource API lives on the standalone `MidenClient` (see `web-client-usage`).
388
+
389
+ ## Account Import then Sync then Read Storage Flow
390
+
391
+ Hook-based pattern for the common "import a remote account, sync to current chain head, then read its state" workflow:
392
+
393
+ ```tsx
394
+ function ImportAndInspect({ accountIdHex }: { accountIdHex: string }) {
395
+ const { importAccount, isImporting } = useImportAccount();
396
+ const { sync, isSyncing, syncHeight } = useSyncState();
397
+ const { account, assets, getBalance, refetch } = useAccount(accountIdHex);
398
+
399
+ async function load() {
400
+ await importAccount({ type: "id", accountId: accountIdHex });
401
+ await sync();
402
+ await refetch();
403
+ }
404
+ // Render account.id, syncHeight, balances...
405
+ }
406
+ ```
407
+
408
+ `useImportAccount` accepts `{ type: "id" | "file" | "seed", ... }`. After `sync()` resolves, the `useAccount(accountIdHex)` view reflects the latest chain state. For the raw `WasmWebClient` methods used by these hooks under the hood (`client.getAccount`, `client.importAccountById`, `client.syncState`), see `src/hooks/useIncrementCounter.ts` for a worked example. For the higher-level `MidenClient.accounts.*` resource API on a standalone `MidenClient` (outside React), see the `web-client-usage` skill.
409
+
410
+ ## Custom Notes and .masp Package Loading
411
+
412
+ `.masp` package files (compiled MASM artifacts) are produced by `cargo miden build` in the `project-template/` workspace and copied into `frontend-template/public/packages/` during the contract handoff (see `CLAUDE.md` "Contract Artifact Handoff" section).
413
+
414
+ The example below builds a custom transaction that emits two output notes carrying fungible assets. Each note has multi-felt input storage that the note script (compiled from MASM into `.masp`) reads and asserts on at consume time. The transaction is signed and submitted by the connected wallet via `useMidenFiWallet().requestTransaction(...)`.
415
+
416
+ ```tsx
417
+ import { useMidenFiWallet } from "@miden-sdk/miden-wallet-adapter-react";
418
+ import { Transaction } from "@miden-sdk/miden-wallet-adapter-base";
419
+ import {
420
+ Package,
421
+ NoteScript,
422
+ Note,
423
+ NoteAssets,
424
+ NoteMetadata,
425
+ NoteRecipient,
426
+ NoteStorage,
427
+ NoteTag,
428
+ NoteType,
429
+ NoteAttachment,
430
+ NoteExecutionHint,
431
+ NoteArray,
432
+ AccountId,
433
+ Felt,
434
+ FeltArray,
435
+ FungibleAsset,
436
+ TransactionRequestBuilder,
437
+ } from "@miden-sdk/miden-sdk";
438
+ import { randomWord } from "@/lib/miden";
439
+
440
+ const { requestTransaction } = useMidenFiWallet();
441
+
442
+ async function submitMultiNoteTx(
443
+ senderBech32: string,
444
+ targetBech32: string,
445
+ faucetBech32: string,
446
+ ) {
447
+ // (a) .masp loading: fetch the pre-built artifact and decode the note script.
448
+ const buf = await fetch("/packages/my_note.masp").then((r) => r.arrayBuffer());
449
+ const pkg = Package.deserialize(new Uint8Array(buf));
450
+ const noteScript = NoteScript.fromPackage(pkg);
451
+
452
+ const sender = AccountId.fromBech32(senderBech32);
453
+ const target = AccountId.fromBech32(targetBech32);
454
+ const faucet = AccountId.fromBech32(faucetBech32);
455
+
456
+ // (b) Multi-input note storage: each output note carries multiple Felt
457
+ // inputs that the MASM script reads from its NoteStorage. Assertions on
458
+ // these felts (e.g. "the first felt must equal the expected nonce") live
459
+ // in the .masp script source under project-template/contracts/.
460
+ function makeRecipient(seedFelts: bigint[]): NoteRecipient {
461
+ const inputs = new FeltArray();
462
+ for (const v of seedFelts) inputs.push(new Felt(v));
463
+ return new NoteRecipient(randomWord(), noteScript, new NoteStorage(inputs));
464
+ }
465
+
466
+ // (c) Asset transfers: each note carries fungible assets that move to the
467
+ // recipient when the note is consumed. FungibleAsset is declared at
468
+ // miden_client_web.d.ts:1474 (constructor `(faucet_id, amount: bigint)` at :1492).
469
+ const assets1 = new NoteAssets([new FungibleAsset(faucet, 1000n)]);
470
+ const assets2 = new NoteAssets([new FungibleAsset(faucet, 500n)]);
471
+
472
+ const tag = NoteTag.withAccountTarget(target);
473
+ const attachment = NoteAttachment.newNetworkAccountTarget(
474
+ target,
475
+ NoteExecutionHint.always(),
476
+ );
477
+ const metadata = new NoteMetadata(sender, NoteType.Public, tag).withAttachment(
478
+ attachment,
479
+ );
480
+
481
+ // Two output notes with different felt inputs and asset amounts.
482
+ const note1 = new Note(assets1, metadata, makeRecipient([1n, 2n, 3n]));
483
+ const note2 = new Note(assets2, metadata, makeRecipient([4n, 5n, 6n]));
484
+
485
+ // (d) Multi-output transaction: emit both notes in one transaction.
486
+ // For transactions that consume multiple input notes simultaneously,
487
+ // TransactionRequestBuilder.withInputNotes(NoteAndArgsArray) is the
488
+ // counterpart (miden_client_web.d.ts:3963).
489
+ const txRequest = new TransactionRequestBuilder()
490
+ .withOwnOutputNotes(new NoteArray([note1, note2]))
491
+ .build();
492
+
493
+ // (f) Submission via the wallet adapter.
494
+ const tx = Transaction.createCustomTransaction(senderBech32, targetBech32, txRequest);
495
+ if (!requestTransaction) throw new Error("Wallet does not support requestTransaction");
496
+ await requestTransaction(tx);
497
+ }
498
+ ```
499
+
500
+ Notes on this pattern:
501
+
502
+ - **Assertions live in MASM, not in TypeScript.** The note script compiled into `.masp` reads its `NoteStorage` felts at consume time and aborts the transaction if its assertions fail. The frontend's job is to construct and submit; MASM enforces. See `project-template/contracts/` for where to author MASM with assertion ops.
503
+ - **Single-output reference for simpler flows.** For a simpler worked example using only one output note with empty assets and a single consume action, see `src/hooks/useIncrementCounter.ts`. That hook is intentionally simpler and does not exercise asset transfers or multi-output emission.
504
+ - **Compile from source (no `.masp`) when needed.** When the note script is not pre-bundled as `.masp`, compile it through `CodeBuilder`. **`compileNoteScript`/`compileTxScript` are methods of `CodeBuilder`, not of `WasmWebClient`.** The pattern is:
505
+
506
+ ```tsx
507
+ const client = useMidenClient();
508
+ const builder = client.createCodeBuilder();
509
+ // optionally: builder.linkStaticLibrary(myLib) or builder.linkDynamicLibrary(myLib)
510
+ const noteScript = builder.compileNoteScript(noteSourceMasm);
511
+ const txScript = builder.compileTxScript(txSourceMasm);
512
+ ```
513
+
514
+ See `miden_client_web.d.ts`:1058-1115 for `CodeBuilder` and :4237 for `createCodeBuilder()`. As the React-idiomatic alternative, `useCompile()` (`@miden-sdk/react/dist/index.d.ts`:1171-1196) wraps `CompilerResource` from the standalone `MidenClient` and exposes `noteScript`, `txScript`, `component`, `isReady` at the hook layer.
515
+ - **Inside `useTransaction`'s `request` callback** the parameter is a `WasmWebClient` (`@miden-sdk/react/dist/index.d.ts`:393). Use `client.createCodeBuilder()` for compile, then build the `TransactionRequest` with `TransactionRequestBuilder` and return it. For the higher-level `MidenClient.compile.*` and `MidenClient.transactions.execute` resource API on a standalone `MidenClient`, see `web-client-usage`.
516
+
517
+ ## Cross-SDK Type Reference
518
+
519
+ The runtime types come from `@miden-sdk/react` (re-exported from `@miden-sdk/miden-sdk` for the underlying `MidenClient` types). The `.d.ts` files are the source of truth. Look them up in:
520
+
521
+ - the installed `.d.ts` for `@miden-sdk/react` (hook return types and option types)
522
+ - the installed `.d.ts` for `@miden-sdk/miden-sdk` (`MidenClient`, `Account`, `AccountId`, `Note`, `Word`, etc.)
523
+
524
+ Path layout differs across package managers (npm flat, pnpm nested, Yarn PnP virtual), so resolve them via your IDE's "Go to Definition" or the installed package surface rather than hard-coded paths.
525
+
526
+ Common app-developer types:
527
+
528
+ | Type | Source package | Notes |
529
+ |------|----------------|-------|
530
+ | `Account`, `AccountHeader` | `@miden-sdk/react` | `id`, `nonce`, `bech32id()` |
531
+ | `AccountId` | `@miden-sdk/miden-sdk` | construct via `AccountId.fromHex(hex)`; throws on invalid hex |
532
+ | `Address` | `@miden-sdk/miden-sdk` | bech32 wrapper; `Address.fromBech32(...)` |
533
+ | `Note`, `InputNoteRecord`, `ConsumableNoteRecord` | `@miden-sdk/react` | re-exported from `@miden-sdk/miden-sdk`. Input notes are received; for output-note types and private-note flows see `web-client-usage`. |
534
+ | `NoteVisibility` (constants + string-union) | `@miden-sdk/miden-sdk` | `const NoteVisibility = { Public: 'public', Private: 'private' }` plus `type NoteVisibility = 'public' \| 'private'` (`api-types.d.ts`:95-101). NOT an enum. Coexists with the raw WASM `NoteType` enum (`miden_client_web.d.ts`:2889) which the template uses directly when building notes via the WASM types (see `src/hooks/useIncrementCounter.ts`). |
535
+ | `AccountType`, `AuthScheme`, `StorageMode` | `@miden-sdk/miden-sdk` | enums; see `web-client-usage` "Visibility & Account Types". |
536
+ | `TransactionRequest` | `@miden-sdk/react` | client factory functions return this |
537
+ | `Word` | `@miden-sdk/miden-sdk` | 32-byte (4 felts) value; `Word.toU64s()` returns `BigUint64Array` of length 4 (each lane is a `bigint` after subscript). See `miden_client_web.d.ts`:4535. |
538
+
539
+ Do not hardcode this table for long-term reference. The `.d.ts` files stay in lockstep with the installed package version; this list will drift.
540
+
541
+ ## Rust to TypeScript Type Mapping
542
+
543
+ The Rust (`miden-client`) and TypeScript (`@miden-sdk/miden-sdk`) SDKs share concepts but diverge on naming and primitive shapes. When porting between them:
544
+
545
+ | Concept | Rust (`miden_objects` / `miden_client`) | TypeScript (`@miden-sdk/miden-sdk`) |
546
+ |---------|------------------------------------------|--------------------------------------|
547
+ | Token amount | `u64` | `bigint` |
548
+ | Field element | `Felt` (Goldilocks `u64` mod p) | `Felt` (wraps `u64`) |
549
+ | 32-byte word | `Word` (`[Felt; 4]`) | `Word`; `toU64s(): BigUint64Array` length 4 (each lane is `bigint` after subscript). |
550
+ | Account identifier | `AccountId` | `AccountId`; construct via `AccountId.fromHex` |
551
+ | Note visibility | `NoteType` enum | constants + string-union `NoteVisibility` (`'public' \| 'private'`) at the high-level `MidenClient` resource API; raw WASM `NoteType` enum (`Private = 2`, `Public = 1`) is also exported and used directly when constructing notes via the WASM types (see `src/hooks/useIncrementCounter.ts`). The two coexist; pick the layer your code lives in. |
552
+ | Account type | `AccountType` | `AccountType` enum |
553
+ | Authentication scheme | `AuthScheme` | `AuthScheme` enum |
554
+ | Storage mode | `StorageMode` | `StorageMode` enum |
555
+
556
+ Common gotchas:
557
+
558
+ - The TS method is `FeltArray.push(element: Felt)` (`miden_client_web.d.ts`:1324); there is no `FeltArray.append`. To convert a `Felt` to a JS `bigint`, use `Felt.asInt()` (`miden_client_web.d.ts`:1296). When a Rust method appears missing in TS, consult `node_modules/@miden-sdk/miden-sdk/dist/index.d.ts` and `dist/crates/miden_client_web.d.ts` first instead of guessing the TS spelling.
559
+ - TS amounts are always `bigint`. Mixing `number` causes silent precision loss above `Number.MAX_SAFE_INTEGER` and `TypeError` below.
560
+ - `Word.toU64s()` returns `BigUint64Array` of length 4 (`miden_client_web.d.ts`:4535). Each lane is a `bigint` (e.g. `word.toU64s()[0]`). Use it when reading the four `u64` lanes from a Value storage slot or building assertions on `Word` outputs.
561
+
562
+ For the canonical Rust types, see [`0xMiden/miden-client`](https://github.com/0xMiden/miden-client) and the `miden_objects` crate. For the canonical TS types, see `node_modules/@miden-sdk/miden-sdk/dist/index.d.ts`.